Dashboard › spotlight › Session 1VcfYc7UnpgK
1VcfYc7UnpgKlFxbPPerform a rigorous, skeptical adversarial CORRECTNESS review of a dependency-bump PR. Focus STRICTLY on REAL bugs and supply-chain/resolution correctness — NOT style/cosmetics.
Repo: /home/byk/Code/getsentry/spotlight Branch: chore/electron-builder-26 (PR #1335), base main.
The change: bump electron-builder from ^24.13.3 to ^26.15.7 in packages/spotlight/package.json, plus the resulting pnpm-lock.yaml changes (and a .lore.md update which you can ignore).
Read the diff:
git -C /home/byk/Code/getsentry/spotlight diff origin/main...chore/electron-builder-26 -- packages/spotlight/package.json
and inspect lockfile resolution with grep/sed on /home/byk/Code/getsentry/spotlight/pnpm-lock.yaml.
Context / intent:
packages/spotlight, invoked via packages/spotlight/electron-builder.cjs -> build:mac).@electron/notarize@2.5.0 (which wraps the notarytool JSON.parse in try/catch) and @electron/osx-sign@1.3.3 (CJS-safe), instead of the old 2.2.1 / 1.0.5.Rigorously verify these REAL-bug concerns, using file evidence from the lockfile and node_modules:
Does electron-builder@26.15.7 actually resolve app-builder-lib@26.15.7 → @electron/notarize@2.5.0 and @electron/osx-sign@1.3.3 on the macOS path? Confirm from the lockfile dependency block for app-builder-lib@26.15.7(...). Also confirm via installed node_modules: readlink -f node_modules/.pnpm/electron-builder@26.15.7*/node_modules/app-builder-lib.
Stale coexisting versions: the lockfile still contains app-builder-lib@24.13.3, @electron/notarize@2.2.1, @electron/osx-sign@1.0.5. ...
I'll perform a rigorous adversarial correctness review of this dependency-bump PR. Let me start by examining the diff and lockfile.
[
{
"content": "Read the PR diff (package.json)",
"status": "in_progress",
"priority": "high"
},
{
"content": "Verify app-builder-lib@26 resolves notarize 2.5.0 / osx-sign 1.3.3",
"status": "pending",
"priority": "high"
},
{
"content": "Trace stale 24.13.3 / notarize 2.2.1 / osx-sign 1.0.5 consumers",
"status": "pending",
"priority": "high"
},
{
"content": "Confirm osx-sign 1.3.3 CJS-safety in node_modules",
"status": "pending",
"priority": "high"
},
{
"content": "Check engines.node vs repo/CI Node",
"status": "pending",
"priority": "high"
},
{
"content": "Check peerDep conflicts (squirrel-windows, electron peer)",
"status": "pending",
"priority": "medium"
},
{
"content": "Scan for silent risky version changes (builder-util, dmg-builder, universal, plist)",
"status": "pending",
"priority": "medium"
},
{
"content": "Verify electron-builder.cjs config + notarize ...
diff --git a/packages/spotlight/package.json b/packages/spotlight/package.json
index 1ef4b806..2ba8b305 100644
--- a/packages/spotlight/package.json
+++ b/packages/spotlight/package.json
@@ -32,9 +32,7 @@
"test:e2e:electron": "playwright test tests/electron.test.ts",
"sample": "node ./_fixtures/send_to_sidecar.cjs"
},
- "files": [
- "dist"
- ],
+ "files": ["dist"],
"bin": {
"spotlight": "./dist/run.js"
},
@@ -105,7 +103,7 @@
"dayjs": "^1.11.13",
"dotenv": "^16.4.5",
"electron": "^39.8.5",
- "electron-builder": "^24.13.3",
+ "electron-builder": "^26.15.7",
"electron-store": "^10.0.1",
"electron-updater": "^6.6.2",
"eslint": "^8.57.1",
.lore.md | 18 +- packages/spotlight/package.json | 6 +- pnpm-lock.yaml | 703 +++++++++++++++++++++++++++++++--------- 3 files changed, 562 insertions(+), 165 deletions(-)
The diff is clean: only the electron-builder bump + a cosmetic files reformatting. Let me examine the lockfile resolution.
3862: electron-builder@26.15.7: 11289: electron-builder@26.15.7(electron-builder-squirrel-windows@24.13.3):
3096: app-builder-lib@24.13.3: 3103: app-builder-lib@26.15.7: 10329: app-builder-lib@24.13.3(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3): 10363: app-builder-lib@26.15.7(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3):
<path>/home/byk/Code/getsentry/spotlight/pnpm-lock.yaml</path> <type>file</type> <content> 10363: app-builder-lib@26.15.7(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3): 10364: dependencies: 10365: '@electron/asar': 3.4.1 10366: '@electron/fuses': 1.8.0 10367: '@electron/get': 3.1.0 10368: '@electron/notarize': 2.5.0 10369: '@electron/osx-sign': 1.3.3 10370: '@electron/rebuild': 4.2.0 10371: '@electron/universal': 2.0.3 10372: '@malept/flatpak-bundler': 0.4.0 10373: '@noble/hashes': 2.2.0 10374: '@peculiar/webcrypto': 1.7.1 10375: '@types/fs-extra': 9.0.13 10376: ajv: 8.18.0 10377: asn1js: 3.0.10 10378: async-exit-hook: 2.0.1 10379: builder-util: 26.15.3 10380: builder-util-runtime: 9.7.0 10381: chromium-pickle-js: 0.2.0 10382: ci-info: 4.3.1 10383: debug: 4.4.3 10384: dmg-builder: 26.15.7(electron-builder-squirrel-windows@24.13.3) 10385: ...
Confirmed: app-builder-lib@26.15.7 resolves @electron/notarize@2.5.0, @electron/osx-sign@1.3.3, plist@3.1.0 (good — no plist bump), @electron/universal@2.0.3. Now let me look at the electron-builder@26.15.7 block and the dmg-builder chain to confirm the macOS path.
<path>/home/byk/Code/getsentry/spotlight/pnpm-lock.yaml</path> <type>file</type> <content> 11289: electron-builder@26.15.7(electron-builder-squirrel-windows@24.13.3): 11290: dependencies: 11291: app-builder-lib: 26.15.7(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3) 11292: builder-util: 26.15.3 11293: builder-util-runtime: 9.7.0 11294: chalk: 4.1.2 11295: ci-info: 4.4.0 11296: dmg-builder: 26.15.7(electron-builder-squirrel-windows@24.13.3) 11297: fs-extra: 10.1.0 11298: lazy-val: 1.0.5 11299: simple-update-notifier: 2.0.0 11300: yargs: 17.7.2 11301: transitivePeerDependencies: 11302: - electron-builder-squirrel-windows 11303: - supports-color 11304: 11305: electron-publish@24.13.1: 11306: dependencies: 11307: '@types/fs-extra': 9.0.13 11308: builder-util: 24.13.1 11309: builder-util-runtime: 9.2.4 11310: chalk: 4.1.2 11311: fs-extra: 10.1.0 11312: ...
electron-builder@26.15.7 → app-builder-lib@26.15.7 → notarize 2.5.0 / osx-sign 1.3.3. Now the critical question: who pulls the stale 24.13.3 chain? Let me examine the importer for spotlight and the squirrel-windows dependency.
271: version: 26.15.7(electron-builder-squirrel-windows@24.13.3) 3101: electron-builder-squirrel-windows: 24.13.3 3108: electron-builder-squirrel-windows: 26.15.7 3784: dmg-builder@26.15.7: 3859: electron-builder-squirrel-windows@24.13.3: 10329: app-builder-lib@24.13.3(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3): 10343: dmg-builder: 26.15.7(electron-builder-squirrel-windows@24.13.3) 10345: electron-builder-squirrel-windows: 24.13.3(dmg-builder@26.15.7) 10363: app-builder-lib@26.15.7(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3): 10384: dmg-builder: 26.15.7(electron-builder-squirrel-windows@24.13.3) 10388: electron-builder-squirrel-windows: 24.13.3(dmg-builder@26.15.7) 11197: dmg-builder@26.15.7(electron-builder-squirrel-windows@24.13.3): 11199: app-builder-lib: 26.15.7(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3) 11204: - electron-builder-squirrel-windows 11279: ...
<path>/home/byk/Code/getsentry/spotlight/pnpm-lock.yaml</path> <type>file</type> <content> 10329: app-builder-lib@24.13.3(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3): 10330: dependencies: 10331: '@develar/schema-utils': 2.6.5 10332: '@electron/notarize': 2.2.1 10333: '@electron/osx-sign': 1.0.5 10334: '@electron/universal': 1.5.1 10335: '@malept/flatpak-bundler': 0.4.0 10336: '@types/fs-extra': 9.0.13 10337: async-exit-hook: 2.0.1 10338: bluebird-lst: 1.0.9 10339: builder-util: 24.13.1 10340: builder-util-runtime: 9.2.4 10341: chromium-pickle-js: 0.2.0 10342: debug: 4.4.3 10343: dmg-builder: 26.15.7(electron-builder-squirrel-windows@24.13.3) 10344: ejs: 3.1.10 10345: electron-builder-squirrel-windows: 24.13.3(dmg-builder@26.15.7) 10346: electron-publish: 24.13.1 10347: form-data: 4.0.6 10348: fs-extra: 10.1.0 10349: hosted-git-info: 4.1.0 10350: ...
Now I understand the topology. The stale 24.13.3 chain (with notarize 2.2.1 / osx-sign 1.0.5) is pulled ONLY by electron-builder-squirrel-windows@24.13.3, which itself depends on app-builder-lib@24.13.3. Let me confirm the spotlight importer block and how squirrel-windows is pinned, plus check whether spotlight declares squirrel-windows directly.
<path>/home/byk/Code/getsentry/spotlight/pnpm-lock.yaml</path> <type>file</type> <content> 260: dayjs: 261: specifier: ^1.11.13 262: version: 1.11.13 263: dotenv: 264: specifier: ^16.4.5 265: version: 16.5.0 266: electron: 267: specifier: ^39.8.5 268: version: 39.8.5 269: electron-builder: 270: specifier: ^26.15.7 271: version: 26.15.7(electron-builder-squirrel-windows@24.13.3) 272: electron-store: 273: specifier: ^10.0.1 274: version: 10.0.1 275: electron-updater: 276: specifier: ^6.6.2 277: version: 6.6.2 278: eslint: 279: specifier: ^8.57.1 280: version: 8.57.1 281: eslint-plugin-react-hooks: 282: specifier: ^4.6.2 283: version: 4.6.2(eslint@8.57.1) 284: eslint-plugin-react-refresh: 285: specifier: ^0.4.12 286: version: 0.4.20(eslint@8.57.1) 287: fetch-event-stream: 288: ...
---nmpm overrides---
24: "overrides": {
Interesting. electron-builder@26.15.7 is resolved with a peer (electron-builder-squirrel-windows@24.13.3). Spotlight does NOT declare squirrel-windows directly. This is an optional peer dependency of electron-builder. Let me check how squirrel-windows enters — whether it's actually installed/present or just a peer marker. Let me look at the electron-builder@26.15.7 peer declaration and the squirrel-windows package.
<path>/home/byk/Code/getsentry/spotlight/pnpm-lock.yaml</path>
<type>file</type>
<content>
3859: electron-builder-squirrel-windows@24.13.3:
3860: resolution: {integrity: sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==}
3861:
3862: electron-builder@26.15.7:
3863: resolution: {integrity: sha512-DBpaNzxsPs1BvEblzFoNriSbzsBqDCy/gseIngeEhYzQG1IxfB7Hvc2tBBVmpWE2BTQGP9J1RrAvDT+Vc/uAxg==}
3864: engines: {node: '>=14.0.0'}
3865: hasBin: true
3866:
3867: electron-publish@24.13.1:
3868: resolution: {integrity: sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==}
(Showing lines 3859-3868 of 15267. Use offset=3869 to continue.)
</content>
<path>/home/byk/Code/getsentry/spotlight/pnpm-lock.yaml</path>
<type>file</type>
<content>
3862: electron-builder@26.15.7:
3863: resolution: {integrity: sha512-DBpaNzxsPs1BvEblzFoNriSbzsBqDCy/gseIngeEhYzQG1IxfB7Hvc2tBBVmpWE2BTQGP9J1RrAvDT+Vc/uAxg==}
3864: engines: {node: '>=14.0.0'}
3865: hasBin: true
3866:
3867: electron-publish@24.13.1:
3868: resolution: {integrity: sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==}
3869:
3870: electron-publish@26.15.3:
3871: resolution: {integrity: sha512-g/2bn8YTavY4cuS5F+jOS7zmZbXXBV8KZ8yHKfJjFPoKtzBqrpCdNPxBd3tqdBwP7BVd0lGzf7Bk2s0KesWZ4Q==}
3872:
3873: electron-store@10.0.1:
3874: resolution: {integrity: sha512-Ok0bF13WWdTzZi9rCtPN8wUfwx+yDMmV6PAnCMqjNRKEXHmklW/rV+6DofV/Vf5qoAh+Bl9Bj7dQ+0W+IL2psg==}
3875: engines: {node: '>=20'}
3876:
3877: electron-to-chromium@1.5.145:
3878: resolution: {integrity: ...
Key finding: electron-builder@26.15.7 declares engines: {node: '>=14.0.0'} in the lockfile — NOT >=22.12. This addresses concern #4 partially. Let me verify against the installed node_modules package.json for both electron-builder and app-builder-lib (lockfile engines can occasionally differ from published). Let me now do the node_modules verification and the squirrel-windows peer investigation.
=== electron-builder -> app-builder-lib readlink ===
/home/byk/Code/getsentry/spotlight/node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/app-builder-lib
=== app-builder-lib@26 engines ===
{"node":">=14.0.0"}
node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3 node_modules/.pnpm/electron-builder@26.15.7_electron-builder-squirrel-windows@24.13.3 node_modules/.pnpm/@electron+notarize@2.5.0 node_modules/.pnpm/@electron+osx-sign@1.3.3
Confirmed via node_modules: electron-builder@26.15.7 → app-builder-lib@26.15.7, engines node >=14.0.0. Now let me verify the app-builder-lib@26's OWN node_modules resolve notarize 2.5.0 / osx-sign 1.3.3 (not the hoisted 24 versions), and check osx-sign CJS-safety.
=== app-builder-lib@26 -> notarize ===
/home/byk/Code/getsentry/spotlight/node_modules/.pnpm/@electron+notarize@2.5.0/node_modules/@electron/notarize
=== app-builder-lib@26 -> osx-sign ===
/home/byk/Code/getsentry/spotlight/node_modules/.pnpm/@electron+osx-sign@1.3.3/node_modules/@electron/osx-sign
=== notarize version ===
node:internal/modules/cjs/loader:1424
throw err;
^
Error: Cannot find module 'node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/@electron/notarize/package.json'
Require stack:
- /home/byk/Code/getsentry/spotlight/[eval]
at Module._resolveFilename (node:internal/modules/cjs/loader:1421:15)
at defaultResolveImpl (node:internal/modules/cjs/loader:1059:19)
at resolveForCJSWithHooks (node:internal/modules/cjs/loader:1064:22)
at Module._load (node:internal/modules/cjs/loader:1227:37)
at TracingChannel.traceSync (node:diagnostics_channel:328:14)
at wrapModuleLoad ...
=== osx-sign@1.3.3 main/exports ===
main: dist/cjs/index.js
type: undefined
exports: undefined
=== does dist/cjs/index.js exist? ===
-rw-r--r-- 1 byk byk 2286 Jul 21 12:19 node_modules/.pnpm/@electron+osx-sign@1.3.3/node_modules/@electron/osx-sign/dist/cjs/index.js
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
The readlinks resolved correctly (the require errors were just my relative-path mistake, not real). Confirmed:
@2.5.0 symlink and osx-sign @1.3.3 symlink.main: dist/cjs/index.js, type: undefined (so CJS), exports: undefined (no exports map to break require()), and the CJS file exists with "use strict". CJS-safe confirmed — no repeat of the 1.0.5 breakage.Let me confirm notarize@2.5.0 has the try/catch wrapping, and verify the notarize versions resolved by checking package.json directly.
=== notarize@2.5.0 version + type ===
version: 2.5.0 main: lib/index.js type: undefined
=== notarytool JSON.parse try/catch check ===
28: function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29: function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
87: catch (e) {
138: parsed = JSON.parse(rawOut);
140: catch (err) {
141: throw new Error(`Failed to notarize via notarytool. Failed with unexpected result: \n\n${rawOut}`);
Confirmed: notarize@2.5.0 wraps JSON.parse (line 138) in a try/catch (lines 140-141) that surfaces Apple's real error. This is the stated goal.
Now the critical concern #2 — does the macOS build path EVER touch the stale 24.13.3 chain? Let me trace this. The stale chain is pulled by electron-builder-squirrel-windows@24.13.3. I need to verify (a) it's a Windows-only path, (b) whether app-builder-lib@26's macPackager could ever require the hoisted 24 versions. Let me check how the notarize/osx-sign is resolved from macPackager at runtime.
=== how macPackager references notarize/osx-sign === === app-builder-lib@26 package.json deps (notarize/osx-sign pins) === notarize: 2.5.0 osx-sign: 1.3.3 universal: 2.0.3
app-builder-lib@26 package.json pins notarize 2.5.0, osx-sign 1.3.3, universal 2.0.3 — exact pins, so pnpm nesting guarantees macPackager resolves those from its OWN scoped node_modules, NOT the hoisted 24 chain. Let me confirm the macPackager filename and where notarize is invoked, to be thorough about the runtime require path.
=== find mac packager + notarize refs === node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/app-builder-lib/out/mac/MacTargetHelper.js node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/app-builder-lib/out/mac/MacTargetHelper.js.map node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/app-builder-lib/out/mac/MacTargetHelper.d.ts node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/app-builder-lib/out/targets/pkg.js node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/app-builder-lib/out/targets/pkg.js.map node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/app-builder-lib/out/options/macOptions.js...
=== MacTargetHelper notarize logic ===
221: const teamId = process.env.APPLE_TEAM_ID;
222: const appleId = process.env.APPLE_ID;
223: const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD;
236: const appleApiKey = process.env.APPLE_API_KEY;
237: const appleApiKeyId = process.env.APPLE_API_KEY_ID;
238: const appleApiIssuer = process.env.APPLE_API_ISSUER;
241: throw new builder_util_1.InvalidConfigurationError(`Env vars APPLE_API_KEY, APPLE_API_KEY_ID and APPLE_API_ISSUER need to be set`);
245: const keychain = process.env.APPLE_KEYCHAIN;
246: const keychainProfile = process.env.APPLE_KEYCHAIN_PROFILE;
256: async notarizeIfProvided(appPath) {
257: const notarizeOptions = this.packager.platformSpecificBuildOptions.notarize;
258: if (notarizeOptions === false) {
259: builder_util_1.log.info({ reason: "`notarize` options were set explicitly `false`" }, "skipped macOS ...
This is the crux. Let me read the full getNotarizeOptions/notarize-detection logic (lines ~210-268) to verify the env-var-triggered auto-notarize path and how @electron/notarize is imported via dynamicImport.
<path>/home/byk/Code/getsentry/spotlight/node_modules/.pnpm/app-builder-lib@26.15.7_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3/node_modules/app-builder-lib/out/mac/MacTargetHelper.js</path>
<type>file</type>
<content>
200: return "mas";
201: }
202: if (targetName === "mas-dev") {
203: return "mas-dev";
204: }
205: return "mac";
206: }
207: /**
208: * Returns true when hardened runtime will be active for signing.
209: * For non-MAS builds it defaults to on; for MAS it defaults to off.
210: */
211: static isHardenedRuntimeEnabledForSigning(isMas, config) {
212: return isMas ? config.hardenedRuntime === true : config.hardenedRuntime !== false;
213: }
214: static assertSafePathForCommandUsage(pathValue, description) {
215: if (/[\0\r\n"'`$;&|<>]/.test(pathValue)) {
216: throw new builder_util_1.InvalidConfigurationError(`Invalid ${description}: contains ...
This is the definitive answer for concern #7. The env-var-triggered auto-notarize path still exists in v26 (lines 236-243): if APPLE_API_KEY/APPLE_API_KEY_ID/APPLE_API_ISSUER are set, it auto-notarizes with tool: "notarytool", passing exactly those. This matches the repo's build.yml which exports those env vars. Critically:
dynamicImport("@electron/notarize") — resolved relative to MacTargetHelper.js's location → its own scoped node_modules → notarize@2.5.0. NOT the hoisted 24 chain.Now let me verify the dynamicImport mechanism resolves to the correct scoped copy (this is the real supply-chain question), and check the notarize config default behavior vs v24. Let me also check the electron-builder.cjs config to confirm intent.
#!/usr/bin/env node
require("dotenv").config();
const builder = require("electron-builder");
let mac = {
target: [
{
target: "default",
arch: ["x64", "arm64"],
},
],
icon: "resources/icons/mac/icon.icns",
hardenedRuntime: true,
gatekeeperAssess: false,
entitlements: "build/entitlements.mac.plist",
entitlementsInherit: "build/entitlements.mac.plist",
cscLink: process.env.CSC_LINK,
cscKeyPassword: process.env.CSC_KEY_PASSWORD,
};
let afterSign = "scripts/notarize.cjs";
if (!process.env.CSC_LINK || !process.env.CSC_KEY_PASSWORD) {
mac = {
...mac,
target: [
{
target: "default",
arch: ["arm64"],
},
],
identity: null,
};
afterSign = undefined;
}
builder.build({
publish: "never",
config: {
appId: "io.sentry.spotlight",
productName: "Spotlight",
asarUnpack: ["resources/**"],
afterSign,
npmRebuild: false,
extraMetadata: {
main: "./dist-electron/main/index.js",
},
...
=== dynamicImport impl ===
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.dynamicImport = dynamicImport;
// TypeScript's "module": "CommonJS" compiles `await import(x)` to
// `await Promise.resolve().then(() => require(x))`, which fails for ESM-only
// packages. helpers/dynamic-import.js is plain JS so TypeScript never
// transforms its native import() call — route through it instead.
const _helper = require("../../helpers/dynamic-import");
function dynamicImport(modulePath) {
return _helper.dynamicImport(modulePath);
}
//# sourceMappingURL=dynamicImport.js.map
=== resolve @electron/notarize FROM app-builder-lib@26 dir ===
/home/byk/Code/getsentry/spotlight/node_modules/.pnpm/@electron+notarize@2.5.0/node_modules/@electron/notarize/lib/index.js
Definitive proof for concern #2: require.resolve('@electron/notarize') from app-builder-lib@26's directory resolves to notarize@2.5.0 — the stale 2.2.1 is NEVER reached on the macOS path. The dynamicImport uses a helpers/dynamic-import.js native import() which resolves relative to the caller module, landing in the scoped node_modules.
Two important observations about electron-builder.cjs:
afterSign = "scripts/notarize.cjs" — but per repo lore this is dead code (electron-builder notarizes internally). Let me verify what scripts/notarize.cjs does, since it IS referenced as afterSign and could be a live path.notarize config key is not explicitly set → v26 auto-detects via env vars (confirmed above).Let me check scripts/notarize.cjs and confirm the afterSign interaction — this is a real concern because if afterSign runs a notarize AND electron-builder also auto-notarizes, there could be double-notarization or a conflict.
require("dotenv").config();
const { notarize } = require("@electron/notarize");
exports.default = async function notarizing(context) {
const { electronPlatformName, appOutDir } = context;
if (electronPlatformName !== "darwin") {
return;
}
const appName = context.packager.appInfo.productFilename;
return await notarize({
appBundleId: "io.sentry.spotlight",
appPath: `${appOutDir}/${appName}.app`,
appleApiKeyId: process.env.APPLE_API_KEY_ID,
appleApiKey: process.env.APPLE_API_KEY,
appleApiIssuer: process.env.APPLE_API_ISSUER,
});
};
=== EXIT: 0 ===
This is a significant finding that needs careful analysis. scripts/notarize.cjs IS wired as afterSign in the config, and it calls notarize() from @electron/notarize. This contradicts the lore note that it's "dead code". Let me analyze:
Which @electron/notarize does scripts/notarize.cjs resolve? It's in packages/spotlight/, so it resolves from the package's own node_modules → could be the hoisted version. Let me check.
Is afterSign actually set at build time? Only if CSC_LINK AND CSC_KEY_PASSWORD are set. In CI, signing uses those. Let me verify the v24 vs v26 behavior: does v26 run BOTH the afterSign hook AND its internal notarizeIfProvided? That would be a double-notarize (or the internal one is what changed).
Let me check the notarize resolution for scripts/notarize.cjs and whether v24 had internal auto-notarize too.
=== which notarize does scripts/notarize.cjs resolve? (from packages/spotlight) === /home/byk/Code/getsentry/spotlight/node_modules/.pnpm/@electron+notarize@2.5.0/node_modules/@electron/notarize/lib/index.js === is @electron/notarize a direct dep of spotlight? === 73: "@electron/notarize": "^2.5.0", === version of that resolved notarize === 2.5.0
Important clarification: @electron/notarize@^2.5.0 is a direct devDependency of spotlight (line 73), so scripts/notarize.cjs resolves notarize@2.5.0 regardless of electron-builder version. This was already true on main.
Now the critical double-notarize question. In v24, app-builder-lib@24 had its own internal notarize detection too. Let me determine: does the afterSign hook running notarize + electron-builder's internal auto-notarize cause a DOUBLE notarization in v26 (new behavior) vs v24? This is the key regression risk. Let me check whether v24's macPackager also auto-notarized on env vars, and whether the CI actually reached "notarization successful" once or twice.
Per lore [d:e0422d], the successful run 29828172734 (on main, post #1334, still electron-builder 24) logged "notarization successful" — that's electron-builder's INTERNAL message (line 269 in v26; v24 has equivalent). Let me verify v24 had the same internal auto-notarize, which would mean afterSign's notarize.cjs was already redundant (truly dead/duplicate) on main.
app-builder-lib@24 dir: node_modules/.pnpm/app-builder-lib@24.13.3_dmg-builder@26.15.7_electron-builder-squirrel-windows@24.13.3
=== v24 macPackager notarize detection ===
21:const notarize_1 = require("@electron/notarize");
275: await this.notarizeIfProvided(appPath, options);
410: async notarizeIfProvided(appPath, buildOptions) {
411: const notarizeOptions = buildOptions.notarize;
412: if (notarizeOptions === false) {
413: builder_util_1.log.info({ reason: "`notarize` options were set explicitly `false`" }, "skipped macOS notarization");
416: const options = this.getNotarizeOptions(appPath);
418: builder_util_1.log.warn({ reason: "`notarize` options were unable to be generated" }, "skipped macOS notarization");
421: await (0, notarize_1.notarize)(options);
424: getNotarizeOptions(appPath) {
438: const appleApiKey = process.env.APPLE_API_KEY;
439: const appleApiKeyId = process.env.APPLE_API_KEY_ID;
440: ...
Confirmed: v24 (app-builder-lib@24.13.3) ALSO had internal env-var-triggered auto-notarize (notarizeIfProvided line 410, getNotarizeOptions reading APPLE_API_KEY etc. line 438). So the behavior is equivalent between v24 and v26 for this project.
Key difference for double-notarize analysis:
require("@electron/notarize") at line 21 — this is a static require, resolving to the hoisted notarize@2.2.1 (from app-builder-lib@24's own deps). So on main, electron-builder's INTERNAL notarize used 2.2.1, while the afterSign hook (scripts/notarize.cjs) used 2.5.0.dynamicImport("@electron/notarize") → notarize@2.5.0.So there ARE two notarize invocations per build (afterSign hook + internal), and this was already true on main. Let me confirm this double-notarize is pre-existing and not a regression. Let me check run 29828172734 log for how many "notarization" messages appeared, and confirm afterSign timing vs internal.
=== v24: does notarizeIfProvided also log 'notarization successful'? ===
async notarizeIfProvided(appPath, buildOptions) {
const notarizeOptions = buildOptions.notarize;
if (notarizeOptions === false) {
builder_util_1.log.info({ reason: "`notarize` options were set explicitly `false`" }, "skipped macOS notarization");
return;
}
const options = this.getNotarizeOptions(appPath);
if (!options) {
builder_util_1.log.warn({ reason: "`notarize` options were unable to be generated" }, "skipped macOS notarization");
return;
}
await (0, notarize_1.notarize)(options);
builder_util_1.log.info(null, "notarization successful");
}
getNotarizeOptions(appPath) {
const appleId = process.env.APPLE_ID;
=== v24 afterSign hook invocation ===
125: await this.info.afterPack(packContext);
126: await this.doSignAfterPack(outDir, appOutDir, ...
Now I have a complete picture. Let me verify the double-notarize concern by checking how afterSign relates to signAndPack and whether both actually run. Let me look at the signApp flow in both versions to see if afterSign hook + internal notarize both fire.
=== v24 signApp: afterSign + notarizeIfProvided ordering (lines 250-290) ===
strictVerify: options.strictVerify,
preAutoEntitlements: options.preAutoEntitlements,
optionsForFile: await this.getOptionsForFile(appPath, isMas, customSignOptions),
provisioningProfile: customSignOptions.provisioningProfile || undefined,
};
await this.doSign(signOptions, customSignOptions);
// https://github.com/electron-userland/electron-builder/issues/1196#issuecomment-312310209
if (masOptions != null && !isDevelopment) {
const certType = isDevelopment ? "Mac Developer" : "3rd Party Mac Developer Installer";
const masInstallerIdentity = await (0, macCodeSign_1.findIdentity)(certType, masOptions.identity, keychainFile);
if (masInstallerIdentity == null) {
throw new builder_util_1.InvalidConfigurationError(`Cannot find valid "${certType}" identity to sign MAS installer, please ...
This confirms both versions run afterSign hook (via platformPackager) AND internal notarizeIfProvided. So the double-notarize path was ALREADY present on main — not a regression introduced by this PR. In practice Apple's notarization is idempotent (re-submitting an already-notarized app just re-staples), and the successful run on main proves it works.
Now let me verify the remaining concerns: peer deps / unmet peers, the risky silent version changes (builder-util, dmg-builder, universal), and check for any install warnings. Let me update todos and run the remaining checks.
[
{
"content": "Read the PR diff (package.json)",
"status": "completed",
"priority": "high"
},
{
"content": "Verify app-builder-lib@26 resolves notarize 2.5.0 / osx-sign 1.3.3",
"status": "completed",
"priority": "high"
},
{
"content": "Trace stale 24.13.3 / notarize 2.2.1 / osx-sign 1.0.5 consumers",
"status": "completed",
"priority": "high"
},
{
"content": "Confirm osx-sign 1.3.3 CJS-safety in node_modules",
"status": "completed",
"priority": "high"
},
{
"content": "Check engines.node vs repo/CI Node",
"status": "in_progress",
"priority": "high"
},
{
"content": "Check peerDep conflicts (squirrel-windows, electron peer)",
"status": "pending",
"priority": "medium"
},
{
"content": "Scan for silent risky version changes (builder-util, dmg-builder, universal, plist)",
"status": "pending",
"priority": "medium"
},
{
"content": "Verify electron-builder.cjs config + notarize ...
=== root engines / node config ===
75: "packageManager": "pnpm@9.15.9",
76: "engines": {
77: "node": ">=20"
79: "volta": {
80: "node": "24.11.1",
--- .nvmrc ---
(no .nvmrc)
--- spotlight engines ---
141: "engines": {
142- "node": ">=20"
143- }
144-}
=== builder-util / dmg-builder / universal / plist engines ===
builder-util@26.15.3 engines:
dmg-builder@26.15.7 engines:
@electron+universal@2.0.3 engines:
builder-util 26.15.3 engines: {"node":">=14.0.0"}
dmg-builder 26.15.7 engines: "none"
@electron/universal 2.0.3 engines: {"node":">=16.4"}
@electron/notarize 2.5.0 engines: {"node":">= 10.0.0"}
@electron/osx-sign 1.3.3 engines: {"node":">=12.0.0"}
=== @electron/universal@2 CJS check (osx-sign-adjacent, used in signing) ===
main: dist/cjs/index.js type: undefined exports: undefined
All engines are satisfied by the repo's Node (volta 24.11.1, engines >=20, CI macos runner Node 24):
node >=14.0.0node >=14.0.0node >=14.0.0node >=16.4 (CJS-safe: main: dist/cjs/index.js, no exports map)node >=10, osx-sign@1.3.3: node >=12Concern #4 is NOT a problem — the earlier worry that v26 needs node>=22.12 was about electron-builder v27-alpha (which bundles app-builder-lib@27 with node>=22.12, per lore [d:33db8d]). v26.15.7 stays at node>=14. Now let me check for peer dependency issues and the electron peer requirement.
=== electron-builder@26.15.7 peerDeps ===
peerDeps: "none"
peerDepsMeta: "none"
=== app-builder-lib@26 peerDeps ===
peerDeps: {"dmg-builder":"26.15.7","electron-builder-squirrel-windows":"26.15.7"}
peerDepsMeta: "none"
=== lockfile peer decls for these ===
3103- app-builder-lib@26.15.7:
3104- resolution: {integrity: sha512-C7APoYISPExUmrEntNhDpz9Tccb4uWuEDfLaC0WPPc7/pwzz0WZGznCz/ycPfkkzw6tKOalceD8g6TgHmVz1QA==}
3105- engines: {node: '>=14.0.0'}
3106: peerDependencies:
3107- dmg-builder: 26.15.7
3108- electron-builder-squirrel-windows: 26.15.7
3109-
This is the key peer-dependency finding. app-builder-lib@26.15.7 declares peer deps:
dmg-builder: 26.15.7 → satisfied (dmg-builder@26.15.7 resolved ✓)electron-builder-squirrel-windows: 26.15.7 → but the lockfile resolved electron-builder-squirrel-windows@24.13.3 (a MISMATCH — 24.13.3 vs required 26.15.7).This is why the stale 24.13.3 chain exists. Let me investigate WHY pnpm resolved squirrel-windows@24.13.3 instead of 26.15.7, and whether it's a real problem. Note electron-builder@26.15.7 itself has no peer deps and doesn't directly depend on squirrel-windows — so how did 24.13.3 get pulled? Let me check the importers block at line 3096/3101/3108.
<path>/home/byk/Code/getsentry/spotlight/pnpm-lock.yaml</path>
<type>file</type>
<content>
3096: app-builder-lib@24.13.3:
3097: resolution: {integrity: sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==}
3098: engines: {node: '>=14.0.0'}
3099: peerDependencies:
3100: dmg-builder: 24.13.3
3101: electron-builder-squirrel-windows: 24.13.3
3102:
3103: app-builder-lib@26.15.7:
3104: resolution: {integrity: sha512-C7APoYISPExUmrEntNhDpz9Tccb4uWuEDfLaC0WPPc7/pwzz0WZGznCz/ycPfkkzw6tKOalceD8g6TgHmVz1QA==}
3105: engines: {node: '>=14.0.0'}
3106: peerDependencies:
3107: dmg-builder: 26.15.7
3108: electron-builder-squirrel-windows: 26.15.7
3109:
3110: archiver-utils@2.1.0:
3111: resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==}
3112: engines: {node: '>= 6'}
3113:
3114: archiver-utils@3.0.4:
3115: resolution: ...
=== who requires electron-builder-squirrel-windows? full lockfile scan === 3101: electron-builder-squirrel-windows: 24.13.3 3108: electron-builder-squirrel-windows: 26.15.7 10345: electron-builder-squirrel-windows: 24.13.3(dmg-builder@26.15.7) 10388: electron-builder-squirrel-windows: 24.13.3(dmg-builder@26.15.7) === is squirrel-windows a direct dep anywhere in repo? === NOT a direct dep in any package.json
Now I understand the full picture. electron-builder-squirrel-windows is an optional peer — it's NOT a direct dep, and pnpm only resolved @24.13.3 as a stub to satisfy the peer marker. Let me verify it's actually declared as optional in electron-builder's peerDependenciesMeta and whether it's even physically installed. This determines whether the 24.13.3 chain is truly inert.
=== search snapshots: what references app-builder-lib@24.13.3? (consumers) ===
3096: app-builder-lib@24.13.3:
10329: app-builder-lib@24.13.3(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3):
11281: app-builder-lib: 24.13.3(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3)
=== electron-builder-squirrel-windows@24.13.3 snapshot deps ===
electron-builder-squirrel-windows@24.13.3(dmg-builder@26.15.7):
dependencies:
app-builder-lib: 24.13.3(dmg-builder@26.15.7)(electron-builder-squirrel-windows@24.13.3)
archiver: 5.3.2
builder-util: 24.13.1
fs-extra: 10.1.0
transitivePeerDependencies:
- dmg-builder
- supports-color
=== Is squirrel-windows in electron-builder's package.json as optional peer? ===
deps: ...
Now it's fully clear. The dependency graph:
electron-builder@26.15.7 deps: app-builder-lib@26.15.7, dmg-builder@26.15.7 (no squirrel-windows).app-builder-lib@26.15.7 has peer electron-builder-squirrel-windows: 26.15.7 — but pnpm resolved it to a 24.13.3 stub (peer not satisfied by 26).electron-builder-squirrel-windows@24.13.3 (which self-references app-builder-lib@24.13.3).The important supply-chain conclusion: squirrel-windows is a Windows-only target and this project only builds mac (via electron-builder.cjs → builder.build({ config: { mac } })). The 24.13.3 chain (with notarize 2.2.1 / osx-sign 1.0.5) is only reachable IF squirrel-windows is invoked, which never happens on the macOS build. The macOS path goes exclusively through electron-builder@26.15.7 → app-builder-lib@26.15.7 → notarize 2.5.0 / osx-sign 1.3.3, proven by require.resolve above.
Let me do one final check — confirm there's a stale peer-mismatch warning and verify this exact situation existed on main too (to rule out regression), plus check what main's lockfile had. Let me diff the lockfile's app-builder-lib importer resolution against main.
=== main's electron-builder resolution (spotlight importer) === 270- specifier: ^24.13.3 271- version: 24.13.3(electron-builder-squirrel-windows@24.13.3) --- main importer line --- 270: specifier: ^24.13.3 271- version: 24.13.3(electron-builder-squirrel-windows@24.13.3) === did main ALSO have squirrel-windows@24.13.3 peer situation? === 11 (count of squirrel-windows@24.13.3 refs on main) === main: app-builder-lib versions present === 3045: app-builder-lib@24.13.3: 10104: app-builder-lib@24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3):
Confirmed: on main, electron-builder@24.13.3 pulled app-builder-lib@24.13.3 with the SAME squirrel-windows@24.13.3 peer situation. On main there was ONLY the 24.13.3 chain (macOS path used notarize@2.2.1 internally).
After the bump: the macOS path now uses app-builder-lib@26.15.7 → notarize@2.5.0. The residual 24.13.3 chain is only for the squirrel-windows peer stub (unused Windows target). This is a net improvement, not a regression.
Let me do a final verification: confirm no unmet/broken peer would fail pnpm install --frozen-lockfile in CI (the real gate), and check the CI build.yml Node version for the electron-mac job.
=== build.yml electron-mac job node setup ===
30: runs-on: ubuntu-latest
49: uses: actions/setup-node@v6
51: node-version-file: "package.json"
178: runs-on: ubuntu-latest
197: uses: actions/setup-node@v6
199: node-version: ${{ matrix.node_version }}
224: runs-on: ubuntu-latest
235: uses: actions/setup-node@v6
237: node-version: ${{ matrix.node_version }}
259: runs-on: ubuntu-latest
270: uses: actions/setup-node@v6
272: node-version: 24
309: runs-on: ubuntu-latest
356: electron-mac:
359: runs-on: macos-latest
370: uses: actions/setup-node@v6
372: node-version-file: "package.json"
=== frozen-lockfile validation (dry, no writes) === Scope: all 3 workspace projects Done in 923ms using pnpm v9.15.9
Both final checks pass:
electron-mac job (line 356-372) runs on macos-latest with node-version-file: "package.json" → uses volta's 24.11.1. Node 24 satisfies all engine requirements (max is universal's >=16.4).pnpm install --frozen-lockfile will pass.Let me do the last read-only check on the volta node version used and confirm the package.json node-version-file resolution (volta node vs engines).
=== node-version-file resolution: package.json has both volta.node and engines.node === volta.node: 24.11.1 engines.node: >=20 note: actions/setup-node with node-version-file reads volta.node (24.11.1) if present, else engines
All checks complete. Let me finalize the todos and deliver the review.
[
{
"content": "Read the PR diff (package.json)",
"status": "completed",
"priority": "high"
},
{
"content": "Verify app-builder-lib@26 resolves notarize 2.5.0 / osx-sign 1.3.3",
"status": "completed",
"priority": "high"
},
{
"content": "Trace stale 24.13.3 / notarize 2.2.1 / osx-sign 1.0.5 consumers",
"status": "completed",
"priority": "high"
},
{
"content": "Confirm osx-sign 1.3.3 CJS-safety in node_modules",
"status": "completed",
"priority": "high"
},
{
"content": "Check engines.node vs repo/CI Node",
"status": "completed",
"priority": "high"
},
{
"content": "Check peerDep conflicts (squirrel-windows, electron peer)",
"status": "completed",
"priority": "medium"
},
{
"content": "Scan for silent risky version changes (builder-util, dmg-builder, universal, plist)",
"status": "completed",
"priority": "medium"
},
{
"content": "Verify electron-builder.cjs config + ...
Repo /home/byk/Code/getsentry/spotlight, branch chore/electron-builder-26, base main.
L1. app-builder-lib@26 peer electron-builder-squirrel-windows: 26.15.7 is unsatisfied (resolved to 24.13.3 stub).
pnpm-lock.yaml:3106-3108 declares the peer as 26.15.7, but resolution pinned electron-builder-squirrel-windows@24.13.3 (pnpm-lock.yaml:271, 10388). This is a version-mismatched peer, but benign here:
mac (electron-builder.cjs → builder.build({ config: { mac } })).grep returned "NOT a direct dep"), and electron-builder@26 has no optionalDependencies and no peerDeps of its own — squirrel-windows only exists as a peer stub app-builder-lib declares.pnpm install --frozen-lockfile --lockfile-only completes clean ("Done in 923ms"), so CI's install gate passes.main with the 24.x tree (git show origin/main:pnpm-lock.yaml → 11 refs to squirrel-windows@24.13.3). Not a regression.L2. Cosmetic reformat bundled in a dependency PR. package.json files: ["dist"] was collapsed to one line (diff lines 35-39). Harmless, ignorable.
#1 — macOS path resolves notarize 2.5.0 / osx-sign 1.3.3: CONFIRMED.
app-builder-lib@26.15.7 block (pnpm-lock.yaml:10368-10369) → @electron/notarize: 2.5.0, @electron/osx-sign: 1.3.3. Installed proof: readlink -f node_modules/.pnpm/electron-builder@26.15.7*/node_modules/app-builder-lib → app-builder-lib@26.15.7; its scoped symlinks point to @electron+notarize@2.5.0 and @electron+osx-sign@1.3.3. The pins in app-builder-lib@26's package.json are exact (notarize: 2.5.0, osx-sign: 1.3.3).
**#2 — Stale ...
<observations> Date: Jul 21, 2026 * 🟡 (12:34) Root package.json engines: node >=20 (line 77); volta.node pinned to 24.11.1 (line 80); packageManager pnpm@9.15.9 (line 75). No .nvmrc present. Spotlight package engines also node >=20 (lines 141-144). * 🟡 (12:35) Engine requirements confirmed: builder-util@26.15.3 needs node >=14.0.0, dmg-builder@26.15.7 has no engines restriction, @electron/unive…
<observations> Date: Jul 21, 2026 * 🟡 (12:29) Assistant began PR correctness review with 8-item todo list: 1. Read PR diff (package.json) [high], 2. Verify app-builder-lib@26 resolves notarize 2.5.0 / osx-sign 1.3.3 [high], 3. Trace stale 24.13.3 / notarize 2.2.1 / osx-sign 1.0.5 consumers [high], 4. Confirm osx-sign 1.3.3 CJS-safety in node_modules [high], 5. Check engines.node vs repo/CI Node …
<observations> Date: Jul 21, 2026 * 🔴 (12:28) User requested a rigorous, skeptical adversarial CORRECTNESS review of a dependency-bump PR, explicitly scoped to real bugs/supply-chain/resolution correctness, NOT style/cosmetics. [requested-review] * 🔴 (12:28) User stated repo path: /home/byk/Code/getsentry/spotlight, branch chore/electron-builder-26 (PR #1335), base branch main. * 🔴 (12:28) Use…