mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
fix(release): bind Helm application version to chart release
Reject mismatched image defaults before packaging; preserve equal and default versions. Reproduced four accepted mismatches before the fix. All 58 focused tests pass; no publication performed. Change-source: pulse-maintainer
This commit is contained in:
@@ -191,6 +191,9 @@ jobs:
|
||||
- name: Run Helm Pages publication retry tests
|
||||
run: python3 scripts/release_control/helm_pages_retry_test.py
|
||||
|
||||
- name: Helm publication version binding
|
||||
run: python3 scripts/release_control/helm_publish_version_test.py
|
||||
|
||||
- name: Run status audit unit tests
|
||||
run: python3 scripts/release_control/status_audit_test.py
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ on:
|
||||
required: true
|
||||
type: string
|
||||
app_version:
|
||||
description: "Application version to embed (defaults to chart version)."
|
||||
description: "Application version (must equal chart version; defaults to it)."
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
@@ -36,7 +36,7 @@ on:
|
||||
description: "Chart version (required when running manually, use format 4.24.0)"
|
||||
required: true
|
||||
app_version:
|
||||
description: "Application version to embed (defaults to chart version)"
|
||||
description: "Application version (must equal chart version; defaults to it)"
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
@@ -90,6 +90,13 @@ jobs:
|
||||
APP_VERSION="$CHART_VERSION"
|
||||
fi
|
||||
|
||||
# Pulse's server and agent image defaults use Chart.AppVersion.
|
||||
# Source provenance alone cannot detect a mismatched image override.
|
||||
if [ "$APP_VERSION" != "$CHART_VERSION" ]; then
|
||||
echo "::error::Release chart app_version must equal chart_version."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
IS_PRERELEASE="false"
|
||||
if [[ "$APP_VERSION" =~ -rc\.[0-9]+$ ]] || [[ "$APP_VERSION" =~ -alpha\.[0-9]+$ ]] || [[ "$APP_VERSION" =~ -beta\.[0-9]+$ ]]; then
|
||||
IS_PRERELEASE="true"
|
||||
|
||||
@@ -242,6 +242,7 @@ scripts/release_control/*
|
||||
!scripts/release_control/governance_stage_guard.py
|
||||
!scripts/release_control/governance_stage_guard_test.py
|
||||
!scripts/release_control/helm_pages_retry_test.py
|
||||
!scripts/release_control/helm_publish_version_test.py
|
||||
!scripts/release_control/live_runtime_proof.py
|
||||
!scripts/release_control/live_runtime_proof_test.py
|
||||
!scripts/release_control/mobile_relay_auth_approvals_proof.py
|
||||
|
||||
@@ -5168,3 +5168,20 @@ passed. A read-only probe of job 101235205647 retained the expected failure text
|
||||
without ESC bytes. Private containment classification and successful scheduled
|
||||
reconciliation still require post-integration evidence; this is not customer
|
||||
convergence or release qualification.
|
||||
|
||||
### 2026-09-05 — Bind hosted chart application version to release version
|
||||
|
||||
The hosted Helm publisher rejects a supplied application version that differs
|
||||
from the chart version before emitting version outputs or packaging. Both server
|
||||
and agent image defaults use Chart.AppVersion, so an exact source SHA and chart
|
||||
digest alone do not prevent an override from selecting another release's images.
|
||||
The normal release caller already supplies equal versions; default and release
|
||||
event paths remain unchanged. This does not remove users' image value overrides.
|
||||
|
||||
Verification: helm_publish_version_test.py executes the actual version-resolution
|
||||
shell. Four mismatches failed assertions before the fix and are rejected after it;
|
||||
stable/alpha/beta/RC equal and default versions and release-event defaults pass.
|
||||
All 4 tests, 7 Helm Pages retry tests and 47 promotion-policy tests pass locally.
|
||||
No hosted publication or installed-image qualification is claimed.
|
||||
External reference retrieved 2026-09-05: https://helm.sh/docs/topics/charts/#the-appversion-field
|
||||
explains that application version is separate from chart version.
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Exercise hosted Helm version resolution without packaging or publication."""
|
||||
from pathlib import Path
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
import textwrap
|
||||
import unittest
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
class HelmPublishVersionTests(unittest.TestCase):
|
||||
def resolve(self, chart="", app="", tag=""):
|
||||
workflow = (ROOT / ".github/workflows/publish-helm-chart.yml").read_text()
|
||||
step = workflow.split(" - name: Determine chart version\n", 1)[1]
|
||||
script = textwrap.dedent(step.split(" run: |\n", 1)[1].split(
|
||||
" - name:", 1)[0])
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
output = Path(tmp) / "output"
|
||||
result = subprocess.run(
|
||||
["bash", "-euo", "pipefail", "-c", script], cwd=ROOT,
|
||||
env={"PATH": os.environ["PATH"], "INPUT_CHART_VERSION": chart,
|
||||
"INPUT_APP_VERSION": app, "RELEASE_TAG_NAME": tag,
|
||||
"GITHUB_OUTPUT": str(output)},
|
||||
capture_output=True, text=True)
|
||||
return result, output.read_text() if output.exists() else ""
|
||||
|
||||
def test_matching_and_default_application_versions(self):
|
||||
for version in ("6.4.3", "6.4.3-rc.2", "6.5.0-beta.1", "6.5.0-alpha.1"):
|
||||
for app in ("", version):
|
||||
with self.subTest(version=version, app=app):
|
||||
result, output = self.resolve(version, app)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("\n" + version + "\n", output)
|
||||
self.assertIn("is_prerelease=" + ("true" if "-" in version else "false"), output)
|
||||
|
||||
def test_release_event_uses_tag(self):
|
||||
result, output = self.resolve(tag="v6.4.3-rc.2")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("\n6.4.3-rc.2\n", output)
|
||||
|
||||
def test_mismatched_application_rejected_before_outputs(self):
|
||||
for chart, app in (("6.4.3", "6.4.2"), ("6.4.3", "6.4.3-rc.2"),
|
||||
("6.4.3-rc.2", "6.4.3"), ("6.4.3", "latest")):
|
||||
with self.subTest(chart=chart, app=app):
|
||||
result, output = self.resolve(chart, app)
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertEqual(output, "")
|
||||
|
||||
def test_missing_version_rejected(self):
|
||||
result, output = self.resolve()
|
||||
self.assertNotEqual(result.returncode, 0)
|
||||
self.assertEqual(output, "")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -2434,6 +2434,13 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
self.assertIn("sync_chart_release_metadata.py", helm)
|
||||
self.assertNotIn("sync_chart_release_metadata.py", helm_pages)
|
||||
self.assertIn("--chart deploy/helm/pulse/Chart.yaml", helm)
|
||||
version_guard = 'if [ "$APP_VERSION" != "$CHART_VERSION" ]; then'
|
||||
self.assertIn(version_guard, helm)
|
||||
self.assertLess(helm.index(version_guard), helm.index(" - name: Package chart"))
|
||||
self.assertIn(
|
||||
"python3 scripts/release_control/helm_publish_version_test.py",
|
||||
(Path(__file__).resolve().parents[2] / ".github/workflows/canonical-governance.yml").read_text(),
|
||||
)
|
||||
self.assertIn('git checkout --detach "refs/tags/${RELEASE_TAG}"', helm)
|
||||
self.assertIn("Verify public GHCR chart identity and provenance", helm)
|
||||
self.assertIn("helm registry logout ghcr.io || true", helm)
|
||||
|
||||
Reference in New Issue
Block a user