Skip to content

fix(build): run hole-punch before signing to preserve code signature#1037

Merged
BYK merged 2 commits into
mainfrom
fix/hole-punch-before-signing
May 29, 2026
Merged

fix(build): run hole-punch before signing to preserve code signature#1037
BYK merged 2 commits into
mainfrom
fix/hole-punch-before-signing

Conversation

@BYK
Copy link
Copy Markdown
Member

@BYK BYK commented May 29, 2026

Summary

Fixes #1033 — nightly macOS binaries had an invalid code signature because script/build.ts ran binpunch.processBinary() after fossilize had already signed + notarized the binary. The mutated bytes invalidated the signature, causing macOS AMFI to SIGKILL the downloaded binary, which surfaced as the opaque Setup failed with exit code 1 during cli upgrade — leaving the install bricked.

Root cause

postProcessTarget() in build.ts called processBinary(outfile) at line 388, after fossilize returned (which includes signing + notarization on main/release builds). 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-latest with can-test: true, and it passes even on signed+hole-punched main builds (run 26542990891). The real reason: AMFI only SIGKILLs an invalid-signature binary when it carries the com.apple.quarantine xattr (i.e. after being downloaded). A freshly-built binary on the build machine is not quarantined.

Changes

1. Move hole-punch into fossilize (--hole-punch flag)

  • Depends on BYK/fossilize#18 (fossilize 0.8.0)
  • Remove binpunch devDep and the post-sign processBinary() call from build.ts
  • Pass --hole-punch to the fossilize CLI invocation — fossilize now runs binpunch internally between chmod and sign+notarize, so the signature covers the final bytes

2. CI signature verification gate

  • Add a Verify code signature (darwin) step using rcodesign verify after the smoke tests, gated on FOSSILIZE_SIGN=y (main/release pushes) + darwin targets
  • This catches broken signatures that execution-based smoke tests miss (no quarantine xattr on CI)

3. Upgrade resilience (SIGKILL detection)

  • In spawnWithRetry(): capture the signal parameter from the child's close event
  • When signal === 'SIGKILL': throw a clear UpgradeError explaining the likely cause (invalid code signature) instead of the opaque Setup failed with exit code 1

4. Cleanup

  • Remove duplicate top-level patchedDependencies (already present in pnpm block)
  • Update .lore.md pipeline description to reflect hole-punch running inside fossilize

Pipeline order (fixed)

download → unsign → strip → inject SEA → chmod → HOLE-PUNCH → sign + notarize → gzip

Blocked on

  • BYK/fossilize#18 merged + released as 0.8.0 on npm (CI will fail to install fossilize@^0.8.0 until then)

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
Copy link
Copy Markdown
Contributor

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread pnpm-lock.yaml Outdated
@BYK
Copy link
Copy Markdown
Member Author

BYK commented May 29, 2026

The pnpm-lock.yaml lockfile mismatch is expected — fossilize 0.8.0 is not yet published to npm (blocked on BYK/fossilize#18). The lockfile was generated with a local file: reference for local validation. Once fossilize 0.8.0 is published, I'll run pnpm install to regenerate a clean lockfile pointing to the registry before merging.

Comment thread pnpm-lock.yaml Outdated
@github-actions
Copy link
Copy Markdown
Contributor

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://cli.sentry.dev/_preview/pr-1037/

Built to branch gh-pages at 2026-05-29 21:48 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@github-actions
Copy link
Copy Markdown
Contributor

Codecov Results 📊

❌ Patch coverage is 60.00%. Project has 4302 uncovered lines.
❌ Project coverage is 81.99%. Comparing base (base) to head (head).

Files with missing lines (1)
File Patch % Lines
src/commands/cli/upgrade.ts 60.00% ⚠️ 2 Missing and 2 partials
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        +7

Generated by Codecov Action

@BYK BYK merged commit e248180 into main May 29, 2026
29 checks passed
@BYK BYK deleted the fix/hole-punch-before-signing branch May 29, 2026 21:55
BYK added a commit that referenced this pull request May 29, 2026
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nightly macOS binary has invalid code signature (hole-punch runs after signing) → cli upgrade SIGKILL / "Setup failed with exit code 1"

1 participant