xref: /aosp_15_r20/external/toolchain-utils/llvm_tools/fetch_cros_sdk_rolls.py (revision 760c253c1ed00ce9abd48f8546f08516e57485fe)
1#!/usr/bin/env python3
2# Copyright 2020 The ChromiumOS Authors
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Gets info about completed chromiumos-sdk runs.
7
8Moreover, this script exists to get versions of published sdk tarballs in
9gs://chromiumos-sdk/. The hope is that it'll help answer the question "when did
10the toolchain ebuild ${x} go live?"
11"""
12
13import argparse
14import json
15import logging
16import os
17from pathlib import Path
18import shutil
19import subprocess
20import sys
21import tempfile
22from typing import Dict, List
23
24
25def fetch_all_sdk_manifest_paths() -> List[str]:
26    """Fetches all paths of SDK manifests; newer = later in the return value."""
27    results = subprocess.run(
28        ["gsutil", "ls", "gs://chromiumos-sdk/cros-sdk-20??.*.Manifest"],
29        check=True,
30        stdout=subprocess.PIPE,
31        encoding="utf-8",
32    ).stdout
33    # These are named so that sorted order == newest last.
34    return sorted(x.strip() for x in results.splitlines())
35
36
37def fetch_manifests_into(into_dir: Path, manifests: List[str]):
38    # Wrap this in a `try` block because gsutil likes to print to stdout *and*
39    # stderr even on success, so we silence them & only print on failure.
40    try:
41        subprocess.run(
42            [
43                "gsutil",
44                "-m",
45                "cp",
46                "-I",
47                str(into_dir),
48            ],
49            check=True,
50            input="\n".join(manifests),
51            stdout=subprocess.PIPE,
52            stderr=subprocess.STDOUT,
53            encoding="utf-8",
54        )
55    except subprocess.CalledProcessError as e:
56        logging.exception("gsutil failed; output:\n%s", e.stdout)
57
58
59def load_manifest_versions(manifest: Path) -> Dict[str, str]:
60    with manifest.open(encoding="utf-8") as f:
61        raw_versions = json.load(f)
62
63    # We get a dict of list of lists of versions and some other metadata, e.g.
64    # {"foo/bar": [["1.2.3", {}]]}
65    # Trim out the metadata.
66    return {k: v[0][0] for k, v in raw_versions["packages"].items()}
67
68
69def main():
70    parser = argparse.ArgumentParser(
71        description=__doc__,
72        formatter_class=argparse.RawDescriptionHelpFormatter,
73    )
74    parser.add_argument(
75        "-d", "--debug", action="store_true", help="Emit debugging output"
76    )
77    parser.add_argument(
78        "-n",
79        "--number",
80        type=int,
81        default=20,
82        help="Number of recent manifests to fetch info about. 0 means "
83        "unlimited.",
84    )
85    args = parser.parse_args()
86
87    is_debug = args.debug
88    logging.basicConfig(level=logging.DEBUG if is_debug else logging.INFO)
89
90    logging.debug("Fetching SDK manifests")
91    manifest_paths = fetch_all_sdk_manifest_paths()
92    logging.debug("%d SDK manifests fetched", len(manifest_paths))
93
94    number = args.number
95    if number:
96        manifest_paths = manifest_paths[-number:]
97
98    tempdir = Path(tempfile.mkdtemp(prefix="cros-sdk-rolls"))
99    try:
100        logging.debug("Working in tempdir %r", tempdir)
101        fetch_manifests_into(tempdir, manifest_paths)
102
103        for path in manifest_paths:
104            basename = os.path.basename(path)
105            versions = load_manifest_versions(tempdir.joinpath(basename))
106            print(f'{basename}: {versions["sys-devel/llvm"]}')
107    finally:
108        if is_debug:
109            logging.debug("Keeping around tempdir %r to aid debugging", tempdir)
110        else:
111            shutil.rmtree(tempdir)
112
113
114if __name__ == "__main__":
115    sys.exit(main())
116