| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- """Seal the reviewed board handoff, validate its hashes, and emit a delivery receipt."""
- import hashlib
- import json
- from pathlib import Path
- import re
- import shutil
- import zipfile
- ROOT = Path(__file__).resolve().parents[1]
- BASE = ROOT / 'work/board-deploy-20261106-r1'
- PKG = BASE / 'package'
- OUT = ROOT / 'artifacts/看板部署-20261106'
- sha = lambda b: hashlib.sha256(b).hexdigest()
- def finalize():
- OUT.mkdir(parents=True, exist_ok=True)
- dest = OUT / 'ksf-board-deploy-20261106-r1.zip'
- if dest.exists():
- raise RuntimeError('RELEASE_ALREADY_SEALED')
- doc = ROOT / 'docs/board-deploy-20261106/发给运维001-部署已推送看板.md'
- shutil.copyfile(doc, PKG / doc.name)
- manifest = json.loads((PKG / 'deploy/board-files.json').read_text(encoding='utf-8'))
- closure = []
- for f in manifest['files']:
- p = PKG / 'payload' / f['path']
- assert sha(p.read_bytes()) == f['sha256'], f['path']
- if p.suffix == '.js':
- for m in re.finditer(r'''(?:from|import)\s*\(?\s*['"](\.[^'"]+)['"]''', p.read_text(encoding='utf-8')):
- imported = (p.parent / m[1]).resolve()
- assert imported.is_relative_to((PKG / 'payload/src').resolve()) and imported.is_file(), (p, m[1])
- closure.append(str(imported.relative_to(PKG)))
- assert not (PKG / 'payload/index.html').exists()
- assert not (PKG / 'payload/src/dev').exists()
- assert not list(PKG.rglob('*.sqlite'))
- assert not list(PKG.rglob('*.pyc'))
- assert not list(PKG.rglob('.env*'))
- publisher = json.loads((BASE / 'static-publisher-validation.json').read_text())
- assert publisher['ok']
- validation = {'ok': True, 'sourceCommit': manifest['commit'], 'staticFilesVerified': 71,
- 'relativeImportsChecked': len(closure), 'nodeTests': {'total': 83, 'passed': 83, 'failed': 0},
- 'pythonTests': {'total': 50, 'passed': 50, 'failed': 0, 'skipped': 0, 'usesPrivateCorpus': True},
- 'publisher': publisher, 'productionWrites': 0, 'productionBrowserVerified': False}
- (PKG / 'VALIDATION.json').write_text(json.dumps(validation, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
- files = sorted(p for p in PKG.rglob('*') if p.is_file() and p.name != 'FILES.sha256')
- lines = [sha(p.read_bytes()) + ' ' + p.relative_to(PKG).as_posix() for p in files]
- (PKG / 'FILES.sha256').write_text('\n'.join(lines) + '\n', encoding='utf-8', newline='\n')
- with zipfile.ZipFile(dest, 'x', zipfile.ZIP_DEFLATED, compresslevel=9) as z:
- for p in [*files, PKG / 'FILES.sha256']:
- z.write(p, p.relative_to(PKG).as_posix())
- with zipfile.ZipFile(dest) as z:
- assert z.testzip() is None
- for line in z.read('FILES.sha256').decode('utf-8').splitlines():
- digest, p = line.split(' ', 1)
- assert sha(z.read(p)) == digest, p
- assert len(z.namelist()) == len(files) + 1
- release = json.loads((PKG / 'RELEASE.json').read_text(encoding='utf-8'))
- receipt = {'ok': True, 'file': dest.name, 'bytes': dest.stat().st_size, 'sha256': sha(dest.read_bytes()),
- 'entries': len(files) + 1, 'filesVerified': len(files), 'release': release['release'], 'commit': release['commit'],
- 'requiresDatabasePackage': release['requiredDatabasePackage'], 'productionInstalled': False,
- 'productionWrites': 0, 'browserVerified': False, 'validation': validation}
- (OUT / 'delivery-receipt.json').write_text(json.dumps(receipt, ensure_ascii=False, indent=2) + '\n', encoding='utf-8')
- shutil.copyfile(doc, OUT / doc.name)
- print(json.dumps(receipt, ensure_ascii=False))
- if __name__ == '__main__':
- finalize()
|