Files

102 lines
7.3 KiB
Python

#!/usr/bin/env python3
"""Reviewed separate DEVELOPMENT jobs after the preserved Core failure.
Default is read-only preflight. Only --run launches the four remaining native jobs.
"""
from pathlib import Path
import argparse,fcntl,hashlib,json,os,subprocess,sys
ROOT=Path('/home/jakub/devel/escapistsrereintroduced')
SESSION=Path('/tmp/erri-radio-maintenance-packages')
COMPAT=Path('/tmp/erri-headless-compat')
STEAM=Path('/home/jakub/.local/share/Steam')
EXTERNAL=STEAM/'steamapps/compatdata/1874880/pfx/drive_c/users/steamuser/Documents/My Games/ArmaReforger/addons'
sys.path.insert(0,str(ROOT/'tools'))
import native_package as pack
EXPECTED={
'package-report.json':'b2a1b0966a4add6af5272ddf2ea619cbe9b5d88c0446e7588fa58f4d514be436',
'authored-before.json':'811dff0d2800f7ac2e0c5954c6885695eb8a31d3af82ba294677191312a9e592',
'external-addons-before.json':'d560c4771507ce4c08574807d22519511081c608cf6fe8f05695ceccddfc671b',
'external-addons-policy.json':'2cdcacde92c4726e8c43809263548d0bc26d83685f70b48afac8c495605457be',
'external-source-cache.json':'d6c118d6628d7bcd70c532fb3212e9117d8a143a91accb2b92dc1006e7909eb9',
'evidence/core/report.json':'1894ba8544c66dd802f2ffe082394d2600ab1dc1a9c90c0eb66bd3fcd0cb70e3',
'evidence/core/native-exit.json':'b151b29d2b2dfcf355d70f71b552ec7de8f3b3ae8325ca5d15bc48ac711e1af5',
}
REMAINING=('ai','intel','map','chat')
def require(value,message):
if not value:raise ValueError(message)
def read(name):return json.loads((SESSION/name).read_text())
def retained_inputs():
require(SESSION.is_dir() and SESSION==SESSION.resolve(),'Session redirected')
for name,sha in EXPECTED.items():
path=SESSION/name;require(path.is_file() and not path.is_symlink() and pack.digest(path)==sha,'Preserved baseline changed: '+name)
original=read('package-report.json');authored=read('authored-before.json')
require(original['stopped_reason']=='native completion not safely observed' and original['skipped_projects']==list(REMAINING),'Wrong stopped package attempt')
require(len(original['projects'])==1 and original['projects'][0]==read('evidence/core/report.json'),'Original Core report mismatch')
require(original['source_repository_unchanged'] is True and original['external_addons_unchanged_after_run'] is True,'Original input integrity failed')
require(pack.inventory(ROOT)==authored and pack.copied_authored_snapshot(SESSION/'source',authored)==authored,'Frozen authored source changed')
core=original['projects'][0]
require(set((SESSION/'packages/core').iterdir())=={SESSION/'packages/core'/n for n in ('addon.gproj','data.pak','resourceDatabase.rdb')},'Core runtime files changed')
for name,record in core['packages'].items():
path=SESSION/'packages/core'/name
require(path.is_file() and not path.is_symlink() and pack.digest(path)==record['sha256'],'Preserved Core artifact changed: '+name)
return original,authored
def external_context(cache_path):
context=pack.external_request('chat',EXTERNAL,ROOT)
require(context['policy_sha256']==read('package-report.json')['external_policy_sha256'] and context['policy']==read('external-addons-policy.json'),'Frozen external policy changed')
context.update(records=read('external-addons-before.json'),cache_path=cache_path,cache_sha256=pack.digest(cache_path))
pack.verify_external(context)
return context
def safe_completion(report,needs_external):
# Error-bearing candidate verdicts remain failed. This predicate permits only
# separately reviewed later jobs after independent idle verification below.
return (report.get('native_exit_code')==0 and report.get('cleanup_confirmed') is True
and report.get('runner_error') is None and report.get('copied_authored_unchanged') is True
and report.get('profile_addons_unchanged') is True
and report.get('completion_observed') in {'process-exited','native-child-exited','native-or-launcher-exited-prefix-active'}
and report.get('external_addons_unchanged_after_run') is True
and report.get('external_addon_count')==(6 if needs_external else 0))
def main():
parser=argparse.ArgumentParser(description=__doc__);parser.add_argument('--run',action='store_true');args=parser.parse_args()
original,authored=retained_inputs();context=external_context(SESSION/'external-source-cache.json')
require(safe_completion(original['projects'][0],False),'Original Core cannot support a separate later job')
for name in REMAINING:
for folder in ('profiles','packages','cache','evidence'):require(not (SESSION/folder/name).exists() and not (SESSION/folder/name).is_symlink(),'Remaining addon path already exists: '+folder+'/'+name)
pack.assert_no_game();pack.assert_prefix_idle(COMPAT)
if not args.run:
print(json.dumps({'preflight':'ready','native_launched':False,'authored_files':len(authored),'external_addons':6,'remaining':REMAINING,'original_report_sha256':EXPECTED['package-report.json'],'build_qualified':False}));return 0
descriptor=os.open(COMPAT/'.erri-workbench.lock',os.O_RDWR|os.O_NOFOLLOW)
with os.fdopen(descriptor,'a') as lock:
fcntl.flock(lock,fcntl.LOCK_EX|fcntl.LOCK_NB)
pack.assert_no_game();pack.assert_prefix_idle(COMPAT);retained_inputs()
output=SESSION/'separate-remaining-development';output.mkdir(exist_ok=False)
cache=output/'external-source-cache.json';cache.write_bytes((SESSION/'external-source-cache.json').read_bytes())
context=external_context(cache)
report={'kind':'SEPARATE DEVELOPMENT PACKAGE JOBS','original_report_sha256':EXPECTED['package-report.json'],'reason':'Core native exit0 with verified owned cleanup; original prefix-active and native-error verdicts retained. Each later job requires independently idle prefix and unchanged frozen inputs.','projects':list(original['projects']),'stopped_reason':None,'build_qualified':False,'release_qualified':False,'published':False}
try:
for name in REMAINING:
retained_inputs();pack.verify_external(context);pack.assert_no_game();pack.assert_prefix_idle(COMPAT)
for folder in ('profiles','packages','cache','evidence'):require(not (SESSION/folder/name).exists() and not (SESSION/folder/name).is_symlink(),'Addon job would overwrite existing evidence: '+name)
result=pack.run_project(STEAM,COMPAT,SESSION,name,300,authored,True,False,external=context if name in {'ai','chat'} else None)
report['projects'].append(result);pack.save(output/'report.json',report)
require(safe_completion(result,name in {'ai','chat'}),'Native status/cleanup/input guard stopped separate jobs at '+name)
retained_inputs();pack.verify_external(context);pack.assert_no_game();pack.assert_prefix_idle(COMPAT)
print(name,'native_exit',result['native_exit_code'],'original_verdict',result['result'],'candidate_created',result['candidate_created'],flush=True)
except (OSError,ValueError,subprocess.SubprocessError) as error:
report['stopped_reason']=str(error)
finally:
report['all_jobs_finished']=len(report['projects'])==5 and report['stopped_reason'] is None
report['all_candidate_verdicts_successful']=all(r.get('candidate_created') is True for r in report['projects'])
pack.save(output/'report.json',report)
require(report['all_jobs_finished'],'Separate jobs stopped: '+str(report['stopped_reason']))
print('Separate DEVELOPMENT jobs completed. Original failed verdicts retained; PAK content and packed startup still require verification.')
return 0
if __name__=='__main__':
try:raise SystemExit(main())
except (OSError,ValueError,subprocess.SubprocessError) as error:print('REFUSED: '+str(error),file=sys.stderr);raise SystemExit(1)