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 | # → notarytool authentication for the notarization upload | 155 | # → notarytool authentication for the notarization upload |
| 156 | # When the secrets aren't set (forks, local), electron-builder | 156 | # When the secrets aren't set (forks, local), electron-builder |
| 157 | # silently produces an unsigned build. | 157 | # silently produces an unsigned build. |
| 158 | CSC_LINK: ${{ secrets.CSC_LINK }} | 158 | # |
| 159 | CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} | 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 | APPLE_API_KEY: ${{ env.APPLE_API_KEY_PATH }} | 174 | APPLE_API_KEY: ${{ env.APPLE_API_KEY_PATH }} |
| 161 | APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} | 175 | APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} |
| 162 | APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} | 176 | APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} |
| @@ -46,6 +46,27 @@ const config: Configuration = { · 45 unmodified lines | |||
| 46 | // artifact only exists once a leg stages an arm64 executor for it. | 46 | // artifact only exists once a leg stages an arm64 executor for it. |
| 47 | win: { | 47 | win: { |
| 48 | target: ["nsis"], | 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 | nsis: { | 71 | nsis: { |
| 51 | oneClick: true, | 72 | oneClick: true, |