blob: 1f0f22935a7106f30595cac1c59df69e2cbeb3cf [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2018 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.
"""Commits files to the chromium git repository."""
from __future__ import print_function
import os
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 CommitError(Exception):
"""Raised if the commit failed."""
class ChromeCommitter(object):
"""Committer object responsible for committing a git change."""
def __init__(self, args):
self._checkout_dir = args.workdir
self._dryrun = args.dryrun
self._git_committer_args = ['-c', 'user.email=%s' % args.user_email,
'-c', 'user.name=%s' % args.user_email]
self._commit_msg = ''
logging.info('user_email=%s', args.user_email)
logging.info('checkout_dir=%s', args.workdir)
def __del__(self):
self.Cleanup()
def FullPath(self, file_path):
"""Returns the full path in the source tree given a relative path.
Args:
file_path: Path of file.
Returns:
Full path rooted in source checkout.
"""
if os.path.isabs(file_path):
return file_path
return os.path.join(self._checkout_dir, file_path)
def Checkout(self, sparse_checkout):
"""Checks out chrome into tmp checkout dir.
Args:
sparse_checkout: List of file paths to fetch.
"""
assert isinstance(sparse_checkout, list)
sparse_checkout += ['codereview.settings', 'WATCHLISTS']
git.ShallowFetch(self._checkout_dir, constants.CHROMIUM_GOB_URL,
sparse_checkout=sparse_checkout)
git.CreateBranch(self._checkout_dir, 'auto-commit-branch',
branch_point='origin/master')
def Commit(self, file_paths, commit_msg):
"""Commits files listed in |file_paths|.
Args:
file_paths: List of files to commit.
commit_msg: Message to use in commit.
"""
assert file_paths and isinstance(file_paths, list)
# Make paths absolute and ensure they exist.
for i, file_path in enumerate(file_paths):
if not os.path.isabs(file_path):
file_paths[i] = self.FullPath(file_path)
if not os.path.exists(file_paths[i]):
raise CommitError('Invalid path: %s' % file_paths[i])
self._commit_msg = 'Automated Commit: ' + commit_msg
try:
for file_path in file_paths:
git.AddPath(file_path)
commit_args = ['commit', '-m', self._commit_msg]
git.RunGit(self._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 CommitError('Could not create git commit: %r' % e)
def Upload(self):
"""Uploads the change to gerrit."""
logging.info('Uploading 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.
git.RunGit(self._checkout_dir, ['--no-pager', 'log', '--pretty=full'],
capture_output=False)
raise CommitError('Could not submit: %r' % e)
logging.info('Submitted to CQ.')
def Cleanup(self):
"""Remove chrome checkout."""
osutils.RmDir(self._checkout_dir, ignore_missing=True)
@staticmethod
def GetParser():
"""Returns parser for ChromeCommitter.
Returns:
Dictionary of parsed command line args.
"""
# We need to use the account used by the builder to upload git CLs when
# generating CLs.
default_git_account = None
if cros_build_lib.HostIsCIBuilder(golo_only=True):
default_git_account = 'chromeos-commit-bot@chromium.org'
elif cros_build_lib.HostIsCIBuilder(gce_only=True):
default_git_account = '3su6n15k.default@developer.gserviceaccount.com'
parser = commandline.ArgumentParser(usage=__doc__, add_help=False)
parser.add_argument('--dryrun', action='store_true', default=False,
help='Don\'t commit changes or send out emails.')
parser.add_argument('--user_email', required=False,
default=default_git_account,
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