#!/usr/bin/python3 """Vendor PyCA cryptography's Rust crates """ import argparse import os import re import tarfile import tempfile import shutil import subprocess import sys VENDOR_DIR = "vendor" CARGO_TOML = "src/rust/Cargo.toml" RE_VERSION = re.compile(r"Version:\s*(.*)") parser = argparse.ArgumentParser(description="Vendor Rust packages") parser.add_argument( "--spec", default="python-cryptography.spec", help="cryptography source tar bundle" ) def cargo(cmd, manifest): args = ["cargo", cmd, f"--manifest-path={manifest}"] return subprocess.check_call( args, stdout=subprocess.DEVNULL, stderr=sys.stderr, env={} ) def tar_reset(tarinfo): """Reset user, group, mtime, and mode to create reproducible tar""" tarinfo.uid = 0 tarinfo.gid = 0 tarinfo.uname = "root" tarinfo.gname = "root" tarinfo.mtime = 0 if tarinfo.type == tarfile.DIRTYPE: tarinfo.mode = 0o755 else: tarinfo.mode = 0o644 if tarinfo.pax_headers: raise ValueError(tarinfo.name, tarinfo.pax_headers) return tarinfo def tar_reproducible(tar, basedir): """Create reproducible tar file""" content = [basedir] for root, dirs, files in os.walk(basedir): for directory in dirs: content.append(os.path.join(root, directory)) for filename in files: content.append(os.path.join(root, filename)) content.sort() for fn in content: tar.add(fn, filter=tar_reset, recursive=False, arcname=fn) def main(): args = parser.parse_args() spec = args.spec # change cwd to work in bundle directory here = os.path.dirname(os.path.abspath(spec)) os.chdir(here) # extract version number from bundle name with open(spec) as f: for line in f: mo = RE_VERSION.search(line) if mo is not None: version = mo.group(1) break else: raise ValueError(f"Cannot find version in {spec}") bundle_file = f"cryptography-{version}.tar.gz" vendor_file = f"cryptography-{version}-vendor.tar.bz2" # remove existing vendor directory and file if os.path.isdir(VENDOR_DIR): shutil.rmtree(VENDOR_DIR) try: os.unlink(vendor_file) except FileNotFoundError: pass print(f"Getting crates for {bundle_file}", file=sys.stderr) # extract tar file in tempdir # fetch and vendor Rust crates with tempfile.TemporaryDirectory(dir=here) as tmp: with tarfile.open(bundle_file) as tar: tar.extractall(path=tmp) manifest = os.path.join(tmp, f"cryptography-{version}", CARGO_TOML) cargo("fetch", manifest) cargo("vendor", manifest) print("\nCreating tar ball...", file=sys.stderr) with tarfile.open(vendor_file, "x:bz2") as tar: tar_reproducible(tar, VENDOR_DIR) # remove vendor dir shutil.rmtree(VENDOR_DIR) parser.exit(0, f"Created {vendor_file}\n") if __name__ == "__main__": main()