wails-release/internal/notarize/notarize.go
Leon Mika 708cc0c864 Add notarytool wrapper supporting api-key and apple-id
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 10:18:46 +10:00

93 lines
2.4 KiB
Go

package notarize
import (
"context"
"encoding/json"
"fmt"
"github.com/leonmika/wails-release/internal/runner"
)
// APIKey is the App Store Connect API key credential set.
type APIKey struct {
KeyPath string
KeyID string
IssuerID string
}
// AppleID is the legacy Apple ID + app-specific password credential set.
type AppleID struct {
Username string
Password string
TeamID string
}
// Opts configures a notarization submission. Exactly one of APIKey or
// AppleID must be set.
type Opts struct {
ZipPath string
APIKey *APIKey
AppleID *AppleID
}
// Submit ships ZipPath to Apple's notary service and waits for the result.
// Rejected submissions cause the per-submission log to be fetched and
// embedded in the returned error.
func Submit(ctx context.Context, r runner.Runner, opts Opts) error {
if (opts.APIKey == nil) == (opts.AppleID == nil) {
return fmt.Errorf("notarize: exactly one of APIKey or AppleID must be set")
}
credArgs := credentialArgs(opts)
args := append([]string{"notarytool", "submit", opts.ZipPath}, credArgs...)
args = append(args, "--wait", "--output-format", "json")
out, err := r.Run(ctx, runner.Spec{Name: "xcrun", Args: args})
if err != nil {
return fmt.Errorf("notarytool submit: %w", err)
}
var result struct {
ID string `json:"id"`
Status string `json:"status"`
}
if jerr := json.Unmarshal(out, &result); jerr != nil {
return fmt.Errorf("notarytool submit: parse output: %w (raw: %s)", jerr, string(out))
}
if result.Status == "Accepted" {
return nil
}
logOut, _ := r.Run(ctx, runner.Spec{
Name: "xcrun",
Args: append([]string{"notarytool", "log", result.ID}, credArgs...),
})
return fmt.Errorf("notarization %s: %s\n%s", result.Status, result.ID, string(logOut))
}
// Staple runs `xcrun stapler staple` against the bundle.
func Staple(ctx context.Context, r runner.Runner, appPath string) error {
if _, err := r.Run(ctx, runner.Spec{
Name: "xcrun",
Args: []string{"stapler", "staple", appPath},
}); err != nil {
return fmt.Errorf("stapler staple: %w", err)
}
return nil
}
func credentialArgs(opts Opts) []string {
if opts.APIKey != nil {
return []string{
"--key", opts.APIKey.KeyPath,
"--key-id", opts.APIKey.KeyID,
"--issuer", opts.APIKey.IssuerID,
}
}
return []string{
"--apple-id", opts.AppleID.Username,
"--password", opts.AppleID.Password,
"--team-id", opts.AppleID.TeamID,
}
}