Fix macOS release build and preflight release requirements - #724
Merged
Merged
Conversation
electron-rebuild was rebuilding every native module against Electron 44's headers, including macos-alias and fs-xattr (transitive deps of the optional appdmg package). macos-alias uses nan, which no longer compiles against Electron 44's V8 due to the new ExternalPointerTypeTag argument on v8::External::New and External::Value, so npm install failed outright. appdmg is a build-time DMG packager that runs under Node, never inside Electron, so rebuilding it against Electron headers was incorrect regardless. better-sqlite3 is the only native module the app loads at runtime. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three independent failures broke `npm run publish-prod` on macOS after the dependency upgrade. Each one masked the next. yauzl: extract-zip@2.0.1 pins yauzl@2.10.0, which uses fd-slicer@1.1.0. On Node 24 it stops delivering read streams partway through an archive (20 of 583 entries, 2 streams opened) and never settles. Inside electron-packager's promise chain the event loop simply drains, so the build exited 0 with no error and no out/ directory while extracting the Electron template zips. yauzl@3 drops fd-slicer and handles the same archive completely; extract-zip only calls yauzl.open, so the API is compatible. image-size: the unscoped "image-size": "^2.0.4" override upgraded appdmg's copy too. appdmg declares ^0.7.4 and calls it callback-style, but v2 dropped the default-function export, so the DMG maker crashed with "sizeOf is not a function". Scope appdmg to ^1.2.1, which keeps the callback API and is still the patched release the override was added for. rebuildConfig: forge passes rebuildConfig straight into @electron/rebuild during packaging. Unfiltered, it rebuilds appdmg's macos-alias against Electron 44's headers, where nan no longer compiles. appdmg is a build-time DMG packager that runs under Node and survives pruning as an optional prod dependency, so it has to be excluded; better-sqlite3 is the only native module the app loads at runtime. Verified with a full `npm run make-local`: universal package built (lipo: x86_64 arm64), DMG passes hdiutil verify, zip and RELEASES.json produced. Signing and notarization are not exercised by make-local, since scripts/make.js only sets MACOS_RELEASE=true for publish. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@electron/packager defaults to continueOnError: true when signing (mac.js:402), so a missing Developer ID identity does not fail the build. It leaves the stock ad-hoc Electron signature in place, and the build dies minutes later inside @electron/notarize's pre-flight with "code object is not signed at all" — a message that describes the symptom and never names the cause. Check up front, before clean.mjs deletes the previous build, that publish has what it needs: the Spaces credentials on every platform, and on macOS that the signing identity is actually in the keychain plus the notarization credentials are set. Each failure names the missing piece and how to get it. forge.config.ts now reads the identity from MACOS_SIGNING_IDENTITY rather than hardcoding it, so the preflight cannot pass while signing uses some other identity. It throws if the variable is unset while MACOS_RELEASE is true, which only happens when forge is invoked outside scripts/make.js. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Four stacked failures broke
npm installandnpm run publish-prodon macOS after the dependency upgrade, plus a fifth problem in the release environment itself. Each one masked the next, so they only surfaced one at a time — which is also what motivated the preflight check at the end.1.
npm installfailed — unscopedelectron-rebuildscripts/postinstall.jsranelectron-rebuild --forcewith no filter, rebuilding every native module against Electron 44's headers — includingmacos-aliasandfs-xattr, transitive deps of the optionalappdmg.macos-aliasusesnan, which is incompatible with Electron 44's V8 (the newExternalPointerTypeTagargument onv8::External::New/External::Value).Added
--only better-sqlite3toscripts/postinstall.jsand therebuild/rebuild-cleanscripts.better-sqlite3is the only native module the app loads at runtime;appdmgis a build-time DMG packager that runs under Node.2. Packaging exited 0 with no output —
yauzlon Node 24The build stopped during Electron template extraction with no error message, exit code 0, and no
out/directory.extract-zip@2.0.1(the latest published version) pinsyauzl@2.10.0, which usesfd-slicer@1.1.0. On Node 24 it stops delivering read streams partway through an archive and never settles:Run under a top-level
awaitthis surfaces asDetected unsettled top-level await. Inside electron-packager's promise chain there's nothing to report it — the event loop just drains and Node exits 0.yauzl@3.4.0dropsfd-slicerand reads the same archive completely (583/583).extract-ziponly callsyauzl.open, so the API is compatible. Added as an override.3. DMG maker crashed — unscoped
image-sizeoverrideThe existing
"image-size": "^2.0.4"override upgradedappdmg's copy along with everything else.appdmgdeclares^0.7.4and calls it callback-style, but v2 dropped the default-function export.Scoped
appdmgto^1.2.1— still the patched release the override was added for, and it keeps the callback API (verified against the actualassets/dmg-background.png:{ height: 290, width: 540, type: 'png' }).4. Would have failed next —
forge.config.tsrebuildConfigForge passes
rebuildConfigstraight into@electron/rebuildduring packaging (@electron-forge/core/dist/api/package.js:181). It was{}, so it would rebuildmacos-aliasagainst Electron headers and hit the samenanwall as #1.appdmgis an optional prod dependency, so it survives pruning and gets packaged.Set to
onlyModules: ["better-sqlite3"]. The build log now shows✔ Preparing native dependencies: 1 / 1.5. Preflight the release requirements
With the build fixed,
publish-prodthen failed on code signing — and the error pointed nowhere useful:The real cause was that the machine had no Developer ID certificate at all.
@electron/packagerdefaults tocontinueOnError: truewhen signing (mac.js:402-404, "Default tocontinueOnError: truesince this was the default behavior before this option was added"), so the missing identity was swallowed. The stock ad-hoc Electron signature survived, and the first thing to complain was@electron/notarize's pre-flight check — minutes into the build, describing the symptom and never the cause.scripts/make.jsnow checks up front, beforeclean.mjsdeletes the previous build:DO_SPACES_KEY,DO_SPACES_SECRETAPPLE_IDandAPPLE_PASSWORDEach failure names the missing piece and how to obtain it.
forge.config.tsnow reads the identity fromMACOS_SIGNING_IDENTITYinstead of hardcoding it, so the preflight cannot pass while signing uses a different identity. It throws if that variable is unset whileMACOS_RELEASE=true, which only happens when forge is invoked outsidescripts/make.js— nothing in the repo or CI does that.Verification
Full
npm run make-localpasses:lipo -archson the packaged binary:x86_64 arm64— universal stitch workedhdiutil verifyon the DMG: checksum VALIDRELEASES.jsonproduced for auto-updatesPreflight behavior:
truefalsepublish+ Spaces creds, no Apple credsAPPLE_IDpublish, nothing setDO_SPACES_KEYmake local(non-publish)Prettier and ESLint clean on both changed files.
Not covered by this verification: code signing and notarization of a real build.
scripts/make.jsonly setsMACOS_RELEASE=truefor thepublishcommand, so nomake-*script exercises them. A test-sign with the new certificate does check out (full chain to Apple Root CA, secure timestamp, hardened runtime,TeamIdentifier=G762K6CH36), butpublish-devshould be run beforepublish-prodto exercise the real path.🤖 Generated with Claude Code