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