fix: sign and notarize macos binary (CON-787) - #321
Open
justinwlin wants to merge 2 commits into
Open
Conversation
the darwin universal binary ships with only the ad-hoc signature the go linker emits, so gatekeeper rejects it with "no usable signature". all four macos install paths (brew formula, brew cask, install.sh, runpodctl update) fetch runpodctl-darwin-all.tar.gz, so signing that one artifact fixes all of them. sign after lipo merges the per-arch builds — signing before the merge produces an invalid signature. snapshots ad-hoc sign and skip notary submission, so local builds stay credential-free. quill signs and notarizes mach-o from linux, so the job stays on ubuntu-latest.
three operability fixes on the signing pipeline:
- output: true on the quill hook. goreleaser swallows hook stdout, which
is where the notary submission id and any rejection reason go —
notarization is the most failure-prone step here and was silent.
- pin quill to v0.7.1. the installer piped into sudo sh took whatever was
latest at release time, so an upstream regression could break signing
with no change on our side, and releases were not reproducible.
- timeout-minutes: 45 on the release job. quill waits for a conclusive
notary verdict with no bound of its own; a stalled apple submission
would hold the release-${ref} concurrency lock for the default 6h and
block any retry.
justinwlin
marked this pull request as ready for review
July 30, 2026 23:48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
macOS binaries ship with only the ad-hoc signature the Go linker emits —
TeamIdentifierunset,Signature=adhoc,spctlverdictrejected. Gatekeeper refuses to run them; the brew cask path fails loudest, as "the application is damaged and can't be opened."All four macOS install paths (brew formula, brew cask,
install.sh,runpodctl update) fetchrunpodctl-darwin-all.tar.gz, so signing that one artifact fixes all four.Changes
.goreleaser.yml—quill sign-and-notarizepost-hook onuniversal_binaries, afterlipomerges the per-arch builds.output: trueso quill's notary submission id and rejection reasons reach the job log; GoReleaser swallows hook stdout otherwise.release.yml— install quill (pinned tov0.7.1), pass fiveQUILL_*secrets to GoReleaser, and bound the job attimeout-minutes: 45.Snapshots ad-hoc sign and skip notary submission (
--dry-run={{ .IsSnapshot }}), so local builds need no credentials. Job stays onubuntu-latest— quill signs and notarizes Mach-O from Linux.The timeout matters because quill waits for a conclusive notary verdict with no bound of its own: a stalled Apple submission would otherwise hold the
release-${ref}concurrency lock for the default 6h and block any retry.Verification
Snapshot build passes; hooks inherit job env; the real universal binary signs valid against the production cert
goreleaser release --snapshot --clean→ exit 0, hook fires on the merged binary and its output is now visible:GoReleaser hooks inherit the job environment — verified with a sentinel variable in an isolated project, since that is how the five secrets reach quill:
Same universal artifact signed with the real Developer ID
.p12:Not yet exercised: the live Apple notary round-trip, which only runs on a real tag. Watch the first tagged release.
Notes
upxstays scoped tolinux_amd64— it rewrites the Mach-O and would destroy the signature..tar.gzcan't be stapled (xcrun stapleris.app/.dmg/.pkgonly), so Gatekeeper does an online ticket lookup. Expected.legacyper-arch darwin binaries remain unsigned. Only pre-change clients self-update via those; currentupdate.goandinstall.shboth usedarwin-all. Unchanged by this PR.Does not fix: Homebrew tap trust (
HOMEBREW_REQUIRE_TAP_TRUST) or self-update checksum verification (API-318). Both orthogonal.CON-787