From 85fb558f0a6994e1285729e99b43814093d7716e Mon Sep 17 00:00:00 2001 From: Richard Courtman Date: Wed, 2 Sep 2026 17:52:33 +0100 Subject: [PATCH] Qualify release candidates against the branch that owns their version The secure-runtime qualification required the candidate commit to be an ancestor of origin/main. Under the release train a patch candidate is cut from release/vX.Y and is not on main at all, so v6.4.3-rc.1's qualification failed at "Verify detached release source" before running any check, and the steward correctly refused to treat the candidate as stable-capable. Resolve the branch the control plane maps the version to, fetch that, check ancestry against it, and hand the same ref to the attestation so the receipt records the line it was verified against. Refs #1753 Contract-Neutral: release qualification checks ancestry against the mapped release branch; no product or contract change (cherry picked from commit 79d87a4a2c94bfae36247f7be1a138e10e24bed0) --- .../qualify-secure-runtime-release.yml | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/qualify-secure-runtime-release.yml b/.github/workflows/qualify-secure-runtime-release.yml index 8738362d9..fedb8f2bb 100644 --- a/.github/workflows/qualify-secure-runtime-release.yml +++ b/.github/workflows/qualify-secure-runtime-release.yml @@ -65,14 +65,25 @@ jobs: TAG: ${{ steps.release.outputs.tag }} run: | set -euo pipefail - git fetch --no-tags origin refs/heads/main:refs/remotes/origin/main + # The release train cuts patch candidates from release/vX.Y, so the + # candidate is reachable from the branch the control plane maps its + # version to, not necessarily from main (v6.4.3-rc.1, 2026-09-02). + required_branch="$(python3 scripts/release_control/control_plane.py \ + --branch-for-version "${TAG#v}")" + [[ "${required_branch}" =~ ^(main|release/v[0-9]+\.[0-9]+)$ ]] || { + echo "::error::Control plane mapped ${TAG} to an unexpected branch: ${required_branch}" + exit 1 + } + git fetch --no-tags origin \ + "refs/heads/${required_branch}:refs/remotes/origin/${required_branch}" commit="$(git rev-parse "${TAG}^{commit}")" test "${GITHUB_REF}" = "refs/tags/${TAG}" test "${GITHUB_SHA}" = "${commit}" test "$(git branch --show-current)" = "" test -z "$(git status --short --untracked-files=all)" - git merge-base --is-ancestor "${commit}" origin/main + git merge-base --is-ancestor "${commit}" "origin/${required_branch}" python3 scripts/write_github_output.py commit "${commit}" + python3 scripts/write_github_output.py line_ref "origin/${required_branch}" - name: Set up Go uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 @@ -117,6 +128,7 @@ jobs: GH_TOKEN: ${{ github.token }} PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }} QUALIFIED_COMMIT: ${{ steps.source.outputs.commit }} + LINE_REF: ${{ steps.source.outputs.line_ref }} RELEASE_ID: ${{ steps.release.outputs.release_id }} TAG: ${{ steps.release.outputs.tag }} run: | @@ -132,7 +144,7 @@ jobs: --verify-release-packet-only \ --checkout "${GITHUB_WORKSPACE}" \ --commit "${QUALIFIED_COMMIT}" \ - --main-ref origin/main \ + --main-ref "${LINE_REF}" \ --collector-v1 "${download_dir}/pulse-secure-runtime-collector-v1-linux-amd64" \ --collector-v2 "${download_dir}/pulse-secure-runtime-collector-v2-linux-amd64" \ --collector-v3 "${download_dir}/pulse-secure-runtime-collector-v3-linux-amd64" \ @@ -299,6 +311,7 @@ jobs: GH_TOKEN: ${{ github.token }} PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }} QUALIFIED_COMMIT: ${{ steps.source.outputs.commit }} + LINE_REF: ${{ steps.source.outputs.line_ref }} RELEASE_ID: ${{ steps.release.outputs.release_id }} TAG: ${{ steps.release.outputs.tag }} WORKFLOW_OUTPUT_1: ${{ steps.lab.outputs.elapsed_seconds }} @@ -314,7 +327,7 @@ jobs: python3 scripts/release_control/secure_runtime_attestation_v7.py \ --checkout "${GITHUB_WORKSPACE}" \ --commit "${QUALIFIED_COMMIT}" \ - --main-ref origin/main \ + --main-ref "${LINE_REF}" \ --receipt "${evidence_dir}/receipt.json" \ --receipt-record-path "docs/release-control/v6/internal/records/secure-agent-runtime-systemd-receipt-v7-${TAG}.json" \ --transcript "${evidence_dir}/transcript.jsonl" \