activity-core/scripts/render_gitops.py
tegwick a12f1169f9
All checks were successful
CI Smoke / host-smoke (push) Successful in 3s
CI Smoke / container-smoke (push) Successful in 14s
Build and Publish Container Image / build-and-push (push) Successful in 27s
Project pinned digest-safe retention tool through existing GitOps resources
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0e241-8285-7a63-8c0c-51c9cb824dc3
2026-09-27 16:04:30 +02:00

70 lines
3.9 KiB
Python

"""Render only the reviewed application resource set, excluding jobs and custody."""
import argparse
import hashlib
import json
import re
import urllib.request
from pathlib import Path
import yaml
ROOT=Path(__file__).resolve().parents[1]
MANAGED={
'ConfigMap': ['actcore-runtime-config','actcore-external-activity-definitions','actcore-report-schemas','actcore-ops-service-inventory'],
'Service': ['actcore-api','actcore-worker-metrics'],
'Deployment': ['actcore-api','actcore-worker','actcore-event-router'],
}
class Dumper(yaml.SafeDumper): pass
def strings(d,v): return d.represent_scalar('tag:yaml.org,2002:str',v,style='|' if '\n' in v else None)
Dumper.add_representer(str,strings)
def render():
docs=list(yaml.safe_load_all((ROOT/'k8s/railiance/20-runtime.yaml').read_text()))
selected=[]
for kind,names in MANAGED.items():
for name in names:
matches=[d for d in docs if d and d['kind']==kind and d['metadata']['name']==name]
if len(matches)!=1: raise ValueError(f'expected exactly one {kind}/{name}')
d=matches[0]
if d['metadata'].get('namespace')!='activity-core': raise ValueError('namespace outside grant')
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)
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)
def verify_platform(source_file=None):
pin=json.loads((ROOT/'k8s/gitops/platform-source.json').read_text())
if (pin.get('schema_version')!=1 or pin.get('repository')!='coulomb/railiance-platform'
or pin.get('path')!='scripts/forgejo_package_prune.py'
or not re.fullmatch('[0-9a-f]{40}',pin['revision'])):
raise ValueError('invalid platform source pin')
if source_file is None:
url=f"https://forgejo.coulomb.social/coulomb/railiance-platform/raw/commit/{pin['revision']}/{pin['path']}"
with urllib.request.urlopen(url,timeout=10) as response: body=response.read(131073)
else: body=Path(source_file).read_bytes()
docs=list(yaml.safe_load_all(render()))
cm=next(d for d in docs if d['metadata']['name']=='actcore-ops-service-inventory')
worker=next(d for d in docs if d['metadata']['name']=='actcore-worker')
if (len(body)>131072 or hashlib.sha256(body).hexdigest()!=pin['sha256']
or cm['data']['forgejo_package_prune.py'].encode()!=body
or worker['spec']['template']['metadata']['annotations'].get('activity-core/retention-sha256')!=pin['sha256']):
raise ValueError('platform tool projection mismatch')
if __name__=='__main__':
parser=argparse.ArgumentParser(); parser.add_argument('--check',action='store_true'); parser.add_argument('--verify-frontend',action='store_true'); parser.add_argument('--verify-platform',action='store_true'); args=parser.parse_args()
path=ROOT/'k8s/gitops/runtime.yaml'; text=render()
if args.check:
if path.read_text()!=text: raise SystemExit('GitOps projection stale; run scripts/render_gitops.py')
else: path.write_text(text)
if args.verify_frontend: verify_frontend()
if args.verify_platform: verify_platform()