| #!/usr/bin/env fuchsia-vendored-python |
| # Copyright 2023 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import argparse |
| import zipfile |
| import subprocess |
| import os |
| import shutil |
| import tempfile |
| |
| |
| def zip_dir(dir, zip_file): |
| for root, _dirs, files in os.walk(dir): |
| for file in files: |
| path = os.path.join(root, file) |
| zip_file.write(path, os.path.relpath(path, dir)) |
| |
| |
| # rmtree manually removes all subdirectories and files instead of using |
| # shutil.rmtree, to avoid registering spurious reads on stale |
| # subdirectories. See https://fxbug.dev/74084. |
| def rmtree(dir): |
| if not os.path.exists(dir): |
| return |
| for root, dirs, files in os.walk(dir, topdown=False): |
| for file in files: |
| os.unlink(os.path.join(root, file)) |
| for dir in dirs: |
| full_path = os.path.join(root, dir) |
| if os.path.islink(full_path): |
| os.unlink(full_path) |
| else: |
| os.rmdir(full_path) |
| |
| |
| def prepare_dirs(repo_dir): |
| path = os.path.join(repo_dir, "repository") |
| os.makedirs(path) |
| |
| return {"root": repo_dir, "repository": path} |
| |
| |
| # `package-tool repository publish` expects the following inputs in its in/out directory: |
| # - `keys/{snapshot|targets|timestamp}.json` containing private metadata keys; |
| # - `repository/{{version-num}}.root.json` containing versioned root metadata; |
| # - `repository/root.json` containing default root metadata. |
| def prepare_publish(args, dirs): |
| for root_metadata_path in args.root_metadata: |
| shutil.copy(root_metadata_path, dirs["repository"]) |
| shutil.copy( |
| args.default_root_metadata, |
| "{}/{}".format(dirs["repository"], "root.json"), |
| ) |
| |
| |
| def package_tool_publish(args, dirs, depfile): |
| cmd_args = [ |
| args.package_tool, |
| "repository", |
| "publish", |
| "--trusted-keys", |
| args.trusted_keys, |
| "--trusted-root", |
| "{}/{}".format(dirs["repository"], "root.json"), |
| "--package-list", |
| args.input, |
| "--depfile", |
| args.depfile, |
| ] |
| |
| if args.delivery_blob_type: |
| cmd_args.extend(["--delivery-blob-type", args.delivery_blob_type]) |
| |
| cmd_args.append(dirs["root"]) |
| |
| subprocess.run(cmd_args, check=True) |
| |
| |
| def main(args): |
| with tempfile.TemporaryDirectory( |
| dir=os.path.dirname(args.output) |
| ) as gendir: |
| dirs = prepare_dirs(gendir) |
| |
| # Prepare for `package-tool repository publish` and gather deps associated with preparations. |
| prepare_publish(args, dirs) |
| |
| depfile = os.path.join(gendir, "deps") |
| |
| # Invoke `package-tool repository publish` and gather deps associated with invocation. |
| package_tool_publish(args, dirs, depfile) |
| |
| # Output repository directory to zip file. |
| with zipfile.ZipFile( |
| args.output, "w", zipfile.ZIP_DEFLATED |
| ) as zip_file: |
| zip_dir(dirs["repository"], zip_file) |
| |
| |
| if __name__ == "__main__": |
| parser = argparse.ArgumentParser( |
| "Creates a zip archive of the TUF repository output by `package-tool repository publish`" |
| ) |
| parser.add_argument( |
| "--package-tool", |
| help="path to the package-tool executable", |
| required=True, |
| ) |
| parser.add_argument( |
| "--trusted-keys", |
| help="path to a keys directory to be consumed by `package-tool repository publish`", |
| ) |
| parser.add_argument( |
| "--root-metadata", |
| help="path to a root metadata file to be used in the TUF repository", |
| action="append", |
| ) |
| parser.add_argument( |
| "--default-root-metadata", |
| help="path to the default TUF root metadata file", |
| required=True, |
| ) |
| parser.add_argument( |
| "--input", |
| help="path to `package-tool repository publish` file input", |
| required=True, |
| ) |
| parser.add_argument( |
| "--delivery-blob-type", help="the type of delivery blob to generate" |
| ) |
| parser.add_argument("--depfile", help="generate a depfile", required=True) |
| parser.add_argument("--output", help="path output zip file", required=True) |
| main(parser.parse_args()) |