diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 49afc5405..a7dae52cf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -161,11 +161,24 @@ jobs: - name: Build app run: npm run build + - name: Archive app build + # WHY a tar instead of uploading `out/` directly: upload-artifact zips its + # input without file modes, so every file comes back 644 (its README, + # "Permission Loss"). `out/` carries executables that must stay + # executable inside the packaged app: the universal dictation hotkey + # helper from scripts/build-hotkey-helper.mjs and the bundled tmux, + # cloudflared and mitmproxy binaries under out/main/runtime. The + # package-macos job packages the downloaded tree verbatim, so release run + # 34739982565 shipped a non-executable helper and + # scripts/verify-packaged-mac.mjs rejected it (#965). tar records modes + # and symlinks, so the zip wrapped around the tar no longer matters. + run: tar -cf app-build.tar out + - name: Upload app build uses: actions/upload-artifact@v7 with: name: app-build - path: out + path: app-build.tar if-no-files-found: error package-macos: @@ -255,7 +268,11 @@ jobs: uses: actions/download-artifact@v8 with: name: app-build - path: out + + - name: Unpack app build + # Restores `out/` with the file modes recorded in build-app; see + # "Archive app build" there for why the tree travels as a tar. + run: tar -xf app-build.tar && rm app-build.tar - name: Package macOS artifacts env: diff --git a/package-lock.json b/package-lock.json index d6029f68d..4d2cc70af 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1301,9 +1301,9 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.14", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", - "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "version": "1.19.17", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.17.tgz", + "integrity": "sha512-dSneS5qhiauZWGDCeK4o695Xd9nUNjviSZCMQrj10eetr8Uln1ucn6bbphOM6UynAMMtNIzZNSpL9vnASJwrPQ==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -6163,9 +6163,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", "funding": [ { "type": "github", @@ -6699,9 +6699,9 @@ } }, "node_modules/hono": { - "version": "4.12.31", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.31.tgz", - "integrity": "sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==", + "version": "4.13.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.7.tgz", + "integrity": "sha512-c8/gF9ac8Y78/agExVocyLevgR+JlpNB444Py0FSX8pJoPdYUfUzRcXtYEYGwt6l19qIlVZPN5Mfsw9jFShmQQ==", "license": "MIT", "engines": { "node": ">=16.9.0" @@ -6861,9 +6861,9 @@ "license": "MIT" }, "node_modules/ip-address": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", - "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", + "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", "license": "MIT", "engines": { "node": ">= 12" @@ -9390,9 +9390,9 @@ } }, "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", diff --git a/scripts/package-mac.mjs b/scripts/package-mac.mjs index b7d3d0972..bbc37a0f1 100644 --- a/scripts/package-mac.mjs +++ b/scripts/package-mac.mjs @@ -34,6 +34,22 @@ import { spawnSync } from 'node:child_process' const env = { ...process.env } + +// WHY empty signing vars are deleted, not just treated as falsy: GitHub +// Actions materialises `${{ secrets.CSC_LINK }}` as an EMPTY STRING when the +// secret does not exist, and release.yml passes every signing secret that way. +// electron-builder deliberately treats "" as a set value +// (platformPackager.getCscLink: chooseNotNull + "allow to specify as empty +// string"), so an empty CSC_LINK is resolved as a certificate PATH relative to +// the project root and packaging dies with " not a file" (#965, release +// run 34739476044). The `!env.CSC_LINK` test below already chose the unsigned +// branch correctly; the empty variable simply survived into the child env. +// Normalising first makes "secret missing" and "variable unset" identical for +// everything downstream, including the notarization vars. +for (const name of ['CSC_LINK', 'CSC_NAME', 'CSC_KEY_PASSWORD']) { + if (env[name] !== undefined && env[name].trim() === '') delete env[name] +} + if (!env.CSC_LINK && !env.CSC_NAME) { env.CSC_IDENTITY_AUTO_DISCOVERY = 'false' delete env.CSC_KEY_PASSWORD