From c3c0cd4ee46dc16ee54e199c078f4a37845fc4d6 Mon Sep 17 00:00:00 2001 From: Haibo Huang <hhb@google.com> Date: Tue, 29 Jan 2019 15:24:45 -0800 Subject: [PATCH] [Updater] Optionally commit and upload change Test: ./updater.sh update --force --branch_and_commit --push_change googletest Test: aosp/889542 Change-Id: I1025e2ded65000ce34e2f02f84052e7871e425d3 --- external_updater.py | 58 ++++++++++++++++++++++++++++++++++++++------- git_updater.py | 3 ++- git_utils.py | 44 +++++++++++++++++++++++++++++++--- 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/external_updater.py b/external_updater.py index 0ee2e86..bc464ac 100644 --- a/external_updater.py +++ b/external_updater.py @@ -26,9 +26,10 @@ import time from google.protobuf import text_format # pylint: disable=import-error -import fileutils from git_updater import GitUpdater from github_archive_updater import GithubArchiveUpdater +import fileutils +import git_utils import updater_utils @@ -79,6 +80,11 @@ def has_new_version(updater): return updater.get_current_version() != updater.get_latest_version() +def _message_for_calledprocesserror(error): + return '\n'.join([error.stdout.decode('utf-8'), + error.stderr.decode('utf-8')]) + + def check_update(proj_path): """Checks updates for a project. Prints result on console. @@ -104,11 +110,8 @@ def check_update(proj_path): err)) return (updater, str(err)) except subprocess.CalledProcessError as err: - msg = 'stdout: {}\nstderr: {}.'.format( - err.stdout, - err.stderr) - print('{} {}.'.format(color_string('Failed.', 'ERROR'), - msg)) + msg = _message_for_calledprocesserror(err) + print('{}\n{}'.format(msg, color_string('Failed.', 'ERROR'))) return (updater, msg) @@ -157,12 +160,42 @@ def check(args): def update(args): """Handler for update command.""" + try: + _do_update(args) + except subprocess.CalledProcessError as err: + msg = _message_for_calledprocesserror(err) + print('{}\n{}'.format(msg, color_string('Failed to upgrade.', 'ERROR'))) + + +TMP_BRANCH_NAME = 'tmp_auto_upgrade' + +def _do_update(args): updater, err = check_update(args.path) if updater is None: return - if has_new_version(updater) or args.force: - updater.update() + if not has_new_version(updater) and not args.force: + return + + full_path = fileutils.get_absolute_project_path(args.path) + if args.branch_and_commit: + git_utils.checkout(full_path, args.remote_name + '/master') + try: + git_utils.delete_branch(full_path, TMP_BRANCH_NAME) + except subprocess.CalledProcessError as err: + # Still continue if the branch doesn't exist. + pass + git_utils.start_branch(full_path, TMP_BRANCH_NAME) + + updater.update() + + if args.branch_and_commit: + msg = 'Upgrade {} to {}\n\nTest: None'.format( + args.path, updater.get_latest_version()) + git_utils.commit(full_path, msg) + + if args.push_change: + git_utils.push(full_path, args.remote_name) def parse_args(): @@ -201,6 +234,15 @@ def parse_args(): '--force', help='Run update even if there\'s no new version.', action='store_true') + update_parser.add_argument( + '--branch_and_commit', action='store_true', + help='Starts a new branch and commit changes.') + update_parser.add_argument( + '--push_change', action='store_true', + help='Pushes change to Gerrit.') + update_parser.add_argument( + '--remote_name', default='aosp', required=False, + help='Upstream remote name.') update_parser.set_defaults(func=update) return parser.parse_args() diff --git a/git_updater.py b/git_updater.py index 81ef922..81ee93d 100644 --- a/git_updater.py +++ b/git_updater.py @@ -135,7 +135,8 @@ class GitUpdater(): print('{} is {} commits behind of {}.'.format( self.merge_from, len(commits), upstream_branch)) - self._write_metadata(self.proj_path) print("Running `git merge {merge_branch}`..." .format(merge_branch=self.merge_from)) git_utils.merge(self.proj_path, self.merge_from) + self._write_metadata(self.proj_path) + git_utils.add_file(self.proj_path, 'METADATA') diff --git a/git_utils.py b/git_utils.py index 67f4500..7b78b21 100644 --- a/git_utils.py +++ b/git_utils.py @@ -18,9 +18,10 @@ import re import subprocess -def _run(cmd, cwd): +def _run(cmd, cwd, redirect=True): """Runs a command with stdout and stderr redirected.""" - return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, + out = subprocess.PIPE if redirect else None + return subprocess.run(cmd, stdout=out, stderr=out, check=True, cwd=cwd) @@ -117,4 +118,41 @@ def is_commit(commit): def merge(proj_path, branch): """Merges a branch.""" - _run(['git', 'merge', branch], cwd=proj_path) + try: + out = _run(['git', 'merge', branch, '--no-commit'], + cwd=proj_path) + except subprocess.CalledProcessError: + # Merge failed. Error is already written to console. + subprocess.run(['git', 'merge', '--abort'], cwd=proj_path) + raise + + +def add_file(proj_path, file_name): + """Stages a file.""" + _run(['git', 'add', file_name], cwd=proj_path) + + +def delete_branch(proj_path, branch_name): + """Force delete a branch.""" + _run(['git', 'branch', '-D', branch_name], cwd=proj_path) + + +def start_branch(proj_path, branch_name): + """Starts a new repo branch.""" + _run(['repo', 'start', branch_name], cwd=proj_path) + + +def commit(proj_path, message): + """Commits changes.""" + _run(['git', 'commit', '-m', message], cwd=proj_path) + + +def checkout(proj_path, branch_name): + """Checkouts a branch.""" + _run(['git', 'checkout', branch_name], cwd=proj_path) + + +def push(proj_path, remote_name): + """Pushes change to remote.""" + return _run(['git', 'push', remote_name, 'HEAD:refs/for/master'], + cwd=proj_path, redirect=False) -- GitLab