from pathlib import Path import hashlib, json R = Path(__file__).resolve().parent.parent P = R / 'storage/app/private/investmoz-v2/portal-audit' load = lambda p: json.loads(p.read_text(encoding='utf-8-sig')) sha = lambda p: hashlib.sha256(p.read_bytes()).hexdigest() candidate = load(P / 'consolidated-candidate-latest.json') O = Path(candidate['output']) manifest = load(O / 'manifesto.json') assert manifest['revision'] == candidate['revision'] assert sha(O / 'manifesto.json') == candidate['manifest_sha256'] assert sha(O / candidate['zip']) == candidate['zip_sha256'] == manifest['zip_sha256'] assert manifest['code_files'] == 22 and manifest['production_assets'] == 159 for relative, expected in manifest['base_hashes'].items(): path = R / relative assert (sha(path) if path.is_file() else None) == expected, relative for relative, expected in manifest['unchanged_dependencies'].items(): assert sha(R / relative) == expected, relative snapshot = load(P / 'workflow-execution-services-latest.json') assert snapshot['active_services_changes'] == snapshot['active_acl_changes'] == 0 scope = { 'code_revision': manifest['code_revision'], 'active_base_hashes': manifest['base_hashes'], 'production_manifest_sha256': manifest['production_manifest_sha256'], 'candidate': candidate, 'resources_fingerprint': manifest['resources_fingerprint'], 'profile_fingerprint': manifest['profile_fingerprint'], 'services_snapshot': snapshot, } (P / 'workflow-execution-backup-scope.json').write_text(json.dumps(scope, indent=2), encoding='utf-8') source = (R / 'tools/portal-workflow-upgrade-target-backup.php').read_text(encoding='utf-8') source = source.replace('workflow-upgrade-target-backup-failure-20261011.json', 'workflow-execution-target-backup-failure.json') source = source.replace('workflow-upgrade-code-20261011.json', 'workflow-execution-backup-scope.json') source = source.replace("$c['driver'] === 'pgsql' && empty($c['url'])", "$c['driver'] === 'pgsql' && empty($c['url']) && in_array($c['host'], ['127.0.0.1','localhost'], true)") source = source.replace("'workflow_target_'", "'workflow_consolidated_target_'").replace('^workflow_target_', '^workflow_consolidated_target_') source = source.replace('workflow-target-backups/', 'workflow-execution-target-backups/') source = source.replace('workflow-upgrade-target-backup-link-20261011.json', 'workflow-execution-target-backup-link.json') source = source.replace('workflow-upgrade-target-backup-20261011.json', 'workflow-execution-target-backup.json') needle = '$zip->close();' addition = '''// Service assets are copied from an independent READ ONLY OS snapshot. $services=$scope['services_snapshot']; foreach($services['files'] as $serviceFile){ $name='services/'.$serviceFile['entry']; $servicePath=$services['directory'].'/'.str_replace('\\\\','/',$serviceFile['entry']); throw_unless(is_file($servicePath)&&hash_file('sha256',$servicePath)===$serviceFile['sha256'],RuntimeException::class,'Private service snapshot differs.'); if($serviceFile['source']!==null){throw_unless(is_file($serviceFile['source'])&&hash_file('sha256',$serviceFile['source'])===$serviceFile['sha256'],RuntimeException::class,'Service recovery source changed after snapshot.');} $zip->addFile($servicePath,$name);$fileHashes[$name]=$serviceFile['sha256']; } $servicesMetadata=$services['directory'].'/snapshot.json'; $zip->addFile($servicesMetadata,'services/snapshot.json');$fileHashes['services/snapshot.json']=hash_file('sha256',$servicesMetadata); ''' assert source.count(needle) == 1 source = source.replace(needle, addition + needle) needle = "$proof['scope']='Read-only backup" position = source.index(needle) addition = '''$proof['candidate_revision']=$scope['candidate']['revision']; $proof['candidate_zip_sha256']=$scope['candidate']['zip_sha256']; $proof['candidate_manifest_sha256']=$scope['candidate']['manifest_sha256']; $proof['resources_fingerprint']=$scope['resources_fingerprint'];$proof['profile_fingerprint']=$scope['profile_fingerprint']; $proof['services_snapshot_at']=$services['at']; $proof['service_asset_count']=count($services['files'])+1; $proof['service_task_export_count']=count(array_filter($services['tasks'],fn($task)=>$task['exists'])); $proof['ACL_metadata_exported']=$services['acl_objects'];$proof['ACL_metadata_roundtrip']=$services['acl_metadata_roundtrip']; $proof['ACL_restore_applied_on_destination']=false; $proof['ACL_scope']=$services['acl_scope']; $proof['signature_file_count']=$services['signature_files']; $proof['source_files_stable_at_snapshot_completion']=true; $proof['no_new_active_accounts_or_grants']=true; ''' source = source[:position] + addition + source[position:] source = source.replace("'files' => count($fileHashes),", "'files' => count($fileHashes), 'sequences' => count($sequences), 'MFA_readable' => $mfaReadable, 'originals' => count($documents), 'candidate' => substr($scope['candidate']['revision'],0,12),") (R / 'tools/portal-workflow-execution-target-backup.php').write_text(source, encoding='utf-8') print(json.dumps({'result': 'renewal_prepared', 'candidate': manifest['revision'][:12], 'code_revision': manifest['code_revision'][:12], 'service_assets': len(snapshot['files']) + 1, 'active_writes': 0}))