fix(deploy): pin main-470ece8, and check pins against real builds
The upgrade failed as `pre-upgrade hooks failed: timed out waiting for the
condition`. The migrate hook was in ImagePullBackOff: tag main-11f689d does not
exist. `.forgejo/workflows/image.yaml` has a `paths:` filter that excludes
deploy/**, so the chart-only commit was green in CI and built nothing — the two
green runs on it were host-smoke and container-smoke, not build-and-push.
470ece8 carries all the code; every commit after it touches only the chart and
generated docs.
scripts/verify_image_pin.py refuses a pin whose commit has no successful
build-and-push run, and says so in those terms rather than as a timeout five
minutes later. Verified against both the bad pin and the good one; treats an
unreachable forge as inconclusive rather than as failure.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
This commit is contained in:
parent
b140929dba
commit
5ae1f4ffc5
3 changed files with 948 additions and 1 deletions
65
scripts/verify_image_pin.py
Executable file
65
scripts/verify_image_pin.py
Executable file
|
|
@ -0,0 +1,65 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Check that the pinned image tag was actually built (STATE-WP-0084-T02).
|
||||
|
||||
A helm upgrade pinned to a tag no registry holds fails as a pre-upgrade hook
|
||||
timeout after ~5 minutes, and the message names a timeout rather than a missing
|
||||
image. The cause is easy to reach honestly: `.forgejo/workflows/image.yaml` has
|
||||
a `paths:` filter, so a chart-only commit is green in CI and builds nothing. A
|
||||
green pipeline is not evidence of an image.
|
||||
|
||||
Exits non-zero if the pinned tag's commit never ran `build-and-push`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.request
|
||||
from pathlib import Path
|
||||
|
||||
VALUES = Path(__file__).resolve().parents[1] / "deploy/railiance/apps/helm/state-hub-values.yaml"
|
||||
API = "https://forgejo.coulomb.social/api/v1/repos/coulomb/state-hub/actions/tasks?limit=50"
|
||||
|
||||
|
||||
def main() -> int:
|
||||
m = re.search(r'^\s*tag:\s*"?main-([0-9a-f]{7,40})"?\s*$', VALUES.read_text(), re.M)
|
||||
if not m:
|
||||
print(f"could not read a main-<sha> tag from {VALUES}")
|
||||
return 2
|
||||
sha = m.group(1)
|
||||
|
||||
try:
|
||||
with urllib.request.urlopen(API, timeout=30) as r:
|
||||
runs = json.load(r).get("workflow_runs", [])
|
||||
except Exception as exc: # noqa: BLE001 - offline is inconclusive, not a failure
|
||||
print(f"could not reach the forge ({type(exc).__name__}); pin unverified")
|
||||
return 0
|
||||
|
||||
built = {
|
||||
run["head_sha"][:7]
|
||||
for run in runs
|
||||
if run.get("name") == "build-and-push" and run.get("status") == "success"
|
||||
}
|
||||
if sha[:7] in built:
|
||||
print(f"ok: main-{sha} was built by build-and-push")
|
||||
return 0
|
||||
|
||||
print(f"main-{sha} has no successful build-and-push run.")
|
||||
try:
|
||||
changed = subprocess.run(
|
||||
["git", "show", "--name-only", "--format=", sha],
|
||||
capture_output=True, text=True, cwd=VALUES.parents[3],
|
||||
).stdout.split()
|
||||
if changed and all(p.startswith("deploy/") or p.endswith(".md") for p in changed):
|
||||
print("It is a chart/docs-only commit, which the paths: filter excludes.")
|
||||
except Exception: # noqa: BLE001
|
||||
pass
|
||||
recent = [r["head_sha"][:7] for r in runs if r.get("name") == "build-and-push"][:3]
|
||||
print(f"Most recent built commits: {', '.join(recent) or 'none found'}")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue