fix(build): run hole-punch before signing to preserve code signature#1037
Conversation
Move ICU hole-punch from post-fossilize (build.ts) into fossilize's pipeline via the new --hole-punch flag (fossilize 0.8.0). This ensures the signature covers the final bytes, fixing the invalid macOS code signature that caused AMFI SIGKILL on downloaded binaries. Changes: - Remove binpunch devDep and post-sign processBinary() call from build.ts - Pass --hole-punch to fossilize (runs before sign+notarize) - Bump fossilize ^0.7.0 -> ^0.8.0 - Add CI signature verification gate (rcodesign verify) for darwin builds on main/release pushes — smoke tests alone don't catch broken sigs because AMFI only SIGKILLs quarantined (downloaded) binaries - Detect SIGKILL in upgrade.ts spawnWithRetry() and surface a clear error instead of opaque 'Setup failed with exit code 1' - Remove duplicate top-level patchedDependencies (already in pnpm block) Fixes #1033
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 186ff22. Configure here.
|
The |
|
Codecov Results 📊❌ Patch coverage is 60.00%. Project has 4302 uncovered lines. Files with missing lines (1)
Coverage diff@@ Coverage Diff @@
## main #PR +/-##
==========================================
- Coverage 82.01% 81.99% -0.02%
==========================================
Files 329 329 —
Lines 23806 23883 +77
Branches 15543 15603 +60
==========================================
+ Hits 19522 19581 +59
- Misses 4284 4302 +18
- Partials 1643 1650 +7Generated by Codecov Action |
Updates fossilize to 0.8.1 which fixes a bug where `strip` failure on cross-compiled binaries (e.g., linux-arm64 on x86_64 host) called `process.exit()` instead of falling through as non-fatal. This was a pre-existing issue that predates #1037 — the `run()` helper in fossilize has always called `process.exit()` for numeric error codes, bypassing the try/catch. The fix uses `execFileAsync` directly so the error is properly caught and treated as non-fatal (prints a warning and continues with an unstripped binary). Upstream: [BYK/fossilize#21](BYK/fossilize#21)

Summary
Fixes #1033 — nightly macOS binaries had an invalid code signature because
script/build.tsranbinpunch.processBinary()afterfossilizehad already signed + notarized the binary. The mutated bytes invalidated the signature, causing macOS AMFI to SIGKILL the downloaded binary, which surfaced as the opaqueSetup failed with exit code 1duringcli upgrade— leaving the install bricked.Root cause
postProcessTarget()inbuild.tscalledprocessBinary(outfile)at line 388, after fossilize returned (which includes signing + notarization onmain/releasebuilds). The hole-punched bytes broke the signature.Why CI didn't catch it
The issue claimed "the darwin binary is never executed on macOS in CI" — that's incorrect. CI runs the darwin-arm64 smoke test on
macos-latestwithcan-test: true, and it passes even on signed+hole-punchedmainbuilds (run 26542990891). The real reason: AMFI only SIGKILLs an invalid-signature binary when it carries thecom.apple.quarantinexattr (i.e. after being downloaded). A freshly-built binary on the build machine is not quarantined.Changes
1. Move hole-punch into fossilize (
--hole-punchflag)binpunchdevDep and the post-signprocessBinary()call frombuild.ts--hole-punchto the fossilize CLI invocation — fossilize now runs binpunch internally betweenchmodandsign+notarize, so the signature covers the final bytes2. CI signature verification gate
Verify code signature (darwin)step usingrcodesign verifyafter the smoke tests, gated onFOSSILIZE_SIGN=y(main/release pushes) + darwin targets3. Upgrade resilience (SIGKILL detection)
spawnWithRetry(): capture thesignalparameter from the child'scloseeventsignal === 'SIGKILL': throw a clearUpgradeErrorexplaining the likely cause (invalid code signature) instead of the opaqueSetup failed with exit code 14. Cleanup
patchedDependencies(already present inpnpmblock).lore.mdpipeline description to reflect hole-punch running inside fossilizePipeline order (fixed)
Blocked on
fossilize@^0.8.0until then)