book-ocr-studio / scripts /build_public.py
moebiusT7's picture
Source beta 2026-09-23, revision 5
0209b2f verified
Raw History Blame Contribute Delete
3.01 kB
"""Build a new, source-only directory from an explicit reviewed allowlist."""
import hashlib
import json
import shutil
import sys
import zipfile
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def build(destination):
destination = Path(destination).resolve()
if destination.exists():
raise ValueError('Destination already exists; use a new release directory')
names = [line.strip() for line in (ROOT/'public-release-files.txt').read_text().splitlines()
if line.strip() and not line.startswith('#')]
if len(names) != len(set(names)):
raise ValueError('Duplicate allowlist entry')
from render_public_docs import PAGES, render
for original, generated in PAGES.items():
if (ROOT/generated).read_text(encoding='utf-8') != render(original):
raise ValueError('Stale HTML; run scripts/render_public_docs.py: '+generated)
payload = {}
secret_file = ROOT/'.chrome-bridge-key'
private_key = secret_file.read_bytes().strip() if secret_file.exists() else b''
private_keys = [private_key] if private_key else []
for profile in (ROOT/'.connector-profiles').glob('*.json'):
secret = json.loads(profile.read_text()).get('api_key', '')
if secret:private_keys.append(secret.encode())
for name in names:
rel = Path(name)
source = ROOT/rel
if rel.is_absolute() or '..' in rel.parts or source.is_symlink() or not source.is_file():
raise ValueError('Invalid allowlist entry: '+name)
if not source.resolve().is_relative_to(ROOT):
raise ValueError('Source escapes project')
data = source.read_bytes()
if any(secret in data for secret in private_keys):
raise ValueError('Local credential found in allowlisted file: '+name)
payload[name] = data
destination.mkdir(parents=True)
for name, data in payload.items():
target = destination/name
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(data)
target.chmod(0o755 if name.endswith('.sh') else 0o644)
manifest = {'format': 1, 'scope': 'source-only', 'files': {
name: {'sha256': hashlib.sha256(data).hexdigest(), 'bytes': len(data)}
for name, data in sorted(payload.items())}}
(destination/'BUILD_MANIFEST.json').write_text(json.dumps(manifest, indent=2)+'\n')
archive = destination/'book-ocr-studio-source.zip'
with zipfile.ZipFile(archive, 'w', zipfile.ZIP_DEFLATED) as output:
for name in sorted([*payload, 'BUILD_MANIFEST.json']):
output.write(destination/name, name)
(destination/'ARCHIVE_SHA256.txt').write_text(hashlib.sha256(archive.read_bytes()).hexdigest()+' '+archive.name+'\n')
from verify_public import verify
verify(destination)
print('Built', destination)
if __name__ == '__main__':
if len(sys.argv) != 2:
raise SystemExit('Usage: python3 scripts/build_public.py NEW_DESTINATION')
build(sys.argv[1])