113 lines
7.0 KiB
Python
113 lines
7.0 KiB
Python
#!/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()
|