| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- """Pack and verify the locked Kylin ARM64 CPU environment archive."""
- import argparse
- from hashlib import sha256
- from pathlib import Path
- import tarfile
- import zipfile
- from scripts.verify_offline_wheels import verify as verify_wheels
- ROOT = Path(__file__).resolve().parents[1]
- UV_ARCHIVE_SHA256 = 'bb66cb52e7b1823aed1183630d8d8e5c958840d584a4c55ec10a4cfc168dcca2'
- UV_MEMBER = 'uv-aarch64-unknown-linux-gnu/uv'
- def digest(path):
- result = sha256()
- with path.open('rb') as stream:
- for block in iter(lambda: stream.read(1024 * 1024), b''):
- result.update(block)
- return result.hexdigest()
- def pack(source, output):
- source, output = Path(source), Path(output)
- if output.exists():
- raise ValueError('environment_archive_exists')
- requirements = source / 'requirements-arm64-cpu.txt'
- wheelhouse = source / 'wheelhouse'
- verify_wheels(requirements, wheelhouse, ROOT / 'uv.lock')
- uv_archive = source / 'uv-aarch64-unknown-linux-gnu.tar.gz'
- if digest(uv_archive) != UV_ARCHIVE_SHA256:
- raise ValueError('uv_archive_hash_mismatch')
- with tarfile.open(uv_archive, 'r:gz') as archive:
- member = archive.getmember(UV_MEMBER)
- if not member.isfile() or member.size > 100_000_000:
- raise ValueError('invalid_uv_binary')
- uv_bytes = archive.extractfile(member).read()
- if uv_bytes[:4] != b'\x7fELF' or int.from_bytes(uv_bytes[18:20], 'little') != 183:
- raise ValueError('uv_not_aarch64_elf')
- files = {'uv': uv_bytes}
- paths = {
- 'requirements-arm64-cpu.txt': requirements,
- 'install_offline_env.sh': ROOT / 'scripts/install_offline_env.sh',
- }
- paths.update({f'wheelhouse/{path.name}': path for path in sorted(wheelhouse.glob('*.whl'))})
- sums = {name: sha256(content).hexdigest() for name, content in files.items()}
- sums.update({name: digest(path) for name, path in paths.items()})
- checksums = ''.join(f'{value} {name}\n' for name, value in sorted(sums.items()))
- output.parent.mkdir(parents=True, exist_ok=True)
- partial = output.with_name(output.name + '.partial')
- if partial.exists():
- raise ValueError('partial_environment_archive_exists')
- try:
- with zipfile.ZipFile(partial, 'w', allowZip64=True) as archive:
- archive.writestr('uv', uv_bytes, compress_type=zipfile.ZIP_STORED)
- for name, path in sorted(paths.items()):
- compression = zipfile.ZIP_STORED if path.suffix == '.whl' else zipfile.ZIP_DEFLATED
- archive.write(path, name, compress_type=compression)
- archive.writestr('SHA256SUMS', checksums)
- verify(partial)
- partial.replace(output)
- finally:
- partial.unlink(missing_ok=True)
- return len(sums)
- def verify(path):
- with zipfile.ZipFile(path) as archive:
- lines = archive.read('SHA256SUMS').decode('ascii').splitlines()
- expected = {}
- for line in lines:
- checksum, name = line.split(' ', 1)
- if (len(checksum) != 64 or name in expected or name.startswith('/')
- or '..' in Path(name).parts or '\\' in name):
- raise ValueError('invalid_environment_manifest')
- expected[name] = checksum
- if set(archive.namelist()) != set(expected) | {'SHA256SUMS'}:
- raise ValueError('unexpected_environment_members')
- for name, checksum in expected.items():
- with archive.open(name) as stream:
- actual = sha256()
- for block in iter(lambda: stream.read(1024 * 1024), b''):
- actual.update(block)
- if actual.hexdigest() != checksum:
- raise ValueError(f'environment_member_hash_mismatch:{name}')
- return len(expected)
- def main():
- parser = argparse.ArgumentParser(description=__doc__)
- commands = parser.add_subparsers(dest='action', required=True)
- pack_parser = commands.add_parser('pack')
- pack_parser.add_argument('source', type=Path)
- pack_parser.add_argument('output', type=Path)
- verify_parser = commands.add_parser('verify')
- verify_parser.add_argument('archive', type=Path)
- args = parser.parse_args()
- count = pack(args.source, args.output) if args.action == 'pack' else verify(args.archive)
- print(f'Offline ARM64 environment archive verified: {count} files.')
- if __name__ == '__main__':
- main()
|