52 lines
2.6 KiB
Python
52 lines
2.6 KiB
Python
"""Offline archive and Git-source verification. Does not run application/toolchain code."""
|
|
from pathlib import Path
|
|
import argparse, hashlib, json, subprocess
|
|
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--git-ref', help='Also compare all archived files with this committed Git tree (for example HEAD).')
|
|
args = parser.parse_args()
|
|
archive = Path(__file__).resolve().parent
|
|
root = archive.parents[2]
|
|
manifest = json.loads((archive / 'manifest.json').read_bytes())
|
|
def sha(raw): return hashlib.sha256(raw).hexdigest()
|
|
def batch(specs):
|
|
data = subprocess.check_output(['git','-c','core.longpaths=true','cat-file','--batch'], cwd=root,
|
|
input=('\n'.join(specs)+'\n').encode())
|
|
offset=0; result=[]
|
|
for spec in specs:
|
|
end=data.index(b'\n',offset); parts=data[offset:end].split()
|
|
assert len(parts)==3 and parts[1]==b'blob',spec
|
|
size=int(parts[2]); result.append(data[end+1:end+1+size]);offset=end+size+2
|
|
assert offset==len(data)
|
|
return result
|
|
paths=[]
|
|
for item in manifest['files']:
|
|
path=(root/item['path']).resolve()
|
|
assert path.is_relative_to(root) and path.is_file()
|
|
raw=path.read_bytes()
|
|
assert len(raw)==item['bytes'] and sha(raw)==item['sha256'],item['path']
|
|
paths.append(item['path'])
|
|
assert len(paths)==len(set(paths))
|
|
payload={p.relative_to(root).as_posix() for p in archive.rglob('*') if p.is_file()}
|
|
assert payload=={p for p in paths if p.startswith(archive.relative_to(root).as_posix()+'/')}|{(archive/'manifest.json').relative_to(root).as_posix()}
|
|
inputs={}
|
|
for rel in manifest['input_manifests']:
|
|
m=json.loads((root/rel).read_bytes()); assert m['source']==manifest['tested_source']
|
|
for entry in m['inputs']:
|
|
assert inputs.setdefault(entry['path'],entry)==entry
|
|
assert len(inputs)==615
|
|
ordered=sorted(inputs)
|
|
for source in (manifest['tested_source'],manifest['publication_base']):
|
|
raw_blobs=batch([source+':'+p for p in ordered])
|
|
for path,raw in zip(ordered,raw_blobs):
|
|
item=inputs[path]
|
|
assert len(raw)==item['bytes'] and sha(raw)==item['sha256'],(source,path)
|
|
if args.git_ref:
|
|
committed=paths+[(archive/'manifest.json').relative_to(root).as_posix()]
|
|
for path,raw in zip(committed,batch([args.git_ref+':'+p for p in committed])):
|
|
assert (root/path).read_bytes()==raw,path
|
|
print(json.dumps({'verified_payload_files':len(paths),'verified_source_inputs':len(inputs),
|
|
'tested_source':manifest['tested_source'],'git_ref':args.git_ref,
|
|
'manifest_sha256':sha((archive/'manifest.json').read_bytes()),
|
|
'scope':'Byte and Git source verification; does not rerun tests or establish native/package acceptance.'},indent=2))
|