diff --git a/internal/codesign/sign.go b/internal/codesign/sign.go new file mode 100644 index 0000000..a7a000e --- /dev/null +++ b/internal/codesign/sign.go @@ -0,0 +1,41 @@ +package codesign + +import ( + "context" + "fmt" + + "github.com/leonmika/wails-release/internal/runner" +) + +// SignOpts configures a codesign invocation. +type SignOpts struct { + AppPath string + Identity string + KeychainPath string +} + +// Sign runs `codesign` against a .app bundle, signing recursively with +// the hardened runtime and a secure timestamp. +func Sign(ctx context.Context, r runner.Runner, opts SignOpts) error { + args := []string{ + "--deep", "--force", "--options", "runtime", "--timestamp", + "--sign", opts.Identity, + "--keychain", opts.KeychainPath, + opts.AppPath, + } + if _, err := r.Run(ctx, runner.Spec{Name: "codesign", Args: args}); err != nil { + return fmt.Errorf("codesign sign: %w", err) + } + return nil +} + +// Verify runs `codesign --verify --deep --strict` against the bundle. +func Verify(ctx context.Context, r runner.Runner, appPath string) error { + if _, err := r.Run(ctx, runner.Spec{ + Name: "codesign", + Args: []string{"--verify", "--deep", "--strict", appPath}, + }); err != nil { + return fmt.Errorf("codesign verify: %w", err) + } + return nil +} diff --git a/internal/codesign/sign_test.go b/internal/codesign/sign_test.go new file mode 100644 index 0000000..b47bf56 --- /dev/null +++ b/internal/codesign/sign_test.go @@ -0,0 +1,47 @@ +package codesign_test + +import ( + "context" + "reflect" + "testing" + + "github.com/leonmika/wails-release/internal/codesign" + "github.com/leonmika/wails-release/internal/runner" +) + +func TestSign_BuildsCorrectArgs(t *testing.T) { + f := &runner.Fake{} + f.On("codesign", nil).Return(nil, nil) + + err := codesign.Sign(context.Background(), f, codesign.SignOpts{ + AppPath: "/build/MyApp.app", + Identity: "Developer ID Application: Acme Inc (ABCD1234)", + KeychainPath: "/tmp/k", + }) + if err != nil { + t.Fatalf("unexpected: %v", err) + } + want := []string{ + "--deep", "--force", "--options", "runtime", "--timestamp", + "--sign", "Developer ID Application: Acme Inc (ABCD1234)", + "--keychain", "/tmp/k", + "/build/MyApp.app", + } + if !reflect.DeepEqual(f.Calls[0].Args, want) { + t.Fatalf("args got %v want %v", f.Calls[0].Args, want) + } +} + +func TestVerify_BuildsCorrectArgs(t *testing.T) { + f := &runner.Fake{} + f.On("codesign", nil).Return(nil, nil) + + err := codesign.Verify(context.Background(), f, "/build/MyApp.app") + if err != nil { + t.Fatalf("unexpected: %v", err) + } + want := []string{"--verify", "--deep", "--strict", "/build/MyApp.app"} + if !reflect.DeepEqual(f.Calls[0].Args, want) { + t.Fatalf("args got %v want %v", f.Calls[0].Args, want) + } +}