From b5784df4b7cd195d69378fad1672abc9718226c3 Mon Sep 17 00:00:00 2001 From: "pulse-triage[bot]" <249995291+pulse-triage[bot]@users.noreply.github.com> Date: Sat, 5 Sep 2026 22:22:57 +0100 Subject: [PATCH] fix(delivery): verify public Helm package against qualified bytes Readable chart metadata does not prove that the public index serves the OCI-qualified package. Pull through the consumer repository and compare exact bytes before reporting convergence, with offline regression coverage for mismatched, missing and unavailable downloads. Activation and publication authority remain unchanged. Change-source: pulse-maintainer --- .github/workflows/helm-pages.yml | 18 ++++++- .../subsystems/deployment-installability.md | 16 ++++++ .../release_control/helm_pages_retry_test.py | 51 +++++++++++++++++++ .../release_promotion_policy_test.py | 3 +- 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/.github/workflows/helm-pages.yml b/.github/workflows/helm-pages.yml index 970dfd3ce..3ca3e13d3 100644 --- a/.github/workflows/helm-pages.yml +++ b/.github/workflows/helm-pages.yml @@ -275,6 +275,10 @@ jobs: set -euo pipefail public_repo="https://rcourtman.github.io/Pulse" public_ready=false + public_work="$(mktemp -d)" + trap 'rm -rf "${public_work}"' EXIT + qualified_chart="dist/pulse-${VERSION}.tgz" + test -f "${qualified_chart}" for attempt in $(seq 1 30); do public_index="$(mktemp)" if curl -fsSL --retry 2 --retry-delay 1 --retry-all-errors \ @@ -283,7 +287,17 @@ jobs: helm repo remove pulse-public >/dev/null 2>&1 || true helm repo add pulse-public "${public_repo}" --force-update helm repo update pulse-public - if helm show chart pulse-public/pulse --version "${VERSION}" >/dev/null; then + # Metadata readability is not an exact-artifact receipt. Pull via + # the consumer index and compare with the OCI-qualified package, + # not the OCI manifest digest (which hashes a different object). + rm -f "${public_work}/pulse-${VERSION}.tgz" + if helm pull pulse-public/pulse --version "${VERSION}" --destination "${public_work}" && \ + helm show chart "${public_work}/pulse-${VERSION}.tgz" >/dev/null; then + if ! cmp -s "${qualified_chart}" "${public_work}/pulse-${VERSION}.tgz"; then + echo "::error::Public Helm chart bytes differ from the qualified package; refusing a successful convergence receipt." + rm -f "${public_index}" + exit 1 + fi public_ready=true rm -f "${public_index}" break @@ -297,4 +311,4 @@ jobs: echo "::error::Public Helm repository did not expose chart ${VERSION}." exit 1 fi - echo "[OK] Public Helm repository serves pulse ${VERSION}." + echo "[OK] Public Helm repository serves the exact qualified pulse ${VERSION} package." diff --git a/docs/release-control/v6/internal/subsystems/deployment-installability.md b/docs/release-control/v6/internal/subsystems/deployment-installability.md index a5c18c6ec..94e05405a 100644 --- a/docs/release-control/v6/internal/subsystems/deployment-installability.md +++ b/docs/release-control/v6/internal/subsystems/deployment-installability.md @@ -15,6 +15,22 @@ ## Purpose +### Public Helm exact-package receipt + +The post-activation public Pages verification in `.github/workflows/helm-pages.yml` +must pull the requested chart version through the consumer repository and compare +its archive bytes with the package already recovered through OCI qualification. +Readable chart metadata alone is not a successful convergence receipt. The OCI +manifest digest hashes a different object and must not be compared directly with +the archive. A byte mismatch fails immediately; unavailable downloads retain the +bounded retry, with previous downloaded files removed before each attempt. +Activation bindings, publication authority and containment remain prerequisites. +The executed-shell fixtures in `scripts/release_control/helm_pages_retry_test.py` +cover matching, mismatched, missing and unavailable public packages without +network or publication writes. They are not installed Helm qualification or proof +that any historical published chart was wrong or has been repaired. + + The shell-owned multi-tenant integration suite uses a dedicated desktop-only Playwright configuration selecting the seven multi-tenant scenarios. It must reject any non-empty E2E tier identity rather than impersonating stable or diff --git a/scripts/release_control/helm_pages_retry_test.py b/scripts/release_control/helm_pages_retry_test.py index 4ccfb78f0..2625cf3fa 100644 --- a/scripts/release_control/helm_pages_retry_test.py +++ b/scripts/release_control/helm_pages_retry_test.py @@ -106,5 +106,56 @@ class HelmPagesRetryTests(unittest.TestCase): self.assertIn("--latest=false", calls[-1]) +class PublicReceiptTests(unittest.TestCase): + def receipt(self, mode): + workflow = (ROOT / '.github/workflows/helm-pages.yml').read_text() + step = workflow.split(' - name: Verify public Pages chart\n', 1)[1] + script = textwrap.dedent(step.split(' run: |\n', 1)[1]) + with tempfile.TemporaryDirectory() as temp: + root = Path(temp) + (root / 'dist').mkdir() + (root / 'dist/pulse-6.4.3.tgz').write_bytes(b'qualified') + programs = { + 'curl': '''#!/usr/bin/env python3 +import pathlib, sys +pathlib.Path(sys.argv[sys.argv.index('-o')+1]).write_text('version: 6.4.3\\n') +''', + 'helm': '''#!/usr/bin/env python3 +import os, pathlib, sys +args = sys.argv[1:] +if args[0] == 'pull': + mode = os.environ['MODE'] + if mode == 'unavailable': sys.exit(1) + if mode != 'missing': + dest = pathlib.Path(args[args.index('--destination')+1]) + (dest / 'pulse-6.4.3.tgz').write_bytes(b'qualified' if mode == 'match' else b'wrong') +''', + 'sleep': '#!/bin/sh\nexit 0\n', + } + for name, body in programs.items(): + path = root / name + path.write_text(body) + path.chmod(0o755) + return subprocess.run(['bash', '-c', script], cwd=root, + env={'PATH': f"{root}:{os.environ['PATH']}", + 'VERSION': '6.4.3', 'MODE': mode}, + capture_output=True, text=True) + + def test_matching_public_package_passes(self): + result = self.receipt('match') + self.assertEqual(result.returncode, 0, result.stderr) + + def test_same_version_wrong_bytes_fail(self): + result = self.receipt('wrong') + self.assertNotEqual(result.returncode, 0) + self.assertNotIn('[OK]', result.stdout) + + def test_missing_download_fails(self): + self.assertNotEqual(self.receipt('missing').returncode, 0) + + def test_unavailable_download_fails(self): + self.assertNotEqual(self.receipt('unavailable').returncode, 0) + + if __name__ == "__main__": unittest.main() diff --git a/scripts/release_control/release_promotion_policy_test.py b/scripts/release_control/release_promotion_policy_test.py index 35ce7ca0f..f7ba48efc 100644 --- a/scripts/release_control/release_promotion_policy_test.py +++ b/scripts/release_control/release_promotion_policy_test.py @@ -2424,7 +2424,8 @@ class ReleasePromotionPolicyTest(unittest.TestCase): self.assertIn('helm repo index "${index_work}"', helm_pages) self.assertIn('git -C gh-pages push origin HEAD:gh-pages', helm_pages) self.assertIn('grep -q "version: ${VERSION}"', helm_pages) - self.assertIn('helm show chart pulse-public/pulse --version "${VERSION}"', helm_pages) + self.assertIn('helm pull pulse-public/pulse --version "${VERSION}"', helm_pages) + self.assertIn('cmp -s "${qualified_chart}" "${public_work}/pulse-${VERSION}.tgz"', helm_pages) self.assertNotIn("helm status pulse || true", helm_pages) self.assertNotIn("kubectl describe pods", helm_pages) self.assertIn("release-convergence.yml/dispatches", release_workflow)