blob: 86542889f389aed9b4a784f85d0d92c0b6d00520 [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2017 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Update the CHROMEOS_LKGM file in a chromium repository.
Note: this borrows heavily from cros_best_revision.py. TODO(stevenjb) Remove
cros_best_revision.py once this replaces it and the Chrome LKGM builder is
turned down.
"""
from __future__ import print_function
import distutils.version
import os
from chromite.cbuildbot import manifest_version
from chromite.lib import constants
from chromite.lib import commandline
from chromite.lib import cros_build_lib
from chromite.lib import cros_logging as logging
from chromite.lib import git
from chromite.lib import osutils
from chromite.lib import tree_status
class LKGMNotValid(Exception):
"""Raised if the LKGM version is unset or not newer than the current value."""
class LKGMNotCommitted(Exception):
"""Raised if we could not submit a new LKGM."""
class ChromeLKGMCommitter(object):
"""Committer object responsible for obtaining a new LKGM and committing it."""
_COMMIT_MSG_TEMPLATE = ('Automated Commit: New LKGM version '
'%(version)s for chromeos.\n\nBUG=762641')
def __init__(self, checkout_dir, lkgm, dryrun, user_email):
self._checkout_dir = checkout_dir
# Strip any chrome branch from the lkgm version.
self._lkgm = manifest_version.VersionInfo(lkgm).VersionString()
self._dryrun = dryrun
self._git_committer_args = ['-c', 'user.email=%s' % user_email,
'-c', 'user.name=%s' % user_email]
self._commit_msg = ''
self._old_lkgm = None
logging.info('lkgm=%s', lkgm)
logging.info('user_email=%s', user_email)
logging.info('checkout_dir=%s', checkout_dir)
def Run(self):
try:
self.CheckoutChromeLKGM()
self.CommitNewLKGM()
self.UploadNewLKGM()
except LKGMNotCommitted as e:
# TODO(stevenjb): Do not catch exceptions (i.e. fail) once this works.
logging.warning('LKGM Commit failed: %r' % e)
finally:
self.Cleanup()
def CheckoutChromeLKGM(self):
"""Checkout CHROMEOS_LKGM file for chrome into tmp checkout dir."""
# TODO(stevenjb): if checkout_dir exists, use 'fetch --depth 1' and
# 'checkout -f origin/master' instead of 'clone --depth 1'.
self.Cleanup()
cros_build_lib.RunCommand(
['git', 'clone', '--depth', '1', constants.CHROMIUM_GOB_URL,
self._checkout_dir])
cros_build_lib.RunCommand(
['git', 'branch', '-D', 'lkgm-roll'], cwd=self._checkout_dir,
error_code_ok=True)
cros_build_lib.RunCommand(
['git', 'checkout', '-b', 'lkgm-roll', 'origin/master'],
cwd=self._checkout_dir)
self._old_lkgm = osutils.ReadFile(
os.path.join(self._checkout_dir, constants.PATH_TO_CHROME_LKGM))
def CommitNewLKGM(self):
"""Commits the new LKGM file using our template commit message."""
if not self._lkgm:
raise LKGMNotValid('LKGM not provided.')
lv = distutils.version.LooseVersion
if self._old_lkgm is not None and not lv(self._lkgm) > lv(self._old_lkgm):
raise LKGMNotValid(
'LKGM version (%s) is not newer than current version (%s).' %
(self._lkgm, self._old_lkgm))
logging.info('Committing LKGM version: %s (was %s),',
self._lkgm, self._old_lkgm)
self._commit_msg = self._COMMIT_MSG_TEMPLATE % dict(version=self._lkgm)
checkout_dir = self._checkout_dir
try:
# Overwrite the lkgm file and commit it.
file_path = os.path.join(checkout_dir, constants.PATH_TO_CHROME_LKGM)
osutils.WriteFile(file_path, self._lkgm)
git.AddPath(file_path)
commit_args = ['commit', '-m', self._commit_msg]
git.RunGit(checkout_dir, self._git_committer_args + commit_args,
print_cmd=True, redirect_stderr=True, capture_output=False)
except cros_build_lib.RunCommandError as e:
raise LKGMNotCommitted(
'Could not create git commit with new LKGM: %r' % e)
def UploadNewLKGM(self):
"""Uploads the change to gerrit."""
logging.info('Uploading LKGM commit.')
try:
# Run 'git cl upload' with --bypass-hooks to skip running scripts that are
# not part of the shallow checkout, -f to skip editing the CL message,
upload_args = ['cl', 'upload', '-v', '-m', self._commit_msg,
'--bypass-hooks', '-f']
if not self._dryrun:
# Add the gardener(s) as TBR; fall-back to tbr-owners.
gardeners = tree_status.GetSheriffEmailAddresses('chrome')
if gardeners:
for tbr in gardeners:
upload_args += ['--tbrs', tbr]
else:
upload_args += ['--tbr-owners']
# Marks CL as ready.
upload_args += ['--send-mail']
git.RunGit(self._checkout_dir, self._git_committer_args + upload_args,
print_cmd=True, redirect_stderr=True, capture_output=False)
# Flip the CQ commit bit.
submit_args = ['cl', 'set-commit', '-v']
if self._dryrun:
submit_args += ['--dry-run']
git.RunGit(self._checkout_dir, submit_args,
print_cmd=True, redirect_stderr=True, capture_output=False)
except cros_build_lib.RunCommandError as e:
# Log the change for debugging.
cros_build_lib.RunCommand(['git', 'log', '--pretty=full'],
cwd=self._checkout_dir)
raise LKGMNotCommitted('Could not submit LKGM: %r' % e)
logging.info('LKGM submitted to CQ.')
def Cleanup(self):
"""Remove chrome checkout."""
osutils.RmDir(self._checkout_dir, ignore_missing=True)
def GetArgs(argv):
"""Returns parsed command line args.
Args:
argv: command line.
"""
parser = commandline.ArgumentParser(usage=__doc__, caching=True)
parser.add_argument('--dryrun', action='store_true', default=False,
help="Find the next LKGM but don't commit it.")
parser.add_argument('--lkgm', required=True,
help="LKGM version to update to.")
parser.add_argument('--user_email', required=False,
default='chromeos-commit-bot@chromium.org',
help="Email address to use when comitting changes.")
parser.add_argument('--workdir',
default=os.path.join(os.getcwd(), 'chrome_src'),
help=('Path to a checkout of the chrome src. '
'Defaults to PWD/chrome_src'))
return parser.parse_args(argv)
def main(argv):
args = GetArgs(argv)
committer = ChromeLKGMCommitter(args.workdir, lkgm=args.lkgm,
dryrun=args.dryrun,
user_email=args.user_email)
committer.Run()
return 0