Skip to content

build(release): notarize macOS binaries#118

Merged
steipete merged 1 commit into
mainfrom
chore/notarize-macos-releases
Jul 17, 2026
Merged

build(release): notarize macOS binaries#118
steipete merged 1 commit into
mainfrom
chore/notarize-macos-releases

Conversation

@steipete

Copy link
Copy Markdown
Contributor

Summary

  • notarize every official GoReleaser Darwin binary before it is archived
  • require the runtime-only NOTARYTOOL_KEYCHAIN_PROFILE and keep failed candidates out of release output
  • independently verify the notarized requirement after archive extraction
  • document the local release contract and cover missing profile, rejection, cleanup, and both shipped architectures

Release invariants

The hook signs a temporary candidate with the existing Foundation identity, hardened runtime, timestamp, and stable identifier-based requirement. It submits an ephemeral ZIP with notarytool --no-s3-acceleration --wait, requires an Accepted JSON response with a valid submission ID, runs codesign --verify --strict --check-notarization -R=notarized, and only then replaces the GoReleaser output. No profile value is stored in Git or GitHub Actions.

Proof

Real macOS scratch snapshot using the approved login-Keychain signing identity and runtime notary profile:

$ GITCRAWL_REQUIRE_CODESIGN=1 CODESIGN_IDENTITY="$FOUNDATION_IDENTITY" NOTARYTOOL_KEYCHAIN_PROFILE="$APPROVED_NOTARY_PROFILE" GOWORK=off goreleaser release --config /private/tmp/gitcrawl-notary-behavior.bZjgwh/goreleaser.yaml --snapshot --clean --skip=publish
... target=darwin_arm64_v8.0
... target=darwin_amd64_v1
... running hook .../scripts/codesign-macos.sh .../darwin_arm64_v8.0/gitcrawl
... running hook .../scripts/codesign-macos.sh .../darwin_amd64_v1/gitcrawl
... release succeeded

The scratch config was the repository config with only dist redirected outside the checkout. Both Apple submissions were accepted. Independent checks on the archived executables:

amd64 archive_entries=CHANGELOG.md,LICENSE,README.md,gitcrawl, arch=x86_64 version=0.8.0-SNAPSHOT-2c26a60 runtime=12.0.0 timestamp_present=yes dr_sha256=9790c5ce3cad478a5bd54381376e456aaa401f659632a8caef62dd59fb0042bc notarized=yes
arm64 archive_entries=CHANGELOG.md,LICENSE,README.md,gitcrawl, arch=arm64 version=0.8.0-SNAPSHOT-2c26a60 runtime=12.0.0 timestamp_present=yes dr_sha256=9790c5ce3cad478a5bd54381376e456aaa401f659632a8caef62dd59fb0042bc notarized=yes
missing_profile_exit=1 input_unchanged=yes
designated_requirements_equal=yes

Full local gate:

  • go mod tidy plus clean module diff; gofmt; go vet ./...
  • govulncheck: no vulnerabilities
  • deadcode: clean
  • go test ./... -covermode=atomic: 85.1% total coverage
  • real build and CLI smoke: pass
  • ./scripts/test-release.sh: pass
  • credential-free GoReleaser snapshot: pass
  • docs site build: pass
  • Docker build and smoke: pass
  • AutoReview local and branch modes: clean

@steipete
steipete requested a review from a team as a code owner July 17, 2026 21:20
@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal priority bug or improvement with limited blast radius. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. labels Jul 17, 2026
@clawsweeper

clawsweeper Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codex review: needs changes before merge. Reviewed July 17, 2026, 5:23 PM ET / 21:23 UTC.

Summary
The PR notarizes official Darwin binaries before packaging, verifies notarization after archive extraction, documents the required private keychain profile, and expands release-script coverage.

Reproducibility: not applicable. as a bug report: this is a proposed release-hardening change, and the PR supplies a concrete after-fix macOS snapshot proving the intended release path on both Darwin architectures.

Review metrics: 2 noteworthy metrics.

  • Release surfaces: 6 files affected; 130 added, 10 removed. The change spans signing, packaging, verification, tests, and release documentation, so its release-contract behavior should be reviewed as one unit.
  • Shipped macOS targets: 2 architectures proven. The supplied real macOS proof covers both darwin_amd64_v1 and darwin_arm64_v8.0 archives.

Merge readiness
Overall: 🐚 platinum hermit
Proof: 🦞 diamond lobster
Patch quality: 🐚 platinum hermit
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Remove the CHANGELOG.md hunk and retain the release summary in the PR body.
  • Confirm the documented notary keychain profile is available on the authorized release Mac.

Risk before merge

  • [P1] Merging changes the authorized macOS release prerequisite: official packaging will fail until the release environment supplies the approved NOTARYTOOL_KEYCHAIN_PROFILE keychain profile. The PR documents and tests that fail-closed behavior, but the release operator should confirm the profile remains available on the release Mac.

