package notarize import ( "context" "encoding/json" "fmt" "lmika.dev/actions/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, } }