diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml index 33f843af..3fb05515 100644 --- a/.github/workflows/release-desktop.yml +++ b/.github/workflows/release-desktop.yml @@ -416,13 +416,25 @@ jobs: CSC_IDENTITY_AUTO_DISCOVERY: 'false' run: node ./node_modules/electron-builder/out/cli/cli.js ${{ matrix.builder_args }} --publish never - - name: Build unsigned Windows application directory for SignPath + # A real NSIS target is intentional here. electron-builder writes + # resources/app-update.yml only while packaging an updater-capable target; + # `--win dir` and the later `--prepackaged` pass both skip that hook. + - name: Build unsigned Windows bootstrap installer for SignPath if: matrix.smoke_platform == 'windows' && needs.signing-preflight.outputs.windows_signed == 'true' working-directory: desktop env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} CSC_IDENTITY_AUTO_DISCOVERY: 'false' - run: node ./node_modules/electron-builder/out/cli/cli.js --win dir ${{ matrix.builder_arch_arg }} --publish never + run: node ./node_modules/electron-builder/out/cli/cli.js --win nsis ${{ matrix.builder_arch_arg }} --publish never + + - name: Verify Windows updater config before SignPath + if: matrix.smoke_platform == 'windows' && needs.signing-preflight.outputs.windows_signed == 'true' + shell: pwsh + run: | + $updaterConfig = Join-Path $PWD "desktop/build-artifacts/electron/${{ matrix.unpacked_dir }}/resources/app-update.yml" + if (-not (Test-Path -LiteralPath $updaterConfig -PathType Leaf)) { + throw "electron-builder did not create the Windows updater config: $updaterConfig" + } - name: Stage project-owned Windows application executables if: matrix.smoke_platform == 'windows' && needs.signing-preflight.outputs.windows_signed == 'true' diff --git a/scripts/pr/release-workflow.test.ts b/scripts/pr/release-workflow.test.ts index 84006fd9..3d442820 100644 --- a/scripts/pr/release-workflow.test.ts +++ b/scripts/pr/release-workflow.test.ts @@ -317,7 +317,8 @@ describe('release desktop workflow', () => { const workflow = readReleaseWorkflow() const applicationConfiguration = readFileSync('.github/signpath/windows-application.xml', 'utf8') const installerConfiguration = readFileSync('.github/signpath/windows-installer.xml', 'utf8') - const applicationBuildStep = extractStep(workflow, 'Build unsigned Windows application directory for SignPath') + const applicationBuildStep = extractStep(workflow, 'Build unsigned Windows bootstrap installer for SignPath') + const verifyUpdaterConfigStep = extractStep(workflow, 'Verify Windows updater config before SignPath') const stageApplicationStep = extractStep(workflow, 'Stage project-owned Windows application executables') const signApplicationStep = extractStep(workflow, 'Sign Windows application executables with SignPath') const restoreApplicationStep = extractStep(workflow, 'Restore and verify signed Windows application executables') @@ -331,8 +332,16 @@ describe('release desktop workflow', () => { expect(workflow).toContain('builder_arch_arg: --arm64') expect(workflow).toContain('unpacked_dir: win-unpacked') expect(workflow).toContain('unpacked_dir: win-arm64-unpacked') - expect(applicationBuildStep).toContain('--win dir ${{ matrix.builder_arch_arg }}') + expect(applicationBuildStep).toContain('--win nsis ${{ matrix.builder_arch_arg }}') expect(applicationBuildStep).toContain("CSC_IDENTITY_AUTO_DISCOVERY: 'false'") + expect(verifyUpdaterConfigStep).toContain('${{ matrix.unpacked_dir }}/resources/app-update.yml') + expect(verifyUpdaterConfigStep).toContain('Test-Path -LiteralPath') + expect(workflow.indexOf('Build unsigned Windows bootstrap installer for SignPath')).toBeLessThan( + workflow.indexOf('Verify Windows updater config before SignPath'), + ) + expect(workflow.indexOf('Verify Windows updater config before SignPath')).toBeLessThan( + workflow.indexOf('Stage project-owned Windows application executables'), + ) expect(stageApplicationStep).toContain('Claude Code Haha.exe') expect(stageApplicationStep).toContain('claude-sidecar-${{ matrix.target_triple }}.exe') expect(stageApplicationStep).not.toContain('rg.exe')