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