CSC_LINK is electron-builder cross-platform, not mac-only: WinPackager falls back to it when WIN_CSC_LINK is unset. Setting it unconditionally handed the Apple Developer ID cert to the windows leg, which wrote its subject CN into app-update.yml as publisherName. Every Windows client then rejected the update it downloaded because the installer carries no signature Windows trusts. Scope CSC_LINK/CSC_KEY_PASSWORD to the mac legs, and set win.verifyUpdateCodeSignature so the updater manifest stops claiming a publisher the build cannot back up.
Unified · Split
| @@ -155,8 +155,22 @@ jobs: · 154 unmodified lines | ||
| 155 | 155 | # → notarytool authentication for the notarization upload |
| 156 | 156 | # When the secrets aren't set (forks, local), electron-builder |
| 157 | 157 | # silently produces an unsigned build. |
| 158 | − CSC_LINK: ${{ secrets.CSC_LINK }} | |
| 159 | − CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} | |
| 158 | + # | |
| 159 | + # CSC_LINK / CSC_KEY_PASSWORD MUST stay scoped to the mac legs. | |
| 160 | + # Despite the name used here, CSC_LINK is electron-builder's | |
| 161 | + # cross-platform certificate variable: WinPackager falls back to it | |
| 162 | + # whenever WIN_CSC_LINK is unset, via | |
| 163 | + # platformPackager.getCscLink("WIN_CSC_LINK"). Handing the Apple | |
| 164 | + # Developer ID .p12 to the windows leg made electron-builder read its | |
| 165 | + # subject and write `publisherName: Developer ID Application: ...` | |
| 166 | + # into app-update.yml, so every Windows client rejected the update it | |
| 167 | + # had just downloaded with "New version is not signed by the | |
| 168 | + # application owner". Empty string is the documented "no certificate" | |
| 169 | + # value — WindowsSignToolManager treats `cscLink === ""` exactly like | |
| 170 | + # unset. The APPLE_* vars below need no such guard: they are only | |
| 171 | + # read by notarytool on darwin and are inert elsewhere. | |
| 172 | + CSC_LINK: ${{ matrix.platform == 'mac' && secrets.CSC_LINK || '' }} | |
| 173 | + CSC_KEY_PASSWORD: ${{ matrix.platform == 'mac' && secrets.CSC_KEY_PASSWORD || '' }} | |
| 160 | 174 | APPLE_API_KEY: ${{ env.APPLE_API_KEY_PATH }} |
| 161 | 175 | APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} |
| 162 | 176 | APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} |
| @@ -46,6 +46,27 @@ const config: Configuration = { · 45 unmodified lines | ||
| 46 | 46 | // artifact only exists once a leg stages an arm64 executor for it. |
| 47 | 47 | win: { |
| 48 | 48 | target: ["nsis"], |
| 49 | + // There is no Windows code-signing certificate yet, so these installers | |
| 50 | + // ship unsigned, and this flag records that fact for the updater. | |
| 51 | + // | |
| 52 | + // Left at its `true` default, electron-builder writes a `publisherName` | |
| 53 | + // into app-update.yml derived from the signing certificate's subject CN | |
| 54 | + // (PublishManager.getAppUpdatePublishConfiguration, gated on | |
| 55 | + // WinPackager.isForceCodeSigningVerification). electron-updater then | |
| 56 | + // requires every downloaded installer to carry an Authenticode signature | |
| 57 | + // matching that name and refuses the update otherwise | |
| 58 | + // (NsisUpdater.verifySignature). An unsigned build that still claims a | |
| 59 | + // publisher can therefore never update itself. | |
| 60 | + // | |
| 61 | + // With this false no `publisherName` is emitted, `verifySignature` | |
| 62 | + // returns null early, and unsigned installers update as intended. It does | |
| 63 | + // not disable signing: `isForceCodeSigningVerification` is only ever read | |
| 64 | + // when computing the updater manifest. | |
| 65 | + // | |
| 66 | + // Delete this line when a real Windows certificate lands (signtoolOptions | |
| 67 | + // or azureSignOptions); leaving it would keep verification off for a | |
| 68 | + // signed build. | |
| 69 | + verifyUpdateCodeSignature: false, | |
| 49 | 70 | }, |
| 50 | 71 | nsis: { |
| 51 | 72 | oneClick: true, |