Make private Pro publication reruns recover promotion-only failures
Some checks are pending
Build and Test / Backend tests (rest-0) (push) Blocked by required conditions
Build and Test / Secret Scan (push) Waiting to run
Build and Test / Detect changed areas (push) Waiting to run
Build and Test / Frontend (push) Blocked by required conditions
Build and Test / Backend tests (api) (push) Blocked by required conditions
Build and Test / Backend tests (rest-1) (push) Blocked by required conditions
Build and Test / Script smoke tests & backend build (push) Blocked by required conditions
Build and Test / Benchmarks (push) Blocked by required conditions
Canonical Governance / governance (push) Waiting to run
Helm CI / Lint and Render Chart (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 2/8) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 3/8) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 4/8) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 5/8) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 6/8) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 7/8) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 8/8) (push) Waiting to run
Core E2E Tests / Playwright Core E2E (shard 1/8) (push) Waiting to run
Core E2E Tests / Agent registration lifecycle (push) Waiting to run
Core E2E Tests / E2E verdict (push) Blocked by required conditions
Unified Agent Native Verification / Linux ARM64 (push) Waiting to run
Unified Agent Native Verification / Linux x64 (push) Waiting to run
Unified Agent Native Verification / Windows x64 (push) Waiting to run
Unified Agent Native Verification / macOS ARM64 (push) Waiting to run
Unified Agent Native Verification / macOS Intel (push) Waiting to run
Unified Agent Native Verification / FreeBSD cross-build contract (push) Waiting to run

The paid-runtime R2 prefix was derived from the wall-clock date, and the
enterprise build was always dispatched as a fresh rebuild. Rerunning the
failed publish job after a transient promotion failure therefore hit the
enterprise R2 overwrite guard (same run id, existing prefix) and the
Definitive Release Verdict could never go green by rerun, as on
v6.2.0-rc.6. The prefix date now comes from the run's createdAt, which
is stable across rerun attempts, and the build dispatch passes
reuse_existing_packet=true so pulse-enterprise validates the packet the
earlier attempt uploaded, skips the rebuild, and only the promotion
re-executes against it. The deployment-installability contract now
records rerun recoverability as part of the promotion boundary.
This commit is contained in:
rcourtman 2026-08-02 00:49:58 +01:00
parent 9137f86355
commit 9d2c49f3e5
3 changed files with 35 additions and 3 deletions

View file

@ -1390,7 +1390,11 @@ jobs:
# Docker publication workflow to pass, then dispatches the pulse-pro live
# promotion workflow and waits for the signed packet to update the license
# broker. A failure here fails the release pipeline instead of leaving paid
# customers on a stale private manifest.
# customers on a stale private manifest. Rerunning this job after a
# promotion-only failure is safe: the R2 prefix is derived from run-stable
# values and the build is dispatched with reuse_existing_packet=true, so the
# enterprise side validates the packet the earlier attempt uploaded, skips
# the rebuild, and only the promotion re-executes.
publish_private_pro_runtime:
needs:
- prepare
@ -1475,7 +1479,22 @@ jobs:
allow_ga_publish=true
fi
r2_prefix="${TAG}-pro-$(date -u '+%Y%m%d')-${GITHUB_RUN_ID}"
# The R2 prefix must be identical across rerun attempts of this run:
# a rerun after a promotion-only failure has to reuse the packet the
# earlier attempt already uploaded instead of tripping the enterprise
# R2 overwrite guard. Run creation date and run id are stable across
# attempts; wall-clock date is not.
run_created_date="$(
gh run view "${GITHUB_RUN_ID}" \
--repo "${GITHUB_REPOSITORY}" \
--json createdAt \
--jq '.createdAt' | cut -c1-10 | tr -d '-'
)"
if [[ ! "${run_created_date}" =~ ^[0-9]{8}$ ]]; then
echo "::error::Could not derive the release run creation date for the R2 prefix."
exit 1
fi
r2_prefix="${TAG}-pro-${run_created_date}-${GITHUB_RUN_ID}"
build_started_at="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
echo "Dispatching private Pro build for ${TAG} with R2 prefix ${r2_prefix}."
gh workflow run build-pro-release.yml \
@ -1488,6 +1507,7 @@ jobs:
-f publish_docker_image=true \
-f docker_image=license.pulserelay.pro/pulse-pro \
-f r2_prefix="${r2_prefix}" \
-f reuse_existing_packet=true \
-f allow_stable_ga_publish="${allow_ga_publish}"
wait_for_workflow rcourtman/pulse-enterprise "Build Pro Release" main "${build_started_at}" "private Pro build" 7200

View file

@ -861,6 +861,16 @@ upgrade, update, release, or artifact-selection behavior.
fail the public release workflow; future private Pro publication must not
depend on an operator noticing a manual checklist step after the public RC
has shipped.
A promotion-only failure must be recoverable by rerunning the public
release run's failed jobs: the paid-runtime R2 prefix is derived from
run-stable values (the run's creation date and run id, never the
wall-clock date at attempt time), and the private build dispatch sets
`reuse_existing_packet=true` so `Build Pro Release` validates a complete
signed packet already present at that prefix, skips the rebuild, and lets
the promotion re-execute against the packet the earlier attempt uploaded.
A rebuilt packet from identical inputs is waste and lineage churn; a
non-empty prefix that fails packet validation must fail the private build
instead of being overwritten.
A support-only private Pro prerelease image is a narrower exception for
customer verification of an already-fixed defect. It may dispatch the private
`Build Pro Release` workflow with `publish_docker_image=true`,

View file

@ -1904,7 +1904,8 @@ func TestCreateReleasePublishesPrivateProRuntime(t *testing.T) {
`github.event.inputs.draft_only != 'true'`,
`startsWith(needs.prepare.outputs.version, '6.')`,
`GH_TOKEN: ${{ secrets.WORKFLOW_PAT }}`,
`r2_prefix="${TAG}-pro-$(date -u '+%Y%m%d')-${GITHUB_RUN_ID}"`,
`--json createdAt`,
`r2_prefix="${TAG}-pro-${run_created_date}-${GITHUB_RUN_ID}"`,
`gh workflow run build-pro-release.yml`,
`--repo rcourtman/pulse-enterprise`,
`-f pulse_ref="${TAG}"`,
@ -1914,6 +1915,7 @@ func TestCreateReleasePublishesPrivateProRuntime(t *testing.T) {
`-f publish_docker_image=true`,
`-f docker_image=license.pulserelay.pro/pulse-pro`,
`-f r2_prefix="${r2_prefix}"`,
`-f reuse_existing_packet=true`,
`-f allow_stable_ga_publish="${allow_ga_publish}"`,
`wait_for_workflow rcourtman/pulse-enterprise "Build Pro Release" main "${build_started_at}" "private Pro build"`,
`gh workflow run promote-paid-runtime-release.yml`,