railiance-platform/scripts/eso_adoption_readiness.py
codex 7c79293831
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Gate ESO adoption on verified predecessors and receive release custody handoff
Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a126e1-76f8-7643-8cf3-e93336539ade
2026-10-11 23:25:20 +02:00

61 lines
4.6 KiB
Python

#!/usr/bin/env python3
"""Local, non-executing ESO adoption readiness gate. No credential or live calls."""
import argparse
from datetime import datetime,timezone
import hashlib
import json
from pathlib import Path
import re
import yaml
D2='89f1058f-33bc-47a8-8a49-0b44d426c67e'
def require(ok,reason):
if not ok:raise ValueError(reason)
def fresh(receipt,now):
stamp=datetime.fromisoformat(receipt['captured_at'].replace('Z','+00:00'))
require(stamp.tzinfo is not None and 0<=(now-stamp).total_seconds()<=300,'predecessor_proof_stale')
def source_check(app):
require(app.get('kind')=='Application' and app.get('metadata',{}).get('name')=='external-secrets' and app['metadata'].get('namespace')=='argocd','source_identity')
spec=app['spec'];source=spec['source'];require(spec.get('project')=='railiance-platform-addons' and spec.get('destination')=={'server':'https://kubernetes.default.svc','namespace':'external-secrets'},'source_destination')
require(source.get('repoURL')=='https://charts.external-secrets.io' and source.get('chart')=='external-secrets' and source.get('targetRevision')=='0.16.1' and source['helm'].get('releaseName')=='external-secrets','source_chart_pin')
values=yaml.safe_load(source['helm']['values']);require(values.get('installCRDs') is True,'install_crds')
options=values.get('crds',{}).get('annotations',{}).get('argocd.argoproj.io/sync-options','')
require(set(options.split(','))=={'Prune=false','Delete=false'},'crd_prune_delete_protection')
sync=spec.get('syncPolicy',{});require('automated' not in sync and not app['metadata'].get('finalizers'),'manual_no_finalizer')
require('ServerSideApply=true' in sync.get('syncOptions',[]),'server_side_apply')
require(not any(x in sync.get('syncOptions',[]) for x in ('Force=true','Replace=true')),'no_force_replace')
return {'chart_pin':'0.16.1','crd_prune_delete_protected':True,'manual_sync_only':True,'server_side_apply':True}
def issue_check(receipt,now):
require(receipt.get('schema_version')=='rapp-issue-core-adoption/v2' and receipt.get('passed') is True,'issue_core_adoption_missing')
fresh(receipt,now);checks=receipt['checks'];require(checks.get('estate_node_ip') is True and checks.get('argo_synced_healthy') is True and checks.get('argo_tracking') is True and checks.get('external_secret_ready') is True and checks.get('private_service') is True,'issue_core_adoption_incomplete')
require(bool(re.fullmatch(r'[0-9a-f]{40}',checks.get('source_revision',''))),'issue_core_immutable_revision')
require(all(checks.get(k)==v for k,v in {'healthz_status':200,'unauthenticated_ingest_status':401,'authenticated_ingest_status':201,'authenticated_close_status':200}.items()),'issue_core_controlled_smoke_missing')
def target_check(receipt,now):
require(receipt.get('schema_version')=='argocd-workload-adoption-proof/v1' and receipt.get('status')=='verified' and receipt.get('application')=='target-revenue','target_revenue_adoption_missing')
fresh(receipt,now);require(receipt.get('manual_apply_prune_off') is True and receipt.get('child_automation_enabled') is False,'target_revenue_adoption_incomplete')
def assess(app,issue,target,now=None):
now=now or datetime.now(timezone.utc);missing=[];checks={}
for name,fn,value in [('source',source_check,app),('RPF-WP-0044-T04',issue_check,issue),('RPF-WP-0044-T05',target_check,target)]:
try:
result=fn(value) if name=='source' else fn(value,now)
checks[name]=result if name=='source' else True
except Exception:missing.append(name)
return {'schema':'eso-adoption-readiness/v1','task':'RPF-WP-0044-T06','approval_ref':D2,'source_ready':bool(checks.get('source')),'ready_for_fresh_native_render_diff':not missing,'missing_or_invalid':missing,'checks':checks,'next_action':'fresh_native_render_diff' if not missing else 'wait_for_predecessor_automated_proof','founder_approval_required':False,'native_mutation':False,'live_or_credential_calls':False}
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--draft',required=True,type=Path);parser.add_argument('--issue-core-proof',type=Path);parser.add_argument('--target-revenue-proof',type=Path)
args=parser.parse_args()
def load(path):
try:return json.loads(path.read_text()) if path else {}
except Exception:return {}
try:app=yaml.safe_load(args.draft.read_text());receipt=assess(app,load(args.issue_core_proof),load(args.target_revenue_proof))
except Exception:receipt={'ready_for_fresh_native_render_diff':False,'failure':'source_unverified','native_mutation':False,'live_or_credential_calls':False}
print(json.dumps(receipt,sort_keys=True));return 0 if receipt['ready_for_fresh_native_render_diff'] else 2
if __name__=='__main__':raise SystemExit(main())