fix(release): rebuild native modules for Electron in fork releases - #80
Merged
Conversation
v1.2.8 shipped a macOS and Windows build whose better-sqlite3 was compiled for the runner's system Node, so the app threw on its first require and never opened a window. It was published, caught, and pulled back to a draft within minutes. `rebuild-native.ts` was given only a `buildPath`, and @electron/rebuild uses that for two separate questions: which modules are prod dependencies (the app's package.json) and where they are installed (a node_modules tree). Under pnpm's hoisted linker no single directory answers both — the app directory says "not installed" because they are hoisted out of it, the workspace root says "not a dependency" — so it rebuilt nothing, printed success and exited 0 in a second. Upstream never hits this because build.ts packages from a `pnpm deploy --legacy --prod` tree where both hold at once; the fork workflow cannot use that tree, since it must package with the fork config, whose file mappings are relative to the app directory. So thread `projectRootPath` through and pass it from both platform jobs. The flag is additive: with `--deploy-dir` it defaults to the deploy directory, exactly today's behaviour for upstream's pipeline. Nothing caught this, which is the more important half. `codesign --verify` happily signs a bundle that cannot boot, and verify-mac.ts probes for a module named `sqlite3` — not one this app uses — and only warns when it is absent. The new verify-native-abi.ts runs the *packaged* Electron as Node and dlopens the packaged .node files, so the ABI is judged by the binary that will actually load them. Verified both ways against the real v1.2.8 artifact: it reports `NODE_MODULE_VERSION 137 ... requires 143` on the shipped bundle, and passes once the module is replaced with an electron-rebuilt one. Also drops the trust-setting removal from the macOS cleanup step. `security remove-trusted-cert -d` waits on an authorization the runner has no GUI to grant and blocks forever, which `|| true` cannot rescue; it held the v1.2.8 job open at 100% complete until it was cancelled. Runners are destroyed after the job anyway.
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.
Description
v1.2.8 was a broken release. Its macOS and Windows builds packaged a
better-sqlite3compiled for the CI runner's system Node, so the app threw on its first
requireand neveropened a window. It was published, caught, and reverted to a draft within minutes; the tag and
draft are deleted and this ships as v1.2.9 — a bump rather than a re-cut of 1.2.8, because
a client that had already updated would never re-download the same version.
Root cause.
rebuild-native.tswas given only abuildPath, and@electron/rebuildusesthat for two separate questions: which modules are prod dependencies (answered by a
package.json) and where they are installed (answered by anode_modulestree). Under pnpm'shoisted linker no single directory answers both — the app directory says "not installed"
because the native modules are hoisted out of it, the workspace root says "not a dependency" —
so it rebuilt nothing, printed success, and exited 0 in about a second. Upstream never hits
this:
build.tspackages from apnpm deploy --legacy --prodtree where both hold at once.The fork workflow cannot use that tree, because it must package with the fork config, whose
filesmappings are relative to the app directory.This fails silently by construction. electron-builder runs with
npmRebuild: false, so itpackages whatever
pnpm installcompiled, andscripts/postinstall.tsdeliberately skipselectron-rebuild when
CIis set. Electron keeps its ownNODE_MODULE_VERSIONnamespace, sothe two can never coincidentally agree — Electron 40 requires 143 where Node 24 produces
137.
Fix. Thread
projectRootPaththroughrebuild-native.tsand pass--project-root "$GITHUB_WORKSPACE"from both platform jobs. The flag is additive: with--deploy-dirit defaults to the deploy directory, exactly today's behaviour for upstream'spipeline.
The guard that was missing. Nothing caught this, which matters more than the bug.
codesign --verifysigns a bundle that cannot boot, andverify-mac.tsprobes for a modulenamed
sqlite3— not one this app uses — and only warns when absent. Newscripts/release/verify-native-abi.tsruns the packaged Electron binary as Node(
ELECTRON_RUN_AS_NODE=1, which reports Electron's module version) anddlopens the packaged.nodefiles, so the ABI is judged by the binary that will actually load them. It runs afterupload, so a failure leaves an updater-invisible draft rather than a shipped release.
Also. Drops the trust-setting removal from the macOS cleanup step.
security remove-trusted-cert -dwaits on an authorization a headless runner cannot grant and blocksforever — a hang is not something
|| truerescues. It held the v1.2.8 mac job open at 100%complete until it was cancelled. Runners are destroyed after the job, so there is nothing to
clean up.
Related issues
Follows #74. No issue; found while releasing.
Testing
Verified the new guard both ways against the real v1.2.8 artifact downloaded from the
release:
compiled against a different Node.js version using NODE_MODULE_VERSION 137. This version of Node.js requires NODE_MODULE_VERSION 143.nodereplaced by an electron-rebuilt one → passes,Verified 1 packaged native module(s)dlopen("relative path not allowed in hardened program"), which would have failed good bundles
And the fix itself:
rebuild-native.tswithprojectRootPathset → 11.7s of real work; without it → ~0s andnothing rebuilt
NODE_MODULE_VERSION 143) and iscorrectly rejected by plain Node, proving it is Electron-targeted
pnpm typecheck(app + release scripts),pnpm exec oxlint,pnpm run formatScreenshot/Recording (if applicable)
Not applicable; release tooling only.
Checklist