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