//go:build windows package builder import ( "fmt" "log" "os" "os/exec" "path/filepath" "strings" ) func (h *Handler) shouldSignBuild(req *BuildRequest) bool { if !h.policy.Sign.Enabled || strings.TrimSpace(h.policy.Sign.CertThumbprint) == "" { return false } return req.SignBuild } func (h *Handler) signExecutable(path string) error { policy := h.policy.Sign tool := strings.TrimSpace(policy.ToolPath) if tool == "" { var err error tool, err = findSignTool() if err != nil { return err } } tsURL := strings.TrimSpace(policy.TimestampURL) if tsURL == "" { tsURL = "http://timestamp.digicert.com" } args := []string{ "sign", "/fd", "SHA256", "/tr", tsURL, "/td", "SHA256", "/sha1", strings.TrimSpace(policy.CertThumbprint), path, } cmd := exec.Command(tool, args...) out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("signtool: %w (%s)", err, strings.TrimSpace(string(out))) } log.Printf("[Forge] Signed %s", filepath.Base(path)) return nil } func findSignTool() (string, error) { if p, err := exec.LookPath("signtool"); err == nil { return p, nil } if p, err := exec.LookPath("signtool.exe"); err == nil { return p, nil } roots := []string{ os.Getenv("ProgramFiles(x86)"), os.Getenv("ProgramFiles"), } for _, root := range roots { if root == "" { continue } kits := filepath.Join(root, "Windows Kits", "10", "bin") matches, _ := filepath.Glob(filepath.Join(kits, "*", "x64", "signtool.exe")) for i := len(matches) - 1; i >= 0; i-- { if _, err := os.Stat(matches[i]); err == nil { return matches[i], nil } } } return "", fmt.Errorf("signtool.exe not found — install Windows SDK or set sign_tool_path in Calibrate") }