Skip to content

ci(macos): sign and notarize release assets when Developer ID secrets exist - #2116

Merged
LHagfoss merged 2 commits into
mainfrom
ci/macos-notarize
Oct 18, 2026
Merged

LHagfoss merged 2 commits into
mainfrom
ci/macos-notarize

Conversation

@LHagfoss

Copy link
Copy Markdown
Owner

Refs #2034

What

Prepares Developer ID signing and notarization so it switches on as soon as the secrets are added. Nothing changes for releases until then.

  • scripts/macos-release-signing.sh (check, import, sign, notarize, cleanup): checks the secrets, imports the .p12 into a temporary keychain, signs with --options runtime --timestamp, submits with xcrun notarytool submit --wait (fails unless Accepted, prints the notary log), staples and runs stapler validate + spctl --assess on an app.
  • scripts/package-swift-app.sh: signs Contents/Helpers/rustcode and the bundle with MACOS_SIGN_IDENTITY when set (hardened runtime + timestamp); otherwise the ad-hoc signing used today.
  • build.yml
    • build (macOS, tag/dispatch only): sign and notarize the CLI binary before it is archived. A bare executable cannot be stapled; Gatekeeper fetches its ticket online.
    • swift-app: import → package (signed) → notarize + staple → zip → unpack the zip, set the browser quarantine flag, stapler validate, spctl --assess, codesign --verify --deep --strict → delete the keychain (always()).
    • With no secrets, check prints a ::notice:: and every signing step is skipped. A partial set fails the job, so a half-done setup is not mistaken for a signed release.
  • No entitlements: the app is not sandboxed, and neither executable uses JIT or loads third-party libraries.
  • Credentials: Apple ID + app-specific password + Team ID (chosen over an App Store Connect API key; documented).
  • macos-swift-ci.yml: runs the ad-hoc packaging path (/usr/bin/true stands in for the engine) and check on PRs.
  • Docs: new docs/macos-release-signing.md (secrets, setup, manual check). docs/install.md keeps the quarantine workaround with a note that it goes away with the first notarized release.

Secrets the owner must add

MACOS_DEVELOPER_ID_P12_BASE64, MACOS_DEVELOPER_ID_P12_PASSWORD, MACOS_NOTARY_APPLE_ID, MACOS_NOTARY_TEAM_ID, MACOS_NOTARY_PASSWORD.

Verification

  • python3 -c 'yaml.safe_load(...)' on both workflows, actionlint 1.7.7 (only the existing macos-26 unknown-label warning, which main already has), bash -n on both scripts.
  • check locally: no secrets → notice, enabled=false, exit 0; one secret → error listing the missing four, exit 1.
  • Not verified: the signed path. It needs the certificate and the Apple credentials; run the Build workflow via workflow_dispatch after adding them (see the doc's Setup step 5).

🤖 Generated with Claude Code

… exist

The swift-app job and the macOS CLI build now sign with a Developer ID
(hardened runtime, secure timestamp), notarize with notarytool, staple the
app and verify the downloaded zip with spctl, but only once all five
MACOS_* secrets are set. Without them the release stays ad-hoc signed and
the job prints a notice; a partial set fails the job.

Refs #2034

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the ci Continuous integration and workflows label Oct 18, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@LHagfoss
LHagfoss merged commit 09b3e92 into main Oct 18, 2026
7 checks passed
@LHagfoss
LHagfoss deleted the ci/macos-notarize branch October 18, 2026 14:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Continuous integration and workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant