Merge pull request #1956 from rcourtman/maintainer/20260907T121525Z-release-v6.4

Reject v6.4 release dispatches when source identity drifts
This commit is contained in:
pulse-triage[bot]
2026-09-07 14:15:10 +01:00
committed by GitHub
6 changed files with 66 additions and 0 deletions
+20
View File
@@ -9,6 +9,10 @@ on:
description: 'Version number (e.g., 4.30.0)'
required: true
type: string
expected_source_sha:
description: 'Exact 40-character commit SHA admitted for this release'
required: true
type: string
release_notes:
description: 'Release notes (markdown)'
required: true
@@ -108,6 +112,22 @@ jobs:
visual_capture_count: ${{ steps.visual_plan.outputs.capture_count }}
visual_comparison_tag: ${{ steps.visual_plan.outputs.comparison_tag }}
steps:
- name: Verify admitted source commit
env:
EXPECTED_SOURCE_SHA: ${{ inputs.expected_source_sha }}
run: |
set -euo pipefail
if [[ ! "${EXPECTED_SOURCE_SHA}" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::expected_source_sha must be an exact 40-character commit SHA"
exit 1
fi
if [[ "${GITHUB_SHA}" != "${EXPECTED_SOURCE_SHA}" || \
"${GITHUB_WORKFLOW_SHA}" != "${EXPECTED_SOURCE_SHA}" ]]; then
echo "::error::Release dispatch expected ${EXPECTED_SOURCE_SHA}, but GitHub resolved source ${GITHUB_SHA} and workflow ${GITHUB_WORKFLOW_SHA}."
exit 1
fi
echo "[OK] Release dispatch is bound to ${EXPECTED_SOURCE_SHA}"
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
@@ -2168,6 +2168,13 @@ artifact-selection behaviour.
trigger, promotion resolver, rendered release body, current upgrade guide,
or current release packet that routes systemd/LXC rollback through the
Unified Agent installer, and must retain explicit Docker image guidance.
17. Bind every publishing release dispatch to the exact commit admitted by the
caller. `.github/workflows/create-release.yml` must require a full
40-character `expected_source_sha` and, before checkout, reject the run
unless both `GITHUB_SHA` and `GITHUB_WORKFLOW_SHA` equal that commit.
`scripts/trigger-release.sh` and `scripts/trigger-stable-patch.sh` must send
the exact remote candidate SHA they already verified; branch ancestry or a
later branch tip is not equivalent release admission.
## Current State
@@ -933,6 +933,10 @@ func TestCreateReleaseUploadsPowerShellInstaller(t *testing.T) {
convergenceWorkflow := string(convergenceContent)
required := []string{
`historical_asset_backfill_only:`,
`expected_source_sha:`,
`EXPECTED_SOURCE_SHA: ${{ inputs.expected_source_sha }}`,
`"${GITHUB_SHA}" != "${EXPECTED_SOURCE_SHA}"`,
`"${GITHUB_WORKFLOW_SHA}" != "${EXPECTED_SOURCE_SHA}"`,
`description: 'Repair an already-published release packet in place without rebuilding binaries'`,
`SYFT_VERSION="1.42.4"`,
`SYFT_ARCHIVE="syft_${SYFT_VERSION}_linux_amd64.tar.gz"`,
@@ -1595,6 +1595,31 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertIn("did not produce a valid promotion metadata envelope", workflow)
self.assertIn("Do not use this artifact to clear", workflow)
def test_release_dispatch_rejects_source_or_workflow_drift_before_checkout(self) -> None:
workflow = yaml.safe_load(read(".github/workflows/create-release.yml"))
steps = workflow["jobs"]["prepare"]["steps"]
self.assertEqual(steps[0]["name"], "Verify admitted source commit")
self.assertIn("actions/checkout@", steps[1]["uses"])
expected = "a" * 40
other = "b" * 40
for admitted, source, workflow_sha, succeeds in (
(expected, expected, expected, True),
(expected, other, expected, False),
(expected, expected, other, False),
(expected, other, other, False),
("", expected, expected, False),
("main", expected, expected, False),
("a" * 39, expected, expected, False),
):
with self.subTest(admitted=admitted, source=source, workflow=workflow_sha):
result = subprocess.run(
["bash", "-c", steps[0]["run"]],
env={"PATH": os.defpath, "EXPECTED_SOURCE_SHA": admitted,
"GITHUB_SHA": source, "GITHUB_WORKFLOW_SHA": workflow_sha},
capture_output=True, text=True, check=False,
)
self.assertEqual(result.returncode == 0, succeeds, result.stdout + result.stderr)
def test_release_workflow_enforces_rc_lineage_soak_and_v5_notice(self) -> None:
content = read(".github/workflows/create-release.yml")
update_demo_workflow = read(".github/workflows/update-demo-server.yml")
@@ -1694,6 +1719,10 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
self.assertIn("build_rollback_section", renderer)
self.assertIn("promotion metadata out of customer notes", renderer)
self.assertIn("historical_asset_backfill_only:", content)
self.assertIn("expected_source_sha:", content)
self.assertIn('EXPECTED_SOURCE_SHA: ${{ inputs.expected_source_sha }}', content)
self.assertIn('"${GITHUB_SHA}" != "${EXPECTED_SOURCE_SHA}"', content)
self.assertIn('"${GITHUB_WORKFLOW_SHA}" != "${EXPECTED_SOURCE_SHA}"', content)
self.assertIn("Repair an already-published release packet in place without rebuilding binaries", content)
self.assertIn("draft: true", content)
self.assertIn("activate_release:", content)
+3
View File
@@ -120,6 +120,7 @@ python3 scripts/check-workflow-dispatch-inputs.py \
--workflow-path .github/workflows/create-release.yml \
--branch "$CURRENT_BRANCH" \
--require version \
--require expected_source_sha \
--require release_notes \
--require release_screenshot_plan \
--require promoted_from_tag \
@@ -372,6 +373,7 @@ echo "Triggering release workflow..."
if [ -n "$NOTES_FILE" ]; then
jq -n \
--arg version "$VERSION" \
--arg expected_source_sha "$LOCAL" \
--rawfile release_notes "$NOTES_FILE" \
--rawfile release_screenshot_plan "$VISUAL_PLAN_FILE" \
--arg rollback_version "$ROLLBACK_VERSION" \
@@ -387,6 +389,7 @@ if [ -n "$NOTES_FILE" ]; then
--arg mobile_release_evidence "$MOBILE_RELEASE_EVIDENCE" \
'{
version: $version,
expected_source_sha: $expected_source_sha,
release_notes: $release_notes,
release_screenshot_plan: $release_screenshot_plan,
rollback_version: $rollback_version,
+3
View File
@@ -203,6 +203,7 @@ else
--workflow-path .github/workflows/create-release.yml \
--branch "$CURRENT_BRANCH" \
--require version \
--require expected_source_sha \
--require release_notes \
--require release_screenshot_plan \
--require promoted_from_tag \
@@ -219,6 +220,7 @@ else
jq -n \
--arg version "$VERSION" \
--arg expected_source_sha "$LOCAL_SHA" \
--rawfile release_notes "$NOTES_FILE" \
--rawfile release_screenshot_plan "$VISUAL_PLAN_FILE" \
--arg promoted_from_tag "" \
@@ -234,6 +236,7 @@ else
--arg mobile_release_evidence "$MOBILE_RELEASE_EVIDENCE" \
'{
version: $version,
expected_source_sha: $expected_source_sha,
release_notes: $release_notes,
release_screenshot_plan: $release_screenshot_plan,
promoted_from_tag: $promoted_from_tag,