cli/pkg/build/signing/sign.go
Snider 552feb9d45 Migrate pkg/build to io.Medium abstraction (#287)
* chore(io): Migrate pkg/build to Medium abstraction

- Updated io.Medium interface with Open() and Create() methods to support streaming.
- Migrated pkg/build, pkg/build/builders, and pkg/build/signing to use io.Medium.
- Added FS field to build.Config and updated build.Builder interface.
- Refactored checksum and archive logic to use io.Medium streaming.
- Updated pkg/release and pkg/build/buildcmd to use io.Local.
- Updated unit tests to match new signatures.

* chore(io): Migrate pkg/build to Medium abstraction (fix CI)

- Fixed formatting in pkg/build/builders/wails.go.
- Fixed TestLoadConfig_Testdata and TestDiscover_Testdata to use absolute paths with io.Local to ensure compatibility with GitHub CI.
- Verified that all build and release tests pass.

* chore(io): Migrate pkg/build to Medium abstraction (fix CI paths)

- Ensured that outputDir and configPath are absolute in runProjectBuild.
- Fixed TestLoadConfig_Testdata and TestDiscover_Testdata to use absolute paths correctly.
- Verified that all build and release tests pass locally.

* chore(io): Migrate pkg/build to Medium abstraction (final fix)

- Improved io.Local to handle relative paths relative to CWD when rooted at "/".
- This makes io.Local a drop-in replacement for the 'os' package for most use cases.
- Ensured absolute paths are used in build logic and tests where appropriate.
- Fixed formatting and cleaned up debug prints.

* chore(io): address code review and fix CI

- Fix MockFile.Read to return io.EOF
- Use filepath.Match in TaskfileBuilder for precise globbing
- Stream xz data in createTarXzArchive to avoid in-memory string conversion
- Fix TestPath_RootFilesystem in local medium tests
- Fix formatting in pkg/build/buildcmd/cmd_project.go

* chore(io): resolve merge conflicts and final migration of pkg/build

- Resolved merge conflicts in pkg/io/io.go, pkg/io/local/client.go, and pkg/release/release.go.
- Reconciled io.Medium interface with upstream changes (unifying to fs.File for Open).
- Integrated upstream validatePath logic into the local medium.
- Completed migration of pkg/build and related packages to io.Medium.
- Addressed previous code review feedback on MockMedium and TaskfileBuilder.

* chore(io): resolve merge conflicts and finalize migration

- Resolved merge conflicts with dev branch.
- Unified io.Medium interface (Open returns fs.File, Create returns io.WriteCloser).
- Integrated upstream validatePath logic.
- Ensured all tests pass across pkg/io, pkg/build, and pkg/release.

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 17:59:10 +00:00

96 lines
2.2 KiB
Go

package signing
import (
"context"
"fmt"
"runtime"
"github.com/host-uk/core/pkg/io"
)
// 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, fs io.Medium, 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, fs, 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, fs io.Medium, 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, fs, 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, fs io.Medium, 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, fs, checksumFile); err != nil {
return fmt.Errorf("failed to sign checksums: %w", err)
}
return nil
}