From f43a7a3e62956fa066c8417a8b67b04e7e435cda Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Sun, 20 Sep 2026 14:24:41 +0100 Subject: [PATCH] fix(release): verify the private Pro source pair before creating a draft A 6.x release creates its draft release object before stage_private_pro_runtime dispatches the private Pro build, which resolves the payload by the exact frozen public commit (docs/release-source-pairs/.json). When that declaration is absent or names a different pulse_sha, the private build refuses and the run leaves an orphaned draft (v6.4.5-beta.2, v6.4.5-beta.3). Verify the declaration in prepare, before any draft exists, and fail with the exact path to create. This is an unconditional fact check independent of the opt-in harness gate in pulse-dev-infra #443/#444; the release steward still owns choosing the pair. Source issue: pulse-dev-infra #441. Change-source: pulse-maintainer --- .github/workflows/create-release.yml | 38 ++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 375f89a2d7..d1c4f7aeef 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -140,6 +140,44 @@ jobs: fi echo "[OK] Release dispatch is bound to ${EXPECTED_SOURCE_SHA}" + - name: Verify the private Pro source pair is declared + # A 6.x release stages a private Pro payload that the private build + # resolves by the exact frozen public commit. Verify that declaration + # before any draft release object exists, so a missing or mismatched + # pair cannot orphan another draft. This is a fact check: the release + # steward still owns choosing the pair. + if: ${{ startsWith(github.event.inputs.version, '6.') && github.event.inputs.historical_asset_backfill_only != 'true' && github.event.inputs.draft_only != 'true' }} + env: + GH_TOKEN: ${{ secrets.WORKFLOW_PAT }} + EXPECTED_SOURCE_SHA: ${{ inputs.expected_source_sha }} + PRIVATE_PAIR_REPOSITORY: rcourtman/pulse-enterprise + run: | + set -euo pipefail + if [[ -z "${GH_TOKEN:-}" ]]; then + echo "::error::WORKFLOW_PAT is required to verify the private Pro source pair before dispatch." + exit 1 + fi + pair_path="docs/release-source-pairs/${EXPECTED_SOURCE_SHA}.json" + pair_err="$(mktemp)" + if ! pair_content="$(gh api \ + -H 'Accept: application/vnd.github+json' \ + -H 'X-GitHub-Api-Version: 2026-03-10' \ + "repos/${PRIVATE_PAIR_REPOSITORY}/contents/${pair_path}" \ + --jq '.content' 2>"${pair_err}")"; then + if grep -qE 'HTTP 404|Not Found' "${pair_err}"; then + echo "::error::${PRIVATE_PAIR_REPOSITORY} has no ${pair_path} for the frozen public commit ${EXPECTED_SOURCE_SHA}. Record that pair, then dispatch again; refusing to create an orphaned draft." + else + echo "::error::Could not verify ${pair_path} in ${PRIVATE_PAIR_REPOSITORY}: $(tr '\n' ' ' <"${pair_err}")" + fi + exit 1 + fi + declared_pulse_sha="$(printf '%s' "${pair_content}" | base64 -d | python3 -c 'import json, sys; print(json.load(sys.stdin).get("pulse_sha", ""))')" + if [[ "${declared_pulse_sha}" != "${EXPECTED_SOURCE_SHA}" ]]; then + echo "::error::${pair_path} declares pulse_sha '${declared_pulse_sha}', not the frozen public commit ${EXPECTED_SOURCE_SHA}." + exit 1 + fi + echo "[OK] Private Pro source pair ${pair_path} declares pulse_sha ${declared_pulse_sha}." + - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: