#!/usr/bin/env python3 """Create a bounded source/PDFium snapshot, excluding results, builds and VM keys.""" import argparse import hashlib import json import os from pathlib import Path import re import tarfile ROOT = Path(__file__).resolve().parents[2] parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('--name', required=True) args = parser.parse_args() if not re.fullmatch(r'[a-z0-9][a-z0-9-]{0,63}', args.name): parser.error('Use a short lowercase snapshot name') work = Path(os.environ.get('DOCVIEW_VM_WORK', ROOT / 'build-ubuntu-vm')).resolve() archive = work / (args.name + '.tar.gz') manifest = work / 'metadata' / (args.name + '.json') if archive.exists() or manifest.exists(): parser.error('Choose a new name to preserve previous source evidence') files = [ROOT / name for name in ('CMakeLists.txt', 'resources.qrc', 'README.md')] for name in ('src', 'qml', 'cmake', 'resources', 'tools', 'tests', 'docs', '.deps/pdfium'): directory = ROOT / name for base, directories, leaves in os.walk(directory, followlinks=False): directories[:] = [d for d in directories if d not in ('results', '__pycache__', '.git') and not (Path(base) / d).is_symlink()] files.extend(Path(base) / leaf for leaf in leaves if leaf != 'validation-record.json') rows, total = [], 0 for path in sorted(set(files)): if path.is_symlink() or not path.is_file() or not path.resolve().is_relative_to(ROOT): raise SystemExit('Unexpected non-regular source input') size = path.stat().st_size total += size if len(rows) >= 10000 or size > 256 * 1024 * 1024 or total > 512 * 1024 * 1024: raise SystemExit('Source snapshot exceeds finite limits') with path.open('rb') as stream: digest = hashlib.file_digest(stream, 'sha256').hexdigest() rows.append({'path': str(path.relative_to(ROOT)), 'size': size, 'sha256': digest}) work.mkdir(parents=True, exist_ok=True) manifest.parent.mkdir(parents=True, exist_ok=True) with tarfile.open(archive, 'x:gz') as stream: for row in rows: stream.add(ROOT / row['path'], arcname=row['path'], recursive=False) with archive.open('rb') as stream: digest = hashlib.file_digest(stream, 'sha256').hexdigest() manifest.write_text(json.dumps({'archiveSha256': digest, 'files': rows}, indent=2) + '\n') print(json.dumps({'files': len(rows), 'sourceBytes': total, 'archiveSha256': digest}))