From 8852d0a273623869f0e92c83d8ce9d28a2a016ca Mon Sep 17 00:00:00 2001 From: Snider Date: Thu, 29 Jan 2026 02:54:06 +0000 Subject: [PATCH] feat(signing): add orchestration helpers SignBinaries, NotarizeBinaries, SignChecksums for pipeline integration. Co-Authored-By: Claude Opus 4.5 --- pkg/build/signing/sign.go | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 pkg/build/signing/sign.go diff --git a/pkg/build/signing/sign.go b/pkg/build/signing/sign.go new file mode 100644 index 0000000..65e82c9 --- /dev/null +++ b/pkg/build/signing/sign.go @@ -0,0 +1,94 @@ +package signing + +import ( + "context" + "fmt" + "runtime" +) + +// Artifact represents a build output that can be signed. +// This mirrors build.Artifact to avoid import cycles. +type Artifact struct { + Path string + OS string + Arch string +} + +// SignBinaries signs macOS binaries in the artifacts list. +// Only signs darwin binaries when running on macOS with a configured identity. +func SignBinaries(ctx context.Context, cfg SignConfig, artifacts []Artifact) error { + if !cfg.Enabled { + return nil + } + + // Only sign on macOS + if runtime.GOOS != "darwin" { + return nil + } + + signer := NewMacOSSigner(cfg.MacOS) + if !signer.Available() { + return nil // Silently skip if not configured + } + + for _, artifact := range artifacts { + if artifact.OS != "darwin" { + continue + } + + fmt.Printf(" Signing %s...\n", artifact.Path) + if err := signer.Sign(ctx, artifact.Path); err != nil { + return fmt.Errorf("failed to sign %s: %w", artifact.Path, err) + } + } + + return nil +} + +// NotarizeBinaries notarizes macOS binaries if enabled. +func NotarizeBinaries(ctx context.Context, cfg SignConfig, artifacts []Artifact) error { + if !cfg.Enabled || !cfg.MacOS.Notarize { + return nil + } + + if runtime.GOOS != "darwin" { + return nil + } + + signer := NewMacOSSigner(cfg.MacOS) + if !signer.Available() { + return fmt.Errorf("notarization requested but codesign not available") + } + + for _, artifact := range artifacts { + if artifact.OS != "darwin" { + continue + } + + fmt.Printf(" Notarizing %s (this may take a few minutes)...\n", artifact.Path) + if err := signer.Notarize(ctx, artifact.Path); err != nil { + return fmt.Errorf("failed to notarize %s: %w", artifact.Path, err) + } + } + + return nil +} + +// SignChecksums signs the checksums file with GPG. +func SignChecksums(ctx context.Context, cfg SignConfig, checksumFile string) error { + if !cfg.Enabled { + return nil + } + + signer := NewGPGSigner(cfg.GPG.Key) + if !signer.Available() { + return nil // Silently skip if not configured + } + + fmt.Printf(" Signing %s with GPG...\n", checksumFile) + if err := signer.Sign(ctx, checksumFile); err != nil { + return fmt.Errorf("failed to sign checksums: %w", err) + } + + return nil +}