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 NEED_SHELL = sys.platform.startswith('win') |
17 | 18 |
18 NEED_SHELL = sys.platform.startswith('win') | 19 |
| 20 class Error(Exception): |
| 21 pass |
19 | 22 |
20 | 23 |
21 def check_output(*args, **kwargs): | 24 def check_output(*args, **kwargs): |
22 """subprocess.check_output() passing shell=True on Windows for git.""" | 25 """subprocess.check_output() passing shell=True on Windows for git.""" |
23 kwargs.setdefault('shell', NEED_SHELL) | 26 kwargs.setdefault('shell', NEED_SHELL) |
24 return subprocess.check_output(*args, **kwargs) | 27 return subprocess.check_output(*args, **kwargs) |
25 | 28 |
26 | 29 |
27 def check_call(*args, **kwargs): | 30 def check_call(*args, **kwargs): |
28 """subprocess.check_call() passing shell=True on Windows for git.""" | 31 """subprocess.check_call() passing shell=True on Windows for git.""" |
29 kwargs.setdefault('shell', NEED_SHELL) | 32 kwargs.setdefault('shell', NEED_SHELL) |
30 subprocess.check_call(*args, **kwargs) | 33 subprocess.check_call(*args, **kwargs) |
31 | 34 |
32 | 35 |
33 def is_pristine(root, merge_base='origin/master'): | 36 def is_pristine(root, merge_base='origin/master'): |
34 """Returns True if a git checkout is pristine.""" | 37 """Returns True if a git checkout is pristine.""" |
35 cmd = ['git', 'diff', '--ignore-submodules', merge_base] | 38 cmd = ['git', 'diff', '--ignore-submodules', merge_base] |
36 return not (check_output(cmd, cwd=root).strip() or | 39 return not (check_output(cmd, cwd=root).strip() or |
37 check_output(cmd + ['--cached'], cwd=root).strip()) | 40 check_output(cmd + ['--cached'], cwd=root).strip()) |
38 | 41 |
39 | 42 |
40 def roll(root, deps_dir, key, reviewers, bug): | 43 def roll(root, deps_dir, key, reviewers, bug): |
41 deps = os.path.join(root, 'DEPS') | 44 deps = os.path.join(root, 'DEPS') |
42 try: | 45 try: |
43 with open(deps, 'rb') as f: | 46 with open(deps, 'rb') as f: |
44 deps_content = f.read() | 47 deps_content = f.read() |
45 except (IOError, OSError): | 48 except (IOError, OSError): |
46 print >> sys.stderr, ( | 49 raise Error('Ensure the script is run in the directory ' |
47 'Ensure the script is run in the directory containing DEPS file.') | 50 'containing DEPS file.') |
48 return 1 | |
49 | 51 |
50 if not is_pristine(root): | 52 if not is_pristine(root): |
51 print >> sys.stderr, 'Ensure %s is clean first.' % root | 53 raise Error('Ensure %s is clean first.' % root) |
52 return 1 | |
53 | 54 |
54 full_dir = os.path.normpath(os.path.join(os.path.dirname(root), deps_dir)) | 55 full_dir = os.path.normpath(os.path.join(os.path.dirname(root), deps_dir)) |
55 head = check_output( | 56 head = check_output(['git', 'rev-parse', 'HEAD'], cwd=full_dir).strip() |
56 ['git', 'rev-parse', 'HEAD'], cwd=full_dir).strip() | |
57 | 57 |
58 if not head in deps_content: | 58 if not head in deps_content: |
59 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' % |
60 deps_dir) | 60 deps_dir) |
| 61 if key is None: |
| 62 print("Warning: no key specified. Using '%s'." % deps_dir) |
| 63 key = deps_dir |
| 64 |
61 # It happens if the user checked out a branch in the dependency by himself. | 65 # It happens if the user checked out a branch in the dependency by himself. |
62 # Fall back to reading the DEPS to figure out the original commit. | 66 # Fall back to reading the DEPS to figure out the original commit. |
63 for i in deps_content.splitlines(): | 67 for i in deps_content.splitlines(): |
64 m = re.match(r'\s+"' + key + '": "([a-z0-9]{40})",', i) | 68 m = re.match(r'\s+"' + key + '": "([a-z0-9]{40})",', i) |
65 if m: | 69 if m: |
66 head = m.group(1) | 70 head = m.group(1) |
67 break | 71 break |
68 else: | 72 else: |
69 print >> sys.stderr, 'Expected to find commit %s for %s in DEPS' % ( | 73 raise Error('Expected to find commit %s for %s in DEPS' % (head, key)) |
70 head, key) | |
71 return 1 | |
72 | 74 |
73 print('Found old revision %s' % head) | 75 print('Found old revision %s' % head) |
74 | 76 |
75 check_call(['git', 'fetch', 'origin'], cwd=full_dir) | 77 check_call(['git', 'fetch', 'origin'], cwd=full_dir) |
76 master = check_output( | 78 master = check_output( |
77 ['git', 'rev-parse', 'origin/master'], cwd=full_dir).strip() | 79 ['git', 'rev-parse', 'origin/master'], cwd=full_dir).strip() |
78 print('Found new revision %s' % master) | 80 print('Found new revision %s' % master) |
79 | 81 |
80 if master == head: | 82 if master == head: |
81 print('No revision to roll!') | 83 raise Error('No revision to roll!') |
82 return 1 | |
83 | 84 |
84 commit_range = '%s..%s' % (head[:9], master[:9]) | 85 commit_range = '%s..%s' % (head[:9], master[:9]) |
85 | 86 |
86 logs = check_output( | 87 logs = check_output( |
87 ['git', 'log', commit_range, '--date=short', '--format=%ad %ae %s'], | 88 ['git', 'log', commit_range, '--date=short', '--format=%ad %ae %s'], |
88 cwd=full_dir).strip() | 89 cwd=full_dir).strip() |
89 logs = re.sub(r'(?m)^(\d\d\d\d-\d\d-\d\d [^@]+)@[^ ]+( .*)$', r'\1\2', logs) | 90 logs = re.sub(r'(?m)^(\d\d\d\d-\d\d-\d\d [^@]+)@[^ ]+( .*)$', r'\1\2', logs) |
90 cmd = 'git log %s --date=short --format=\'%%ad %%ae %%s\'' % commit_range | 91 cmd = 'git log %s --date=short --format=\'%%ad %%ae %%s\'' % commit_range |
91 reviewer = 'R=%s\n' % ','.join(reviewers) if reviewers else '' | 92 reviewer = 'R=%s\n' % ','.join(reviewers) if reviewers else '' |
92 bug = 'BUG=%s\n' % bug if bug else '' | 93 bug = 'BUG=%s\n' % bug if bug else '' |
(...skipping 18 matching lines...) Expand all Loading... |
111 f.write(deps_content) | 112 f.write(deps_content) |
112 check_call(['git', 'add', 'DEPS'], cwd=root) | 113 check_call(['git', 'add', 'DEPS'], cwd=root) |
113 check_call(['git', 'commit', '-m', msg], cwd=root) | 114 check_call(['git', 'commit', '-m', msg], cwd=root) |
114 print('') | 115 print('') |
115 if not reviewers: | 116 if not reviewers: |
116 print('You forgot to pass -r, make sure to insert a R=foo@example.com line') | 117 print('You forgot to pass -r, make sure to insert a R=foo@example.com line') |
117 print('to the commit description before emailing.') | 118 print('to the commit description before emailing.') |
118 print('') | 119 print('') |
119 print('Run:') | 120 print('Run:') |
120 print(' git cl upload --send-mail') | 121 print(' git cl upload --send-mail') |
121 return 0 | |
122 | 122 |
123 | 123 |
124 def main(): | 124 def main(): |
125 parser = optparse.OptionParser( | 125 parser = optparse.OptionParser( |
126 description=sys.modules[__name__].__doc__, | 126 description=sys.modules[__name__].__doc__, |
127 usage='roll-dep [flags] <dependency path> <variable>') | 127 usage='roll-dep [flags] <dependency path> <variable>') |
128 parser.add_option( | 128 parser.add_option( |
129 '-r', '--reviewer', default='', | 129 '-r', '--reviewer', default='', |
130 help='To specify multiple reviewers, use comma separated list, e.g. ' | 130 help='To specify multiple reviewers, use comma separated list, e.g. ' |
131 '-r joe,jane,john. Defaults to @chromium.org') | 131 '-r joe,jane,john. Defaults to @chromium.org') |
132 parser.add_option('-b', '--bug', default='') | 132 parser.add_option('-b', '--bug', default='') |
133 options, args = parser.parse_args() | 133 options, args = parser.parse_args() |
134 if not len(args) or len(args) > 2: | 134 if not len(args) or len(args) > 2: |
135 parser.error('Expect one or two arguments' % args) | 135 parser.error('Expect one or two arguments' % args) |
136 | 136 |
137 reviewers = None | 137 reviewers = None |
138 if options.reviewer: | 138 if options.reviewer: |
139 reviewers = options.reviewer.split(',') | 139 reviewers = options.reviewer.split(',') |
140 for i, r in enumerate(reviewers): | 140 for i, r in enumerate(reviewers): |
141 if not '@' in r: | 141 if not '@' in r: |
142 reviewers[i] = r + '@chromium.org' | 142 reviewers[i] = r + '@chromium.org' |
143 | 143 |
144 return roll( | 144 try: |
145 os.getcwd(), | 145 roll( |
146 args[0], | 146 os.getcwd(), |
147 args[1] if len(args) > 1 else None, | 147 deps_dir=args[0], |
148 reviewers, | 148 key=args[1] if len(args) > 1 else None, |
149 options.bug) | 149 reviewers=reviewers, |
| 150 bug=options.bug) |
| 151 |
| 152 except Error as e: |
| 153 sys.stderr.write('error: %s\n' % e) |
| 154 return 1 |
| 155 |
| 156 return 0 |
150 | 157 |
151 | 158 |
152 if __name__ == '__main__': | 159 if __name__ == '__main__': |
153 sys.exit(main()) | 160 sys.exit(main()) |
OLD | NEW |