the-custodian/docs/changes/CUST-WP-0080/inventory_forge_coverage.py
codex 9af8689327
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Reject generated-only commits / generated-commit-guard (push) Successful in 10s
security: close sync deadline fix and checkpoint scoped forge expansion
2026-10-13 16:55:37 +02:00

86 lines
5.8 KiB
Python

#!/usr/bin/env python3
"""Read-only database/directory/hook reconciliation; commit aggregates only."""
import argparse
import datetime
import hashlib
import importlib.util
import json
import os
from pathlib import Path
import re
import shlex
HERE = Path(__file__).parent
s=importlib.util.spec_from_file_location('template', HERE/'activate_forge_template.py')
t=importlib.util.module_from_spec(s)
s.loader.exec_module(t)
def main():
parser=argparse.ArgumentParser(description=__doc__)
parser.add_argument('--private-output',type=Path,required=True)
parser.add_argument('--receipt',type=Path,required=True)
args=parser.parse_args()
original=json.loads((HERE/'receive-hook-proposal.json').read_text())
pod=t.target_pod(t.deployment(),original)
t.check_artifacts(pod)
for artifact in original['artifacts'][:2]:
mode=t.native.execute(pod,'stat','-c','%a %u %g',original['version_directory']+'/'+artifact['name']).decode().strip()
if mode!='755 1000 1000':raise ValueError('artifact-custody-changed')
query=('SELECT json_agg(row_to_json(x)) FROM (SELECT r.id,u.lower_name AS owner,r.lower_name AS name,'
'r.is_private,r.is_empty,r.is_archived,r.is_mirror,r.is_fork,r.created_unix,r.git_size,r.lfs_size '
'FROM repository r JOIN public.user u ON u.id=r.owner_id ORDER BY r.id) x')
raw=t.native.command(['-n','databases','exec','forgejo-db-1','--','psql','-U','postgres','-d','forgejo','-At','-c',query])
rows=json.loads(raw)
statements=['set -euo pipefail']
for r in rows:
if not isinstance(r['id'],int) or any(not re.fullmatch(r'[a-z0-9_.-]+',r[k]) or r[k] in ('.','..') for k in ('owner','name')):
raise ValueError('repository-identity-refused')
path='/data/git/gitea-repositories/'+r['owner']+'/'+r['name']+'.git'
statements.extend(['repo='+shlex.quote(path), 'printf "REPO '+str(r['id'])+'\\n"',
'test -d "$repo" && test ! -L "$repo" && test ! -L "$repo/hooks" && test ! -L "$repo/hooks/pre-receive.d"',
'test -z "$(find "$repo/hooks" -maxdepth 2 -type l -print -quit)"',
'find "$repo/hooks" -maxdepth 2 -type f ! -name "*.sample" -exec sha256sum {} \\;',
'printf "SHALLOW "; git --git-dir="$repo" rev-parse --is-shallow-repository',
'if git --git-dir="$repo" config --local --includes --name-only --list | grep -Eiq "(^extensions.partialclone$|^remote\\..*\\.(promisor|partialclonefilter)$)"; then printf "PARTIAL true\\n"; else printf "PARTIAL false\\n"; fi',
'if git --git-dir="$repo" rev-parse --verify HEAD >/dev/null 2>&1; then printf "GITLINKS "; git --git-dir="$repo" ls-tree -r --format="%(objectmode)" HEAD | awk \'$0 == "160000" { n++ } END { print n+0 }\'; else printf "GITLINKS unborn\\n"; fi'])
observed=t.native.execute(pod,'bash','-s',payload=('\n'.join(statements)+'\n').encode(),timeout=180).decode()
by_id={r['id']:r for r in rows}; current=None
for line in observed.splitlines():
if line.startswith('REPO '):
current=by_id[int(line[5:])]; current['hooks']={}
elif line.startswith('SHALLOW '):current['shallow']=line[8:]=='true'
elif line.startswith('PARTIAL '):current['partial']=line[8:]=='true'
elif line.startswith('GITLINKS '):current['head_gitlinks']=None if line[9:]=='unborn' else int(line[9:])
else:
digest,path=line.split(' ',1)
prefix='/data/git/gitea-repositories/'+current['owner']+'/'+current['name']+'.git/hooks/'
if not re.fullmatch(r'[a-f0-9]{64}',digest) or not path.startswith(prefix):raise ValueError('unexpected-hook-output')
current['hooks'][path[len(prefix):]]=digest
default=original['existing_hook_hashes']; admitted=dict(default)
admitted['pre-receive.d/'+original['hook_name']]=original['hook_sha256']
for r in rows:
r['profile']='admitted' if r['hooks']==admitted else 'builtin-only' if r['hooks']==default else 'unknown'
r['head_compatible']=not r['shallow'] and not r['partial'] and r['head_gitlinks'] in (None,0)
directories=t.native.execute(pod,'find','/data/git/gitea-repositories','-mindepth','2','-maxdepth','2','-type','d','-name','*.git').decode().splitlines()
expected={'/data/git/gitea-repositories/'+r['owner']+'/'+r['name']+'.git' for r in rows}
if set(directories)!=expected:raise ValueError('database-directory-mismatch')
private={'observed_at':datetime.datetime.now(datetime.timezone.utc).isoformat(),'pod_uid':pod['pod_uid'],'repositories':rows}
content=(json.dumps(private,indent=2)+'\n').encode()
fd=os.open(args.private_output,os.O_WRONLY|os.O_CREAT|os.O_EXCL,0o600)
with os.fdopen(fd,'wb') as output:output.write(content)
receipt={'result':'reconciled','observed_at':private['observed_at'],'pod_uid':pod['pod_uid'],
'repositories':len(rows),'repository_directories':len(directories),
'profiles':{kind:sum(r['profile']==kind for r in rows) for kind in ('admitted','builtin-only','unknown')},
'builtin_only_head_compatible':sum(r['profile']=='builtin-only' and r['head_compatible'] for r in rows),
'repositories_with_head_gitlinks':sum(bool(r['head_gitlinks']) for r in rows),
'shallow':sum(r['shallow'] for r in rows),'partial':sum(r['partial'] for r in rows),
'mirrors':sum(r['is_mirror'] for r in rows),'private':sum(r['is_private'] for r in rows),
'lfs_nonzero_size':sum(bool(r['lfs_size']) for r in rows),
'private_inventory_sha256':hashlib.sha256(content).hexdigest(),
'scope':'Current database/directory reconciliation, exact hook content, shallow/partial and default-HEAD gitlink compatibility; not historical scanning or authenticated route acceptance.'}
args.receipt.write_text(json.dumps(receipt,indent=2)+'\n')
print(json.dumps(receipt))
if __name__=='__main__':main()