Sign + notarize macOS forkpress binaries in BK#385
Draft
mokagio wants to merge 2 commits into
Draft
Conversation
c34ecb7 to
9c0626a
Compare
9c0626a to
ee7027f
Compare
Adds Developer ID code signing and Apple notary submission for the mac builds produced on the BK `mac` queue, distributed outside the Mac App Store. Cert installation reuses the a8c fastlane-match convention: a thin `Fastfile` exposes a `set_up_signing` lane that pulls the Developer ID cert from the shared S3 store into a temp CI keychain via `sync_code_signing`. The lane is invoked from each mac BK step before the codesign call; matches the platform-imessage and workspace flows so the operational story is the same across a8c CLI repos. Three composable bash helpers under `scripts/macos/`: - `codesign.sh` — Developer ID signing with hardened runtime and secure timestamp. Resolves the identity from the keychain by team id (default `PZYM8XX95Q`), with `FORKPRESS_CODESIGN_IDENTITY` / `--identity` overrides. Optional entitlements file. - `notarize.sh` — submits a ditto-zipped Mach-O to `xcrun notarytool` with App Store Connect API key auth, waits for the verdict, prints the notary log on rejection. - `sign-and-notarize.sh` — orchestrator. `--skip-notarize` (or `FORKPRESS_SKIP_NOTARIZE=1`) signs but skips the round-trip. `entitlements.plist` ships with an empty `<dict/>`: forkpress's embedded static PHP has opcache/JIT disabled and all deps are statically linked, so no hardened-runtime opt-outs are required. `mac-aarch64-build.sh` signs between `cargo build --release` and the COW e2e so the e2e exercises the hardened-runtime binary the artifact upload ships, then notarizes after a green e2e. `mac-x86_64-build.sh` signs the cross-built x86_64 Mach-O as a codesign smoke check but doesn't notarize: that binary uses `FORKPRESS_RUNTIME_BUNDLE=/dev/null` so it has no embedded PHP and isn't runnable, so submitting it to Apple's notary service would just burn the API quota on a non-shippable artifact. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.7 <noreply@anthropic.com>
5a4f2a2 to
37af782
Compare
Drop the three bash helpers under `scripts/macos/` (`codesign.sh`, `notarize.sh`, `sign-and-notarize.sh`) in favor of two new lanes in `fastlane/Fastfile`: - `sign_binary binary:<path>` — Developer ID codesign with hardened runtime + secure timestamp. Identity lookup uses the same keychain awk crawl as before; team id is now a Fastfile constant, no longer a CLI flag nobody overrode. - `notarize_binary binary:<path>` — ditto-zips and submits to Apple via the built-in `notarize` action (`use_notarytool: true`, `skip_stapling: true` since a raw Mach-O can't be stapled). Auth picked up from `app_store_connect_api_key` reading the `APP_STORE_CONNECT_API_KEY_*` env vars. `bundle_id` is set so the action doesn't try to read it from a non-existent `Info.plist`, and `verbose: false` is required because the action otherwise interleaves notarytool debug lines into the JSON output it tries to parse. Both lanes resolve `binary:` against `REPO_ROOT` because fastlane's `sh` action runs commands from inside `fastlane/`, where a `target/...` relative path doesn't exist — codesign then errors out with "No such file or directory". The entitlements path becomes a Fastfile constant pointing at `scripts/macos/entitlements.plist`, which stays (it's the actual plist that codesign needs). BK steps now invoke `bundle exec fastlane sign_binary` / `notarize_ binary` directly. `bundle install` is already a sunk cost for the `set_up_signing` lane, so consolidating onto fastlane removes the parallel bash chain without adding new deps. `fastlane/.gitignore` ignores the `README.md` + `report.xml` that the fastlane CLI dumps after each run. --- Generated with the help of Claude Code, https://claude.ai/code Co-Authored-By: Claude Code Opus 4.7 <noreply@anthropic.com>
37af782 to
2d47ae6
Compare
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.
Rationale
forkpress ships a Mac CLI binary outside the App Store. To pass Gatekeeper without quarantine warnings, the binary needs a Developer ID Application signature with hardened runtime, plus an Apple notarization ticket. This wires both into the BK
macqueue build steps so every CI run on this pipeline produces a release-grade binary.Distribution model is Developer ID + notarization — no App Store provisioning profile, no App Store Connect app record beyond the API key already in scope.
Intentional tradeoffs
entitlements.plistships empty. The embedded static PHP runtime is built without opcache/JIT and links all deps statically, so no hardened-runtime opt-outs are needed today. Adding entitlements later is a one-line change.xcrun stapleronly works on bundles/DMGs; for a raw CLI the notarization ticket is recorded against the binary's code-signature hash and resolved by Gatekeeper online lookup. Acceptable for a CLI distributed via tar.gz.FORKPRESS_SKIP_NOTARIZE=1escape hatch. Lets developers sign without paying the ~1–5 min notarytool round-trip, and gives a clean opt-out for any future branch run without App Store Connect creds in scope.Gotchas
security find-identity -p codesigningdoesn't surface aDeveloper ID Application:line for the team, the step exits with an actionable error. (Cert is expected to be present; if it isn't, that's a fleet-infrastructure follow-up rather than a script change.)notarytoolrejects the PEM body asinvalidPEMDocumentif the trailing newline is missing — the script writes the key withprintf '%b\n'to handle that.How to test
==> codesigning ...and==> notarization accepted (id=...); the x86_64 step does the same back-to-back without an e2e in between.scripts/macos/codesign.sh target/aarch64-apple-darwin/release/forkpressthencodesign --display --verbose=2should reportAuthority=Developer ID Application: ...and the hardened-runtime flag.Stacked on #312 — base is
mokagio/buildkite-ci-skeleton, retarget totrunkafter that merges.