Skip to content

Commit 50a68f1

Browse files
committed
fix(release): the macOS signing keychain is stood up where its password is known
electron-builder makes its own keychain and unlocks it with a password it generated. On the runner provisioner that landed 2026-08-28 that unlock fails — `security set-key-partition-list` reports the passphrase as wrong, three attempts, before anything is signed — while the same five secrets signed v0.61.0 fine forty minutes earlier on 20260707.563. Same image (macos-26-arm64), same code path, different provisioner. So the keychain is created here with a password this job chose, the .p12 is imported into it, and electron-builder is handed it through CSC_KEYCHAIN. It then signs out of a keychain that is already unlocked rather than making one. A missing identity signs nothing and says nothing, which would read as a successful unsigned release — so the step asks the keychain for a Developer ID Application identity and fails if it has none. CSC_LINK goes empty on BOTH legs now. It is electron-builder's platform-neutral variable, and the Windows leg seeing a certificate is what shipped v0.36.0 with an installer signed by an Apple identity that electron-updater then refused.
1 parent f10b44c commit 50a68f1

1 file changed

Lines changed: 39 additions & 12 deletions

File tree

‎.github/workflows/release-desktop.yml‎

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -360,23 +360,50 @@ jobs:
360360
- name: Build desktop renderer + main
361361
run: pnpm --filter ./desktop build
362362

363+
# electron-builder makes its own keychain and unlocks it with a password it
364+
# generated. On the runner provisioner that landed 2026-08-28 that unlock
365+
# fails — `set-key-partition-list` calls the passphrase wrong before anything
366+
# is signed — while the same secrets signed fine on 20260707.563. So the
367+
# keychain is stood up here, where its password is known, and handed over.
368+
- name: Stand up the signing keychain
369+
if: matrix.platform == 'macos-latest' && github.event_name == 'push'
370+
shell: bash
371+
env:
372+
MAC_CSC_LINK: ${{ secrets.MAC_CSC_LINK }}
373+
MAC_CSC_KEY_PASSWORD: ${{ secrets.MAC_CSC_KEY_PASSWORD }}
374+
run: |
375+
set -euo pipefail
376+
KEYCHAIN="$RUNNER_TEMP/estella-signing.keychain-db"
377+
PASSWORD="$(openssl rand -base64 24)"
378+
security create-keychain -p "$PASSWORD" "$KEYCHAIN"
379+
security set-keychain-settings -lut 21600 "$KEYCHAIN"
380+
security unlock-keychain -p "$PASSWORD" "$KEYCHAIN"
381+
printf '%s' "$MAC_CSC_LINK" | base64 --decode > "$RUNNER_TEMP/cert.p12"
382+
security import "$RUNNER_TEMP/cert.p12" -k "$KEYCHAIN" -P "$MAC_CSC_KEY_PASSWORD" \
383+
-T /usr/bin/codesign -T /usr/bin/security
384+
rm -f "$RUNNER_TEMP/cert.p12"
385+
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$PASSWORD" "$KEYCHAIN" >/dev/null
386+
security list-keychains -d user -s "$KEYCHAIN" $(security list-keychains -d user | tr -d '"')
387+
# An identity that is not there signs nothing and says nothing: without
388+
# this the build would ship UNSIGNED, which reads as a success.
389+
security find-identity -v -p codesigning "$KEYCHAIN" | grep -q 'Developer ID Application' \
390+
|| { echo '::error::the signing keychain holds no Developer ID Application identity'; exit 1; }
391+
echo "CSC_KEYCHAIN=$KEYCHAIN" >> "$GITHUB_ENV"
392+
363393
- name: Package and publish desktop app
364394
if: github.event_name == 'push'
365395
shell: bash
366396
env:
367397
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
368-
# macOS ONLY. CSC_LINK is electron-builder's platform-NEUTRAL certificate
369-
# variable: handed to the Windows leg it signs the NSIS installer with the
370-
# Apple Developer ID .p12 (windowsSignToolManager falls back to CSC_LINK
371-
# when WIN_CSC_LINK is unset), producing an installer whose chain Windows
372-
# cannot build to a trusted root — and stamping that identity into
373-
# app-update.yml as `publisherName`. electron-updater then demands an
374-
# Authenticode status of Valid before installing, so every Windows
375-
# auto-update is refused AFTER downloading the whole installer. An empty
376-
# value reads as absent (getCscLink treats "" as null), so the Windows leg
377-
# ships unsigned, which is what this project intends.
378-
CSC_LINK: ${{ matrix.platform == 'macos-latest' && secrets.MAC_CSC_LINK || '' }}
379-
CSC_KEY_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.MAC_CSC_KEY_PASSWORD || '' }}
398+
# Empty on BOTH legs. macOS signs out of the CSC_KEYCHAIN stood up above;
399+
# Windows must never see a certificate here — CSC_LINK is electron-builder's
400+
# PLATFORM-NEUTRAL variable, so the Windows leg would sign its NSIS
401+
# installer with the Apple Developer ID .p12, stamp that identity into
402+
# app-update.yml as `publisherName`, and electron-updater would then refuse
403+
# every update whose Authenticode status is not Valid. v0.36.0 shipped that
404+
# way. An empty value reads as absent (getCscLink treats "" as null).
405+
CSC_LINK: ''
406+
CSC_KEY_PASSWORD: ''
380407
# An app-specific password, NOT the Apple ID's own.
381408
APPLE_ID: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_ID || '' }}
382409
APPLE_APP_SPECIFIC_PASSWORD: ${{ matrix.platform == 'macos-latest' && secrets.APPLE_APP_SPECIFIC_PASSWORD || '' }}

0 commit comments

Comments
 (0)