initial commit

This commit is contained in:
2026-09-21 13:41:40 +09:00
commit 855c7328df
411 changed files with 85352 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""Repackage the fixed Ubuntu v2 install with pinned qpdf notices, without rebuilding."""
from pathlib import Path
import hashlib
import json
import os
import subprocess
import sys
import tarfile
import time
HOME = Path('/home/docview')
ROOT = Path(__file__).resolve().parents[2]
OUTPUT = HOME / 'validation/qpdf-notice-package'
PRIOR_SOURCE = HOME / 'docview-context-source'
INSTALL = HOME / 'docview-context-install'
VERSION = '0.1.0~validation5'
def sha(path):
with path.open('rb') as stream:
return hashlib.file_digest(stream, 'sha256').hexdigest()
def main():
assert Path.home() == HOME and os.getuid() == 1000
assert ROOT == HOME / 'validation/qpdf-notice-tools'
OUTPUT.mkdir(parents=True, exist_ok=False)
sys.path.insert(0, str(ROOT / 'tools'))
from verify_ubuntu_package import inspect, verify, MANIFEST
from collect_ubuntu_validation_runtime import QPDF_NOTICE_PIN
env = {**os.environ, 'PATH': '/opt/docview-deps/bin:/opt/docview-qt/6.11.2/gcc_64/bin:' + os.environ['PATH'],
'LD_LIBRARY_PATH': '/opt/docview-deps/lib:/opt/docview-qt/6.11.2/gcc_64/lib'}
for key in ('LD_PRELOAD', 'LD_AUDIT', 'QTWEBENGINE_DISABLE_SANDBOX'):
env.pop(key, None)
preserved = [INSTALL/'bin'/name for name in ('docview','docview-pdf-worker','docview-archive-worker')]
preserved += [INSTALL/'lib/libpdfium.so', HOME/'validation/pdfium-context-prefix/BUILDINFO.json',
Path('/opt/docview-deps/lib/libqpdf.so.30')]
before = {str(p):sha(p) for p in preserved}
report = {'success': False, 'version': VERSION, 'scope': 'Notice collection and packaging only; no C++ rebuild or repeated full CTest',
'runtimeBefore': before, 'runnerSha256': sha(Path(__file__)), 'commands': []}
def run(name, command):
start = time.monotonic()
with (OUTPUT/(name+'.log')).open('w') as log:
result = subprocess.run(command, env=env, stdout=log, stderr=subprocess.STDOUT, timeout=900)
report['commands'].append({'name':name, 'command':command, 'exitCode':result.returncode,
'seconds':time.monotonic()-start, 'logSha256':sha(OUTPUT/(name+'.log'))})
assert result.returncode == 0, name
try:
for name in ('test_ubuntu_validation_runtime','test_qpdf_notice_package','test_pdfium_candidate_integration'):
run(name,[sys.executable,str(ROOT/'tests'/(name+'.py')),'-v'])
command = [sys.executable,str(ROOT/'tools/package_ubuntu.py'),'--prefix',str(INSTALL),
'--qtpaths','/opt/docview-qt/6.11.2/gcc_64/bin/qtpaths','--dependency-prefix','/opt/docview-deps',
'--source-root',str(HOME/'dependency-sources/qpdf-12.4.1'),
'--source-root',str(HOME/'dependency-sources/libzip-1.11.4'),
'--qpdf-source-archive',str(HOME/'incoming/qpdf-12.4.1.tar.gz'),
'--input-manifest',str(HOME/'incoming/sdk-downloads.json'),
'--sdk-notices',str(PRIOR_SOURCE/'tests/results/source-archives/qt-sdk-notices-final'),
'--sdk-supplement',str(PRIOR_SOURCE/'tests/results/qt-notice-supplement/collection'),
'--qt-licenses',str(PRIOR_SOURCE/'tests/results/ubuntu-package/inputs/qt-licenses'), '--version',VERSION]
archives = []
for name in ('package','repeat'):
run(name,[*command,'--output',str(OUTPUT/name)])
archive = OUTPUT/name/('docview_'+VERSION+'_amd64.deb'); archives.append(archive)
run(name+'-verify',[sys.executable,str(ROOT/'tools/verify_ubuntu_package.py'),'--archive',str(archive),
'--output',str(OUTPUT/name/'verification.json')])
assert sha(archives[0]) == sha(archives[1])
prior = HOME/'validation/pdfium-context-package/package/package/docview_0.1.0~validation4_amd64.deb'
assert sha(prior) == 'bd4f00912078d406cb466cd6910ed01c0c9fdd32b76cb2f47b74cfb9719f1bd9'
def inventory(path):
data, _, _ = inspect(path, '--fsys-tarfile')
control, _, _ = inspect(path, '--ctrl-tarfile')
return {**data, **{'DEBIAN/'+name:dict(row,path='DEBIAN/'+name) for name,row in control.items()}}
old, new = inventory(prior), inventory(archives[0])
differences = {'added':[new[k] for k in sorted(new.keys()-old.keys())],
'removed':[old[k] for k in sorted(old.keys()-new.keys())],
'changed':[{'path':k,'before':old[k],'after':new[k]} for k in sorted(old.keys()&new.keys()) if old[k]!=new[k]]}
protected = lambda k: not k.startswith(('DEBIAN/','opt/docview/share/doc/docview/'))
protected_paths = {k for k in old.keys()|new.keys() if protected(k)}
unchanged = all(old.get(k)==new.get(k) for k in protected_paths)
assert unchanged, 'Runtime or application payload changed'
diff = {'success': True, 'method':'Bounded raw data/control inventory; old validation4 is not accepted by the current notice verifier',
'oldArchiveSha256':sha(prior),'newArchiveSha256':sha(archives[0]),'oldFiles':len(old),'newFiles':len(new),
'allRuntimeAndApplicationFilesUnchanged':unchanged,'protectedFileCount':len(protected_paths),**differences}
(OUTPUT/'payload-difference.json').write_text(json.dumps(diff,indent=2)+'\n')
result = verify(archives[0])
notice = next(row for row in result['qpdfNoticeCorrespondence']['notices'] if row['source']=='NOTICE.md')
wanted = './opt/docview/share/doc/docview/third-party/runtime/'+notice['copiedPath']
process = subprocess.Popen(['dpkg-deb','--fsys-tarfile',str(archives[0])],stdout=subprocess.PIPE)
found = None
with tarfile.open(fileobj=process.stdout,mode='r|') as tar:
for member in tar:
if member.name == wanted:
assert member.isfile() and member.size==QPDF_NOTICE_PIN['notices']['NOTICE.md']['size']
found = tar.extractfile(member).read()
process.stdout.close(); assert process.wait(timeout=15)==0
assert found is not None and hashlib.sha256(found).hexdigest()==QPDF_NOTICE_PIN['notices']['NOTICE.md']['sha256']
assert found==(HOME/'dependency-sources/qpdf-12.4.1/NOTICE.md').read_bytes()
(OUTPUT/'NOTICE.from-deb.md').write_bytes(found)
report.update(success=True, archive=str(archives[0]),archiveSha256=sha(archives[0]),
repeatSha256=sha(archives[1]),archiveBytes=archives[0].stat().st_size,
qpdfNoticeCorrespondence=result['qpdfNoticeCorrespondence'],pdfiumCorrespondence=result['pdfiumCorrespondence'],
actualNoticeBytesMatchSelectedSource=True, actualPayloadCompared=True)
finally:
report['runtimeAfter']={str(p):sha(p) for p in preserved}
report['runtimeUnchanged']=report['runtimeBefore']==report['runtimeAfter']
report['success']=report['success'] and report['runtimeUnchanged']
(OUTPUT/'report.json').write_text(json.dumps(report,indent=2)+'\n')
print(json.dumps({k:report[k] for k in ('success','archiveSha256','archiveBytes','runtimeUnchanged')}))
if __name__=='__main__': main()