| #!/usr/bin/env python3 |
| # Copyright 2022 The Chromium Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Script to launch the Monorail release tarball builder. |
| |
| It can be used to build a tarball with Monorail code based on a release branch |
| (i.e. `refs/releases/monorail/...`). It triggers a go/monorail-release-tarballs |
| build that uploads the release tarball and triggers its deployment to |
| monorail-dev, after which it can be promoted to monorail-prod. |
| |
| See go/monorail-deploy for more details. |
| """ |
| |
| import argparse |
| import json |
| import subprocess |
| import sys |
| from six.moves.urllib import error |
| from six.moves.urllib import request |
| |
| |
| INFRA_GIT = 'https://chromium.googlesource.com/infra/infra' |
| TARBALL_BUILDER = 'infra-internal/monorail-release/monorail-release-tarballs' |
| |
| |
| def resolve_commit(ref): |
| """Queries gitiles for a commit hash matching the given infra.git ref. |
| |
| Args: |
| ref: a `refs/...` ref to resolve into a commit. |
| |
| Returns: |
| None if there's no such ref, a gitiles commit URL otherwise. |
| """ |
| try: |
| resp = request.urlopen('%s/+/%s?format=JSON' % (INFRA_GIT, ref)) |
| except error.HTTPError as exc: |
| if exc.code == 404: |
| return None |
| raise |
| |
| # Gitiles JSON responses start with XSS-protection header. |
| blob = resp.read() |
| if blob.startswith(b')]}\''): |
| blob = blob[4:] |
| |
| commit = json.loads(blob)['commit'] |
| return '%s/+/%s' % (INFRA_GIT, commit) |
| |
| |
| def ensure_logged_in(): |
| """Ensures `bb` tool is in PATH and the caller is logged in there. |
| |
| Returns: |
| True if logged in, False if not and we should abort. |
| """ |
| try: |
| proc = subprocess.run(['bb', 'auth-info'], capture_output=True) |
| except OSError: |
| print( |
| 'Could not find `bb` tool in PATH. It comes with depot_tools. ' |
| 'Make sure depot_tools is in PATH and up-to-date, then try again.') |
| return False |
| |
| if proc.returncode == 0: |
| return True # already logged in |
| |
| # Launch interactive login process. |
| proc = subprocess.run(['bb', 'auth-login']) |
| if proc.returncode != 0: |
| print('Failed to login') |
| return False |
| return True |
| |
| |
| def submit_build(ref, commit): |
| """Submits a Monorail tarball builder build via `bb` tool. |
| |
| Args: |
| ref: a `refs/...` ref with the code to build. |
| commit: a gitiles commit matching this ref. |
| |
| Returns: |
| None if failed, a URL to the pending build otherwise. |
| """ |
| cmd = ['bb', 'add', '-json', '-ref', ref, '-commit', commit, TARBALL_BUILDER] |
| proc = subprocess.run(cmd, capture_output=True) |
| if proc.returncode != 0: |
| print( |
| 'Failed to schedule the build:\n%s' |
| % proc.stderr.decode('utf-8').strip()) |
| return None |
| build_id = json.loads(proc.stdout)['id'] |
| return 'https://ci.chromium.org/b/%s' % build_id |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| description='Submits a request to build Monorail tarball for LUCI CD.') |
| parser.add_argument( |
| 'branch', type=str, |
| help='a branch to build from: refs/releases/monorail/<num> or just <num>') |
| parser.add_argument( |
| '--silent', action='store_true', |
| help='disable interactive prompts') |
| args = parser.parse_args() |
| |
| ref = args.branch |
| if not ref.startswith('refs/'): |
| ref = 'refs/releases/monorail/' + ref |
| |
| # `bb add` call wants a concrete git commit SHA1 as input. |
| commit = resolve_commit(ref) |
| if not commit: |
| print('No such release branch: %s' % ref) |
| return 1 |
| |
| # Give a chance to confirm this is the commit we want to build. |
| if not args.silent: |
| print( |
| 'Will submit a request to build a Monorail code tarball from %s:\n' |
| ' %s\n\n' |
| 'You may be asked to sign in with your google.com account if it is ' |
| 'the first time you are using this script.\n' |
| % (ref, commit) |
| ) |
| if input('Proceed [Y/n]? ') not in ('', 'Y', 'y'): |
| return 0 |
| |
| # Submit the build via `bb` tool. |
| if not args.silent and not ensure_logged_in(): |
| return 1 |
| build_url = submit_build(ref, commit) |
| if not build_url: |
| return 1 |
| |
| print( |
| '\nScheduled the build: %s\n' |
| '\n' |
| 'When it completes it will trigger deployment of this release to ' |
| 'monorail-dev. You can then promote it to production using the same ' |
| 'procedure as with regular releases built from `main` branch.\n' |
| '\n' |
| 'Note that if the produced release tarball is 100%% identical to any ' |
| 'previously built tarball (e.g. there were no cherry-picks into the ' |
| 'release branch since it was cut from `main`), an existing tarball and ' |
| 'its version name will be reused.' |
| % build_url |
| ) |
| return 0 |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |