From a3ef1226b7dde7bc661d787fcaddf7256d9fb6b2 Mon Sep 17 00:00:00 2001 From: rcourtman Date: Thu, 9 Jul 2026 23:44:22 +0100 Subject: [PATCH] Fail fast on missing native signing configuration --- .github/workflows/build-release-candidate.yml | 41 ++++++++++++++++++- .../subsystems/deployment-installability.md | 3 +- .../release_promotion_policy_test.py | 13 ++++++ 3 files changed, 54 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-release-candidate.yml b/.github/workflows/build-release-candidate.yml index 0f55d6a15..497609061 100644 --- a/.github/workflows/build-release-candidate.yml +++ b/.github/workflows/build-release-candidate.yml @@ -24,9 +24,45 @@ permissions: contents: read jobs: + signing-configuration: + name: Verify Native Signing Configuration + if: ${{ inputs.require_platform_signing }} + runs-on: ubuntu-24.04 + timeout-minutes: 2 + steps: + - name: Report missing signing secrets + env: + APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64 }} + APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD }} + APPLE_DEVELOPER_ID_APPLICATION_IDENTITY: ${{ secrets.APPLE_DEVELOPER_ID_APPLICATION_IDENTITY }} + APPLE_NOTARY_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_KEY_P8_BASE64 }} + APPLE_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }} + APPLE_NOTARY_ISSUER_ID: ${{ secrets.APPLE_NOTARY_ISSUER_ID }} + WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64: ${{ secrets.WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64 }} + WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD }} + run: | + set -euo pipefail + missing=0 + for name in \ + APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64 \ + APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD \ + APPLE_DEVELOPER_ID_APPLICATION_IDENTITY \ + APPLE_NOTARY_KEY_P8_BASE64 \ + APPLE_NOTARY_KEY_ID \ + APPLE_NOTARY_ISSUER_ID \ + WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64 \ + WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD; do + if [ -z "${!name:-}" ]; then + echo "::error::Missing required Actions secret ${name}." + missing=1 + fi + done + exit "$missing" + sign-macos-agent: name: Sign and Notarize macOS Agent - if: ${{ inputs.require_platform_signing }} + needs: signing-configuration + if: ${{ inputs.require_platform_signing && needs.signing-configuration.result == 'success' }} runs-on: macos-15 timeout-minutes: 30 steps: @@ -111,7 +147,8 @@ jobs: sign-windows-agent: name: Authenticode Sign Windows Agent - if: ${{ inputs.require_platform_signing }} + needs: signing-configuration + if: ${{ inputs.require_platform_signing && needs.signing-configuration.result == 'success' }} runs-on: windows-2025 timeout-minutes: 25 steps: diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index 13ddf14a8..c050b5328 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -341,7 +341,8 @@ TLS floor in the dynamic config. A manually dispatched release rehearsal must activate the same signed candidate build whenever its required `version` input is non-empty and must require the same macOS notarization and Windows Authenticode lanes as a - publish run. + publish run. A cheap signing-configuration job must report every missing + repository secret before either platform runner is allocated. Scheduled watchdog rehearsals omit that input and must skip candidate signing while retaining the non-publish policy and integration checks. Release-facing agent-paradigm blurbs under `docs/releases/` must describe diff --git a/scripts/release_control/release_promotion_policy_test.py b/scripts/release_control/release_promotion_policy_test.py index ad43537e8..9911791db 100644 --- a/scripts/release_control/release_promotion_policy_test.py +++ b/scripts/release_control/release_promotion_policy_test.py @@ -621,6 +621,19 @@ class ReleasePromotionPolicyTest(unittest.TestCase): self.assertIn("PULSE_UPDATE_SIGNING_PUBLIC_KEY=${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}", content) self.assertIn("Validate installer signing key pins", candidate_workflow) self.assertIn("timeout-minutes: 60", candidate_workflow) + self.assertIn("Verify Native Signing Configuration", candidate_workflow) + self.assertEqual(candidate_workflow.count("needs: signing-configuration"), 2) + for signing_secret in ( + "APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64", + "APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD", + "APPLE_DEVELOPER_ID_APPLICATION_IDENTITY", + "APPLE_NOTARY_KEY_P8_BASE64", + "APPLE_NOTARY_KEY_ID", + "APPLE_NOTARY_ISSUER_ID", + "WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64", + "WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD", + ): + self.assertIn(signing_secret, candidate_workflow) self.assertIn('tar -xzf "$tarball" -C "$extract_dir" -- "$@"', release_validator) self.assertNotIn('tar -xOf "$tarball" "$entry"', release_validator) self.assertIn("go run ./scripts/release_update_key.go public-key-ssh", candidate_workflow)