ci(release): prevent signing on workflow reruns (#17802)

This commit is contained in:
Jinwoo Hong
2026-09-01 01:05:00 -04:00
committed by GitHub
parent 05b31d6e92
commit e2f326cad7
2 changed files with 61 additions and 23 deletions
+32 -22
View File
@@ -1193,12 +1193,22 @@ jobs:
with:
ref: refs/tags/${{ needs.cut.outputs.tag }}
# GitHub reruns also resume jobs skipped behind a failed gate. Never
# recreate Windows signing requests on a rerun; reuse the assets from the
# original attempt and require a fresh dispatch if they are missing.
- name: Skip Windows artifact rebuild on rerun
if: matrix.platform == 'win' && github.run_attempt != 1
shell: bash
run: |
echo "Windows artifact/signing steps are disabled on reruns (attempt $GITHUB_RUN_ATTEMPT)."
echo "Existing signed release assets must be reused; dispatch a fresh release only when a rebuild is required." >> "$GITHUB_STEP_SUMMARY"
# Why: `uses: ./…` resolves from the checked-out tag, not from the workflow
# ref, so cutting from an older/off-main ref whose tree predates a composite
# action would fail the step with "Can't find 'action.yml'". Restore the
# actions directory from the commit this workflow file itself came from.
- name: Restore composite actions from the workflow ref
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: bash
env:
WORKFLOW_SHA: ${{ github.workflow_sha }}
@@ -1405,7 +1415,7 @@ jobs:
# Why: SignPath signs GitHub workflow artifacts, so Windows builds must
# upload only after the production-signed installer has been returned.
- name: Build Windows release artifacts
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
uses: nick-fields/retry@v4
with:
timeout_minutes: 30
@@ -1416,7 +1426,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Verify Windows node-pty ConPTY runtime
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
run: |
$runtimeDir = 'dist/win-unpacked/resources/node_modules/node-pty/build/Release'
@@ -1433,7 +1443,7 @@ jobs:
}
- name: Install SignPath PowerShell module
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
uses: ./.github/actions/install-signpath-module
# ── Windows inner-binary signing (issue #7785) ─────────────────────
@@ -1450,7 +1460,7 @@ jobs:
# valid signature (Microsoft's OpenConsole.exe) must keep their signer.
- name: Stage unsigned inner PE files for signing
id: stage-inner
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
continue-on-error: true
shell: pwsh
run: |
@@ -1489,7 +1499,7 @@ jobs:
- name: Upload unsigned inner binaries for SignPath
id: upload-unsigned-inner
if: matrix.platform == 'win' && steps.stage-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.stage-inner.outcome == 'success'
continue-on-error: true
uses: actions/upload-artifact@v7
with:
@@ -1499,7 +1509,7 @@ jobs:
- name: Submit inner binaries signing request
id: submit-inner-signing
if: matrix.platform == 'win' && steps.upload-unsigned-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.upload-unsigned-inner.outcome == 'success'
continue-on-error: true
uses: signpath/github-action-submit-signing-request@v2
with:
@@ -1513,7 +1523,7 @@ jobs:
- name: Notify Slack that inner-binary signing is waiting for approval
id: notify-inner-signing
if: matrix.platform == 'win' && steps.submit-inner-signing.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.submit-inner-signing.outcome == 'success'
continue-on-error: true
shell: pwsh
env:
@@ -1581,7 +1591,7 @@ jobs:
# falls through to today's unsigned-inner flow rather than blocking.
- name: Download signed inner binaries from SignPath
id: download-signed-inner
if: matrix.platform == 'win' && steps.submit-inner-signing.outcome == 'success' && steps.notify-inner-signing.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.submit-inner-signing.outcome == 'success' && steps.notify-inner-signing.outcome == 'success'
continue-on-error: true
shell: pwsh
env:
@@ -1604,7 +1614,7 @@ jobs:
# shipping a mix of signed and unsigned binaries.
- name: Restore signed inner binaries into unpacked app
id: restore-signed-inner
if: matrix.platform == 'win' && steps.download-signed-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.download-signed-inner.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
@@ -1645,7 +1655,7 @@ jobs:
# unsigned again, which the evidence gate will flag.
- name: Replace cached elevate.exe with the signed copy
id: sign-elevate-cache
if: matrix.platform == 'win' && steps.restore-signed-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.restore-signed-inner.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
@@ -1672,7 +1682,7 @@ jobs:
- name: Rebuild NSIS installer from signed unpacked app
id: rebuild-nsis-signed
if: matrix.platform == 'win' && steps.restore-signed-inner.outcome == 'success'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.restore-signed-inner.outcome == 'success'
continue-on-error: true
shell: pwsh
run: |
@@ -1689,7 +1699,7 @@ jobs:
}
- name: Roll back to original installer after failed rebuild
if: matrix.platform == 'win' && steps.rebuild-nsis-signed.outcome == 'failure'
if: matrix.platform == 'win' && github.run_attempt == 1 && steps.rebuild-nsis-signed.outcome == 'failure'
shell: pwsh
run: |
if (Test-Path 'prepack-backup/orca-windows-setup.exe') {
@@ -1699,7 +1709,7 @@ jobs:
}
# ── End Windows inner-binary signing ───────────────────────────────
- name: Upload unsigned Windows installer for SignPath
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
id: upload-unsigned-windows-installer
uses: actions/upload-artifact@v7
with:
@@ -1711,7 +1721,7 @@ jobs:
# so the release job waits while the signing request is approved in UI.
- name: Submit Windows installer signing request
id: submit-signing-request
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
uses: signpath/github-action-submit-signing-request@v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
@@ -1723,7 +1733,7 @@ jobs:
wait-for-completion: false
- name: Notify Slack that Windows signing is waiting for approval
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
@@ -1787,7 +1797,7 @@ jobs:
Invoke-RestMethod -Method Post -Uri $env:SLACK_WEBHOOK_URL -ContentType 'application/json' -Body $payload
- name: Download signed Windows installer from SignPath
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
env:
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
@@ -1805,7 +1815,7 @@ jobs:
Expand-Archive -Path signed-windows.zip -DestinationPath signed-windows -Force
- name: Stage signed Windows release assets
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
run: |
$signedInstaller = Get-ChildItem -Path signed-windows -Recurse -File -Filter 'orca-windows-setup.exe' | Select-Object -First 1
@@ -1842,7 +1852,7 @@ jobs:
Get-Item 'dist/orca-windows-setup.exe', 'dist/orca-windows-setup.exe.blockmap', 'dist/latest.yml'
- name: Verify signed Windows installer
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
run: |
$signature = Get-AuthenticodeSignature -FilePath 'dist/orca-windows-setup.exe'
@@ -1860,7 +1870,7 @@ jobs:
# proven on a real release, then flip ORCA_WINDOWS_INNER_SIGNATURE_REQUIRED
# to 'true' so unsigned inner binaries block the release.
- name: Verify Windows inner binary signatures
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
shell: pwsh
env:
ORCA_WINDOWS_INNER_SIGNATURE_REQUIRED: 'false'
@@ -1992,7 +2002,7 @@ jobs:
if ($policyFailure) { throw $policyFailure }
- name: Upload Windows inner signing evidence
if: always() && matrix.platform == 'win'
if: always() && matrix.platform == 'win' && github.run_attempt == 1
uses: actions/upload-artifact@v7
with:
name: orca-windows-inner-signing-evidence-${{ needs.cut.outputs.tag }}
@@ -2003,7 +2013,7 @@ jobs:
retention-days: 30
- name: Publish signed Windows release artifacts
if: matrix.platform == 'win'
if: matrix.platform == 'win' && github.run_attempt == 1
uses: nick-fields/retry@v4
with:
timeout_minutes: 10
@@ -38,7 +38,7 @@ describe('Windows signing workflow contract', () => {
const installStep = steps[installStepIndexes[0]]
expect(installStep.if).toBe("matrix.platform == 'win'")
expect(installStep.if).toBe("matrix.platform == 'win' && github.run_attempt == 1")
expect(installStep.uses).toBe('./.github/actions/install-signpath-module')
expect(installStep.run).toBeUndefined()
@@ -139,6 +139,34 @@ describe('Windows signing workflow contract', () => {
expect(installRun).toContain('throw "SHA-256 mismatch for $source')
})
it('never recreates Windows signing requests on a workflow rerun', () => {
const parsedWorkflow = readWorkflow('.github/workflows/release-cut.yml')
const steps = parsedWorkflow.jobs.build.steps
const stepNames = steps.map((step) => step.name)
const skipStep = steps.find((step) => step.name === 'Skip Windows artifact rebuild on rerun')
expect(skipStep?.if).toBe("matrix.platform == 'win' && github.run_attempt != 1")
expect(skipStep?.run).toContain('Existing signed release assets must be reused')
const signingStepNames = [
'Build Windows release artifacts',
'Stage unsigned inner PE files for signing',
'Upload unsigned inner binaries for SignPath',
'Submit inner binaries signing request',
'Download signed inner binaries from SignPath',
'Upload unsigned Windows installer for SignPath',
'Submit Windows installer signing request',
'Download signed Windows installer from SignPath',
'Stage signed Windows release assets',
'Publish signed Windows release artifacts'
]
for (const stepName of signingStepNames) {
const step = steps[stepNames.indexOf(stepName)]
expect(step?.if, stepName).toContain('github.run_attempt == 1')
}
})
it('shares one SignPath module install path between release and rehearsal', () => {
const rehearsalWorkflow = readWorkflow('.github/workflows/windows-signing-rehearsal.yml')
const stepNames = rehearsalWorkflow.jobs.rehearse.steps.map((step) => step.name)