Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
30 changes: 15 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions scripts/package-mac.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<repo> 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
Expand Down