Verify domain definition projection against pinned frontend source
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 20s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e241-8285-7a63-8c0c-51c9cb824dc3
This commit is contained in:
tegwick 2026-09-27 15:26:04 +02:00
parent c12a8fbcfb
commit 68063cc57a
4 changed files with 42 additions and 2 deletions

View file

@ -14,7 +14,7 @@ COPY .forgejo/workflows/image.yaml ./.forgejo/workflows/image.yaml
COPY schemas/ ./schemas/ COPY schemas/ ./schemas/
COPY k8s/ ./k8s/ COPY k8s/ ./k8s/
COPY scripts/render_gitops.py scripts/check_gitops_promotion.py ./scripts/ COPY scripts/render_gitops.py scripts/check_gitops_promotion.py ./scripts/
RUN uv sync --frozen --extra dev && uv run --no-sync pytest -q -p no:cacheprovider tests/test_frontend_patterns.py tests/test_instruction_evaluation.py tests/test_admin_sync_api.py tests/test_gitops_release.py RUN uv sync --frozen --extra dev && uv run --no-sync python scripts/render_gitops.py --check --verify-frontend && uv run --no-sync pytest -q -p no:cacheprovider tests/test_frontend_patterns.py tests/test_instruction_evaluation.py tests/test_admin_sync_api.py tests/test_gitops_release.py
# Stage 2 — runtime image # Stage 2 — runtime image
FROM python:3.12-slim@sha256:78387bc3881b8273120a12ebe6c1ab22b018ccc2c9adf565ae1ac9b536e184ea AS runtime FROM python:3.12-slim@sha256:78387bc3881b8273120a12ebe6c1ab22b018ccc2c9adf565ae1ac9b536e184ea AS runtime

View file

@ -0,0 +1,10 @@
{
"schema_version": 1,
"repository": "coulomb/frontend-patterns",
"revision": "10f0131f67daac14b14d48735f71db65366ebde5",
"files": {
"frontend-patterns-daily.md": "b604873587003c96a7015d705936a5aaf865123e34b4be3afe928c459a02c7d1",
"frontend-patterns-monthly.md": "52755a99c02e6ed4ef1f99261bbb5ce59214b6761b360aa467b405e4932c9b98",
"frontend-patterns-weekly.md": "5437dc5fbcec8fecbbca0e0c53531750c90a9aa5c044df5b1b40e4b96af9c186"
}
}

View file

@ -1,5 +1,9 @@
"""Render only the reviewed application resource set, excluding jobs and custody.""" """Render only the reviewed application resource set, excluding jobs and custody."""
import argparse import argparse
import hashlib
import json
import re
import urllib.request
from pathlib import Path from pathlib import Path
import yaml import yaml
ROOT=Path(__file__).resolve().parents[1] ROOT=Path(__file__).resolve().parents[1]
@ -22,9 +26,25 @@ def render():
if d['metadata'].get('namespace')!='activity-core': raise ValueError('namespace outside grant') if d['metadata'].get('namespace')!='activity-core': raise ValueError('namespace outside grant')
selected.append(d) selected.append(d)
return '# Generated by scripts/render_gitops.py; do not edit directly.\n'+'---\n'.join(yaml.dump(d,Dumper=Dumper,sort_keys=False) for d in selected) return '# Generated by scripts/render_gitops.py; do not edit directly.\n'+'---\n'.join(yaml.dump(d,Dumper=Dumper,sort_keys=False) for d in selected)
def verify_frontend(source_dir=None):
pin=json.loads((ROOT/'k8s/gitops/frontend-source.json').read_text())
names={f'frontend-patterns-{mode}.md' for mode in ('daily','weekly','monthly')}
if pin.get('schema_version')!=1 or pin.get('repository')!='coulomb/frontend-patterns' or set(pin['files'])!=names or not re.fullmatch('[0-9a-f]{40}',pin['revision']):
raise ValueError('invalid frontend source pin')
cm=next(d for d in yaml.safe_load_all(render()) if d['metadata']['name']=='actcore-external-activity-definitions')
for name,digest in pin['files'].items():
if source_dir is None:
url=f"https://forgejo.coulomb.social/coulomb/frontend-patterns/raw/commit/{pin['revision']}/activity-definitions/{name}"
with urllib.request.urlopen(url,timeout=10) as response: body=response.read(32769)
else: body=(Path(source_dir)/name).read_bytes()
if len(body)>32768 or hashlib.sha256(body).hexdigest()!=digest or cm['data'][name].encode()!=body:
raise ValueError('frontend definition projection mismatch: '+name)
if __name__=='__main__': if __name__=='__main__':
parser=argparse.ArgumentParser(); parser.add_argument('--check',action='store_true'); args=parser.parse_args() parser=argparse.ArgumentParser(); parser.add_argument('--check',action='store_true'); parser.add_argument('--verify-frontend',action='store_true'); args=parser.parse_args()
path=ROOT/'k8s/gitops/runtime.yaml'; text=render() path=ROOT/'k8s/gitops/runtime.yaml'; text=render()
if args.check: if args.check:
if path.read_text()!=text: raise SystemExit('GitOps projection stale; run scripts/render_gitops.py') if path.read_text()!=text: raise SystemExit('GitOps projection stale; run scripts/render_gitops.py')
else: path.write_text(text) else: path.write_text(text)
if args.verify_frontend: verify_frontend()

View file

@ -62,3 +62,13 @@ def test_refuses_authority_expansion(change):
elif change=='tag':c['image']='forgejo.coulomb.social/coulomb/activity-core:latest' elif change=='tag':c['image']='forgejo.coulomb.social/coulomb/activity-core:latest'
else:after.pop() else:after.pop()
with pytest.raises(ValueError):load('check_gitops_promotion').validate(before,after,evidence,now) with pytest.raises(ValueError):load('check_gitops_promotion').validate(before,after,evidence,now)
def test_frontend_projection_checks_content_and_pin(tmp_path):
renderer=load('render_gitops')
cm=next(d for d in yaml.safe_load_all(renderer.render()) if d['metadata']['name']=='actcore-external-activity-definitions')
for name,body in cm['data'].items():
if name.startswith('frontend-patterns-'): (tmp_path/name).write_text(body)
renderer.verify_frontend(tmp_path)
(tmp_path/'frontend-patterns-daily.md').write_text('tampered')
with pytest.raises(ValueError): renderer.verify_frontend(tmp_path)