| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright 2015 The Chromium Authors. All rights reserved. | 2 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 """Rolls DEPS controlled dependency. | 6 """Rolls DEPS controlled dependency. |
| 7 | 7 |
| 8 Works only with git checkout and git dependencies. | 8 Works only with git checkout and git dependencies. |
| 9 """ | 9 """ |
| 10 | 10 |
| 11 import optparse | 11 import optparse |
| 12 import os | 12 import os |
| 13 import re | 13 import re |
| 14 import subprocess | 14 import subprocess |
| 15 import sys | 15 import sys |
| 16 | 16 |
| 17 | 17 |
| 18 NEED_SHELL = sys.platform.startswith('win') |
| 19 |
| 20 |
| 21 def check_output(*args, **kwargs): |
| 22 """subprocess.check_output() passing shell=True on Windows for git.""" |
| 23 kwargs.setdefault('shell', NEED_SHELL) |
| 24 return subprocess.check_output(*args, **kwargs) |
| 25 |
| 26 |
| 27 def check_call(*args, **kwargs): |
| 28 """subprocess.check_call() passing shell=True on Windows for git.""" |
| 29 kwargs.setdefault('shell', NEED_SHELL) |
| 30 subprocess.check_call(*args, **kwargs) |
| 31 |
| 32 |
| 18 def is_pristine(root, merge_base='origin/master'): | 33 def is_pristine(root, merge_base='origin/master'): |
| 19 """Returns True if a git checkout is pristine.""" | 34 """Returns True if a git checkout is pristine.""" |
| 20 cmd = ['git', 'diff', '--ignore-submodules', merge_base] | 35 cmd = ['git', 'diff', '--ignore-submodules', merge_base] |
| 21 return not ( | 36 return not (check_output(cmd, cwd=root).strip() or |
| 22 subprocess.check_output(cmd, cwd=root).strip() or | 37 check_output(cmd + ['--cached'], cwd=root).strip()) |
| 23 subprocess.check_output(cmd + ['--cached'], cwd=root).strip()) | |
| 24 | 38 |
| 25 | 39 |
| 26 def roll(root, deps_dir, key, reviewers, bug): | 40 def roll(root, deps_dir, key, reviewers, bug): |
| 27 deps = os.path.join(root, 'DEPS') | 41 deps = os.path.join(root, 'DEPS') |
| 28 try: | 42 try: |
| 29 with open(deps, 'rb') as f: | 43 with open(deps, 'rb') as f: |
| 30 deps_content = f.read() | 44 deps_content = f.read() |
| 31 except OSError: | 45 except OSError: |
| 32 print >> sys.stderr, ( | 46 print >> sys.stderr, ( |
| 33 'Ensure the script is run in the directory containing DEPS file.') | 47 'Ensure the script is run in the directory containing DEPS file.') |
| 34 return 1 | 48 return 1 |
| 35 | 49 |
| 36 if not is_pristine(root): | 50 if not is_pristine(root): |
| 37 print >> sys.stderr, 'Ensure %s is clean first.' % root | 51 print >> sys.stderr, 'Ensure %s is clean first.' % root |
| 38 return 1 | 52 return 1 |
| 39 | 53 |
| 40 full_dir = os.path.join(os.path.dirname(root), deps_dir) | 54 full_dir = os.path.normpath(os.path.join(os.path.dirname(root), deps_dir)) |
| 41 head = subprocess.check_output( | 55 head = check_output( |
| 42 ['git', 'rev-parse', 'HEAD'], cwd=full_dir).strip() | 56 ['git', 'rev-parse', 'HEAD'], cwd=full_dir).strip() |
| 43 | 57 |
| 44 if not head in deps_content: | 58 if not head in deps_content: |
| 45 print('Warning: %s is not checked out at the expected revision in DEPS' % | 59 print('Warning: %s is not checked out at the expected revision in DEPS' % |
| 46 deps_dir) | 60 deps_dir) |
| 47 # It happens if the user checked out a branch in the dependency by himself. | 61 # It happens if the user checked out a branch in the dependency by himself. |
| 48 # Fall back to reading the DEPS to figure out the original commit. | 62 # Fall back to reading the DEPS to figure out the original commit. |
| 49 for i in deps_content.splitlines(): | 63 for i in deps_content.splitlines(): |
| 50 m = re.match(r'\s+"' + key + '": "([a-z0-9]{40})",', i) | 64 m = re.match(r'\s+"' + key + '": "([a-z0-9]{40})",', i) |
| 51 if m: | 65 if m: |
| 52 head = m.group(1) | 66 head = m.group(1) |
| 53 break | 67 break |
| 54 else: | 68 else: |
| 55 print >> sys.stderr, 'Expected to find commit %s for %s in DEPS' % ( | 69 print >> sys.stderr, 'Expected to find commit %s for %s in DEPS' % ( |
| 56 head, key) | 70 head, key) |
| 57 return 1 | 71 return 1 |
| 58 | 72 |
| 59 print('Found old revision %s' % head) | 73 print('Found old revision %s' % head) |
| 60 | 74 |
| 61 subprocess.check_call(['git', 'fetch', 'origin'], cwd=full_dir) | 75 check_call(['git', 'fetch', 'origin'], cwd=full_dir) |
| 62 master = subprocess.check_output( | 76 master = check_output( |
| 63 ['git', 'rev-parse', 'origin/master'], cwd=full_dir).strip() | 77 ['git', 'rev-parse', 'origin/master'], cwd=full_dir).strip() |
| 64 print('Found new revision %s' % master) | 78 print('Found new revision %s' % master) |
| 65 | 79 |
| 66 if master == head: | 80 if master == head: |
| 67 print('No revision to roll!') | 81 print('No revision to roll!') |
| 68 return 1 | 82 return 1 |
| 69 | 83 |
| 70 commit_range = '%s..%s' % (head[:9], master[:9]) | 84 commit_range = '%s..%s' % (head[:9], master[:9]) |
| 71 | 85 |
| 72 logs = subprocess.check_output( | 86 logs = check_output( |
| 73 ['git', 'log', commit_range, '--date=short', '--format=%ad %ae %s'], | 87 ['git', 'log', commit_range, '--date=short', '--format=%ad %ae %s'], |
| 74 cwd=full_dir).strip() | 88 cwd=full_dir).strip() |
| 75 logs = re.sub(r'(?m)^(\d\d\d\d-\d\d-\d\d [^@]+)@[^ ]+( .*)$', r'\1\2', logs) | 89 logs = re.sub(r'(?m)^(\d\d\d\d-\d\d-\d\d [^@]+)@[^ ]+( .*)$', r'\1\2', logs) |
| 76 cmd = 'git log %s --date=short --format=\'%%ad %%ae %%s\'' % commit_range | 90 cmd = 'git log %s --date=short --format=\'%%ad %%ae %%s\'' % commit_range |
| 77 reviewer = 'R=%s\n' % ','.join(reviewers) if reviewers else '' | 91 reviewer = 'R=%s\n' % ','.join(reviewers) if reviewers else '' |
| 78 bug = 'BUG=%s\n' % bug if bug else '' | 92 bug = 'BUG=%s\n' % bug if bug else '' |
| 79 msg = ( | 93 msg = ( |
| 80 'Roll %s/ to %s.\n' | 94 'Roll %s/ to %s.\n' |
| 81 '\n' | 95 '\n' |
| 82 '$ %s\n' | 96 '$ %s\n' |
| 83 '%s\n\n' | 97 '%s\n\n' |
| 84 '%s' | 98 '%s' |
| 85 '%s') % ( | 99 '%s') % ( |
| 86 deps_dir, | 100 deps_dir, |
| 87 master, | 101 master, |
| 88 cmd, | 102 cmd, |
| 89 logs, | 103 logs, |
| 90 reviewer, | 104 reviewer, |
| 91 bug) | 105 bug) |
| 92 | 106 |
| 93 print('Commit message:') | 107 print('Commit message:') |
| 94 print('\n'.join(' ' + i for i in msg.splitlines())) | 108 print('\n'.join(' ' + i for i in msg.splitlines())) |
| 95 deps_content = deps_content.replace(head, master) | 109 deps_content = deps_content.replace(head, master) |
| 96 with open(deps, 'wb') as f: | 110 with open(deps, 'wb') as f: |
| 97 f.write(deps_content) | 111 f.write(deps_content) |
| 98 subprocess.check_call(['git', 'add', 'DEPS'], cwd=root) | 112 check_call(['git', 'add', 'DEPS'], cwd=root) |
| 99 subprocess.check_call(['git', 'commit', '-m', msg], cwd=root) | 113 check_call(['git', 'commit', '-m', msg], cwd=root) |
| 100 print('') | 114 print('') |
| 101 if not reviewers: | 115 if not reviewers: |
| 102 print('You forgot to pass -r, make sure to insert a R=foo@example.com line') | 116 print('You forgot to pass -r, make sure to insert a R=foo@example.com line') |
| 103 print('to the commit description before emailing.') | 117 print('to the commit description before emailing.') |
| 104 print('') | 118 print('') |
| 105 print('Run:') | 119 print('Run:') |
| 106 print(' git cl upload --send-mail') | 120 print(' git cl upload --send-mail') |
| 107 return 0 | 121 return 0 |
| 108 | 122 |
| 109 | 123 |
| 110 def main(): | 124 def main(): |
| 111 parser = optparse.OptionParser( | 125 parser = optparse.OptionParser( |
| 112 description=sys.modules[__name__].__doc__, | 126 description=sys.modules[__name__].__doc__, |
| 113 usage='roll-dep [flags] <dependency path> <variable>') | 127 usage='roll-dep [flags] <dependency path> <variable>') |
| 114 parser.add_option( | 128 parser.add_option( |
| 115 '-r', '--reviewer', default='', | 129 '-r', '--reviewer', default='', |
| 116 help='To specify multiple reviewers, use comma separated list, e.g. ' | 130 help='To specify multiple reviewers, use comma separated list, e.g. ' |
| 117 '-r joe,jack,john. Defaults to @chromium.org') | 131 '-r joe,jane,john. Defaults to @chromium.org') |
| 118 parser.add_option('-b', '--bug', default='') | 132 parser.add_option('-b', '--bug', default='') |
| 119 options, args = parser.parse_args() | 133 options, args = parser.parse_args() |
| 120 if not len(args) or len(args) > 2: | 134 if not len(args) or len(args) > 2: |
| 121 parser.error('Expect one or two arguments' % args) | 135 parser.error('Expect one or two arguments' % args) |
| 122 | 136 |
| 123 reviewers = None | 137 reviewers = None |
| 124 if options.reviewer: | 138 if options.reviewer: |
| 125 reviewers = options.reviewer.split(',') | 139 reviewers = options.reviewer.split(',') |
| 126 for i, r in enumerate(reviewers): | 140 for i, r in enumerate(reviewers): |
| 127 if not '@' in r: | 141 if not '@' in r: |
| 128 reviewers[i] = r + '@chromium.org' | 142 reviewers[i] = r + '@chromium.org' |
| 129 | 143 |
| 130 return roll( | 144 return roll( |
| 131 os.getcwd(), | 145 os.getcwd(), |
| 132 args[0], | 146 args[0], |
| 133 args[1] if len(args) > 1 else None, | 147 args[1] if len(args) > 1 else None, |
| 134 reviewers, | 148 reviewers, |
| 135 options.bug) | 149 options.bug) |
| 136 | 150 |
| 137 | 151 |
| 138 if __name__ == '__main__': | 152 if __name__ == '__main__': |
| 139 sys.exit(main()) | 153 sys.exit(main()) |
| OLD | NEW |