"""Build a metadata-manifested overlay of the tested server/admin changes.""" from pathlib import Path import hashlib import json import shutil import tarfile root = Path('C:/kefu') release = root / 'deploy/token-usage-20260917' old_release = root / 'deploy/ark-chat-config-20260917' payload = release / 'payload' assert not (release / 'manifest.json').exists(), 'Use a fresh release payload; do not overwrite an uploaded candidate' backend = ['admin_backend', 'admin_api', 'model_gateway', 'model_protocol', 'model_usage', 'knowledge_worker'] tests = ['test_model_usage_collection', 'test_model_usage_recovery', 'test_model_usage_ledger', 'test_model_gateway', 'test_gateway_guard_audit', 'test_gateway_media_audit', 'test_gateway_failure_audit', 'test_knowledge_model_output', 'test_knowledge_tasks', 'test_knowledge', 'test_admin_api', 'test_ark_chat_config'] files = ['wechat_rpa/' + name + '.py' for name in backend + tests] front = 'admin-web/apps/web-antd/src/' files += [front + 'api/console.ts', front + 'views/console/call-stats.vue'] files += [front + 'views/console/' + name for name in ( 'ModelUsageStats.vue', 'model-usage-format.ts', 'use-usage-request.ts', 'ModelUsageStats.test.ts', 'model-usage.test.ts')] files += [str(p.relative_to(root)).replace('\\', '/') for p in (root / 'admin-web/apps/web-antd/dist').rglob('*') if p.is_file()] manifest = [] for name in files: source = root / name target = payload / name target.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(source, target) data = source.read_bytes() manifest.append({'path': name, 'bytes': len(data), 'sha256': hashlib.sha256(data).hexdigest()}) (release / 'manifest.json').write_text(json.dumps(manifest, indent=2), encoding='utf-8') with tarfile.open(release / 'payload.tar.gz', 'w:gz') as archive: for row in manifest: archive.add(payload / row['path'], arcname=row['path']) previous_image = json.loads((release / 'baseline.json').read_text())['image'] for filename in ('01-stage.sh', '02-backup-build.sh', '03-deploy.sh', 'rollback.sh', 'Dockerfile.overlay'): text = (old_release / filename).read_text(encoding='utf-8').replace('ark-chat-config-20260917', 'token-usage-20260917') text = text.replace('sha256:1d6df0547b32c29ab28c6d96c0a434f5587cb1b62e97c7aaa0a454c4932c1976', previous_image) if filename == '02-backup-build.sh': text = text.replace('test_ark_chat_config test_model_gateway test_admin_api.ModelTestApiTest', ' '.join(tests)) if filename == '03-deploy.sh': start = text.index("baseline=json.loads(") end = text.index('\nPY', start) text = text[:start] + '''baseline=json.loads((release/'baseline.json').read_text())['expected'] for name, expected in baseline.items(): assert hashlib.sha256((root/'wechat_rpa'/name).read_bytes()).hexdigest()==expected,name+' changed during staging' ''' + text[end:] text = text.replace("print('Model configuration fix source and frontend installed.',flush=True)", "print('Token usage source and frontend installed.',flush=True)") # Complete in-flight generation where possible before replacing processes. text = text.replace('docker compose config --quiet', 'docker compose config --quiet\ndocker compose stop -t 190 knowledge-worker\ndocker compose stop -t 65 gateway') (release / filename).write_text(text, encoding='utf-8', newline='\n') print(json.dumps({'files': len(manifest), 'package_bytes': (release / 'payload.tar.gz').stat().st_size, 'backend_tests': tests}, indent=2))