blob: 6bfcd2ae9a0903ae56b887479dfe11103d7c3d6a [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2021 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# based on an almost identical script by: jyrki@google.com (Jyrki Alakuijala)
"""Updates the commit message used in the auto-roller.
Merges several small commit logs into a single more useful commit message.
Usage:
update_commit_message.py --old-revision=<sha1>
"""
import argparse
import logging
import os
import platform
import re
import shutil
import subprocess
import sys
import tempfile
GCLIENT_LINE = r'([^:]+): ([^@]+)@(.*)'
CHANGE_TEMPLATE = '* %s: %s.git/+log/%s..%s'
EXIT_SUCCESS = 0
EXIT_FAILURE = 1
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
GCLIENT = """\
solutions = [{
'name': '.',
'url': 'https://chromium.googlesource.com/vulkan-deps.git',
'deps_file': 'DEPS',
'managed': False,
}]
"""
INSERT_NEEDLE = 'If this roll has caused a breakage'
def run(cmd, args):
exe = ('%s.bat' % cmd) if platform.system() == 'Windows' else cmd
cmd_args = [exe] + list(args)
return subprocess.check_output(cmd_args).decode('ascii').strip()
def git(*args):
return run('git', args)
def gclient(*args):
return run('gclient', args)
def parse_revinfo(output):
expr = re.compile(GCLIENT_LINE)
config = dict()
urls = dict()
for line in output.split('\n'):
match = expr.match(line.strip())
if match:
dep = match.group(1)
urls[dep] = match.group(2)
config[dep] = match.group(3)
return config, urls
def _local_commit_amend(commit_msg, dry_run):
logging.info('Amending changes to local commit.')
old_commit_msg = git('log', '-1', '--pretty=%B')
logging.debug('Existing commit message:\n%s\n', old_commit_msg)
insert_index = old_commit_msg.rfind(INSERT_NEEDLE)
if insert_index == -1:
logging.exception('"%s" not found in commit message.' % INSERT_NEEDLE)
new_commit_msg = old_commit_msg[:insert_index] + commit_msg + '\n\n' + old_commit_msg[insert_index:]
logging.debug('New commit message:\n%s\n', new_commit_msg)
if not dry_run:
with tempfile.NamedTemporaryFile(delete=False, mode="w") as ntf:
ntf.write(new_commit_msg)
ntf.close()
git('commit', '--amend', '--no-edit', '--file=%s' % ntf.name)
os.unlink(ntf.name)
def main(raw_args):
parser = argparse.ArgumentParser()
parser.add_argument('--old-revision', help='Old git revision in the roll.', required=True)
parser.add_argument(
'--dry-run',
help='Test out functionality without making changes.',
action='store_true',
default=False)
parser.add_argument(
'-v', '--verbose', help='Verbose debug logging.', action='store_true', default=False)
args = parser.parse_args(raw_args)
if args.verbose:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.INFO)
cwd = os.getcwd()
os.chdir(SCRIPT_DIR)
old_deps_content = git('show', '%s:DEPS' % args.old_revision)
with tempfile.TemporaryDirectory() as tempdir:
os.chdir(tempdir)
# Add the gclientfile.
with open(os.path.join(tempdir, '.gclient'), 'w') as gcfile:
gcfile.write(GCLIENT)
gcfile.close()
# Get the current config.
shutil.copyfile(os.path.join(SCRIPT_DIR, 'DEPS'), os.path.join(tempdir, 'DEPS'))
gclient_head_output = gclient('revinfo')
# Get the prior config.
with open('DEPS', 'w') as deps:
deps.write(old_deps_content)
deps.close()
gclient_old_output = gclient('revinfo')
os.chdir(SCRIPT_DIR)
head_config, urls = parse_revinfo(gclient_head_output)
old_config, _ = parse_revinfo(gclient_old_output)
changed_deps = []
for dep, new_sha1 in head_config.items():
if dep in old_config:
old_sha1 = old_config[dep]
if new_sha1 != old_sha1:
dep_short = dep.replace('\\', '/').split('/')[0]
repo = urls[dep]
logging.debug('Found change: %s to %s' % (dep, new_sha1))
changed_deps.append(CHANGE_TEMPLATE %
(dep_short, repo, old_sha1[:10], new_sha1[:10]))
if not changed_deps:
print('No changed dependencies, early exit.')
return EXIT_SUCCESS
commit_msg = 'Changed dependencies:\n%s' % '\n'.join(sorted(changed_deps))
os.chdir(cwd)
_local_commit_amend(commit_msg, args.dry_run)
return EXIT_SUCCESS
if __name__ == '__main__':
sys.exit(main(sys.argv[1:]))