Maintainer options:

  1. Confirm the release preflight (recommended)
    Accept the intentional release-environment requirement after confirming the authorized release Mac has the documented private NOTARYTOOL_KEYCHAIN_PROFILE.
  2. Pause for release-environment readiness
    Hold the PR if the authorized release Mac cannot yet supply the profile, because official macOS packaging will correctly fail closed.

Next step before merge

  • A narrow mechanical cleanup can remove the release-owned changelog hunk without changing the notarization implementation or its proof.

Security
Cleared: The patch keeps the notary credential runtime-only, uses the existing keychain-profile mechanism, and adds verification without introducing a concrete secret-exposure or supply-chain concern.

Review findings

  • [P3] Remove the release-owned changelog entry — CHANGELOG.md:5
Review details

Best possible solution:

Land the fail-closed notarization flow after removing the release-owned changelog edit and confirming the authorized release Mac has the documented private keychain profile; keep the release summary in the PR body rather than CHANGELOG.md.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a bug report: this is a proposed release-hardening change, and the PR supplies a concrete after-fix macOS snapshot proving the intended release path on both Darwin architectures.

Is this the best way to solve the issue?

Yes, with one release-process correction: candidate-first signing and notarization prevents failed submissions from replacing artifacts, while the release-owned changelog entry should be removed from the branch.

Full review comments:

  • [P3] Remove the release-owned changelog entry — CHANGELOG.md:5
    CHANGELOG.md is release-owned under the repository review policy, so this normal implementation PR should not add an unreleased entry. Keep the user-visible release context in the PR body or commit message and leave changelog curation to the release process.
    Confidence: 0.97

Overall correctness: patch is correct
Overall confidence: 0.78

AGENTS.md: unclear because the file could not be read completely.

Codex review notes: model internal, reasoning high; reviewed against 2c26a60d6ac6.

Label changes

Label changes:

  • add P2: This is a bounded release-hardening change with a meaningful but operator-contained effect on official macOS packaging.
  • add merge-risk: 🚨 compatibility: Official release environments without the newly required private notary keychain profile will intentionally stop before packaging.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes a real macOS GoReleaser snapshot and independent post-archive verification for both shipped architectures; no additional contributor proof is needed.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes a real macOS GoReleaser snapshot and independent post-archive verification for both shipped architectures; no additional contributor proof is needed.

Label justifications:

  • P2: This is a bounded release-hardening change with a meaningful but operator-contained effect on official macOS packaging.
  • merge-risk: 🚨 compatibility: Official release environments without the newly required private notary keychain profile will intentionally stop before packaging.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes a real macOS GoReleaser snapshot and independent post-archive verification for both shipped architectures; no additional contributor proof is needed.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes a real macOS GoReleaser snapshot and independent post-archive verification for both shipped architectures; no additional contributor proof is needed.
Evidence reviewed

Acceptance criteria:

  • [P1] ./scripts/test-release.sh.

What I checked:

  • Notarization contract: The proposed hook copies the GoReleaser output to a temporary candidate, signs it with the hardened runtime and timestamp, requires an Accepted notarytool response, verifies the notarized requirement, and only then replaces the original binary. (scripts/codesign-macos.sh:31, a1a63f4bc23c)
  • Release preflight: The release-packaging entry point now rejects official packaging when NOTARYTOOL_KEYCHAIN_PROFILE is absent and checks the added macOS tooling before release work starts. (scripts/package-release.sh:21, a1a63f4bc23c)
  • Regression coverage: The release test adds missing-profile, rejection, cleanup, notarization-verification, and both-architecture cases, matching the newly fail-closed release contract. (scripts/test-release.sh:25, a1a63f4bc23c)
  • After-fix behavior proof: The PR body provides a real macOS GoReleaser snapshot using an approved keychain identity and notary profile, with Accepted Apple submissions and independent archive checks for darwin_amd64_v1 and darwin_arm64_v8.0. (a1a63f4bc23c)
  • Current-main comparison: The supplied PR metadata identifies 2c26a60d6ac621ccc14d620596a94755521274c7 as the base and a1a63f4bc23c556abcfcd6a5f0073475591782c9 as the proposed head, so the central notarization change remains pending rather than implemented on the stated current main. (2c26a60d6ac6)

Likely related people:

  • steipete: Authored the current release-notarization proposal and supplied the macOS release-contract proof; broader current-main ownership could not be verified because read-only shell inspection failed before repository history could be queried. (role: release-flow contributor; confidence: low; commits: a1a63f4bc23c; files: scripts/codesign-macos.sh, scripts/package-release.sh, scripts/test-release.sh)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@steipete
steipete merged commit afaefcb into main Jul 17, 2026
10 checks passed
@steipete
steipete deleted the chore/notarize-macos-releases branch July 17, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant