Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(342)

Side by Side Diff: roll_dep.py

Issue 431003002: Add comment with svn revision number. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2014 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2014 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 """This scripts takes the path to a dep and an svn revision, and updates the 6 """This scripts takes the path to a dep and an svn revision, and updates the
7 parent repo's DEPS file with the corresponding git revision. Sample invocation: 7 parent repo's DEPS file with the corresponding git revision. Sample invocation:
8 8
9 [chromium/src]$ roll-dep third_party/WebKit 12345 9 [chromium/src]$ roll-dep third_party/WebKit 12345
10 10
11 After the script completes, the DEPS file will be dirty with the new revision. 11 After the script completes, the DEPS file will be dirty with the new revision.
12 The user can then: 12 The user can then:
13 13
14 $ git add DEPS 14 $ git add DEPS
15 $ git commit 15 $ git commit
16 """ 16 """
17 17
18 import ast 18 import ast
19 import os 19 import os
20 import re 20 import re
21 import sys 21 import sys
22 22
23 from itertools import izip 23 from itertools import izip
24 from subprocess import Popen, PIPE 24 from subprocess import Popen, PIPE
25 from textwrap import dedent 25 from textwrap import dedent
26 26
27 27
28 SHA1_RE = re.compile('^[a-fA-F0-9]{40}$')
29 GIT_SVN_ID_RE = re.compile('^git-svn-id: .*@([0-9]+) .*$')
30
31
28 def posix_path(path): 32 def posix_path(path):
29 """Convert a possibly-Windows path to a posix-style path.""" 33 """Convert a possibly-Windows path to a posix-style path."""
30 return re.sub('^[A-Z]:', '', path.replace(os.sep, '/')) 34 (_, path) = os.path.splitdrive(path)
35 return path.replace(os.sep, '/')
31 36
32 37
33 def platform_path(path): 38 def platform_path(path):
34 """Convert a path to the native path format of the host OS.""" 39 """Convert a path to the native path format of the host OS."""
35 return path.replace('/', os.sep) 40 return path.replace('/', os.sep)
36 41
37 42
38 def find_gclient_root(): 43 def find_gclient_root():
39 """Find the directory containing the .gclient file.""" 44 """Find the directory containing the .gclient file."""
40 cwd = posix_path(os.getcwd()) 45 cwd = posix_path(os.getcwd())
(...skipping 19 matching lines...) Expand all
60 cwd.startswith(os.path.join(gclient_root, soln_relpath))): 65 cwd.startswith(os.path.join(gclient_root, soln_relpath))):
61 return soln 66 return soln
62 assert False, 'Could not determine the parent project for %s' % dep_path 67 assert False, 'Could not determine the parent project for %s' % dep_path
63 68
64 69
65 def verify_git_revision(dep_path, revision): 70 def verify_git_revision(dep_path, revision):
66 """Verify that a git revision exists in a repository.""" 71 """Verify that a git revision exists in a repository."""
67 p = Popen(['git', 'rev-list', '-n', '1', revision], 72 p = Popen(['git', 'rev-list', '-n', '1', revision],
68 cwd=dep_path, stdout=PIPE, stderr=PIPE) 73 cwd=dep_path, stdout=PIPE, stderr=PIPE)
69 result = p.communicate()[0].strip() 74 result = p.communicate()[0].strip()
70 if p.returncode != 0 or not re.match('^[a-fA-F0-9]{40}$', result): 75 if p.returncode != 0 or not SHA1_RE.match(result):
71 result = None 76 result = None
72 return result 77 return result
73 78
74 79
80 def get_svn_revision(dep_path, git_revision):
81 """Given a git revision, return the corresponding svn revision."""
82 p = Popen(['git', 'log', '-n', '1', '--pretty=format:%B', git_revision],
83 stdout=PIPE, cwd=dep_path)
84 (log, _) = p.communicate()
85 assert p.returncode == 0, 'git log %s failed.' % git_revision
86 for line in reversed(log.splitlines()):
87 m = GIT_SVN_ID_RE.match(line.strip())
88 if m:
89 return m.group(1)
90 return None
91
92
75 def convert_svn_revision(dep_path, revision): 93 def convert_svn_revision(dep_path, revision):
76 """Find the git revision corresponding to an svn revision.""" 94 """Find the git revision corresponding to an svn revision."""
77 err_msg = 'Unknown error' 95 err_msg = 'Unknown error'
78 revision = int(revision) 96 revision = int(revision)
79 with open(os.devnull, 'w') as devnull: 97 with open(os.devnull, 'w') as devnull:
80 for ref in ('HEAD', 'origin/master'): 98 for ref in ('HEAD', 'origin/master'):
81 try: 99 try:
82 log_p = Popen(['git', 'log', ref], 100 log_p = Popen(['git', 'log', ref],
83 cwd=dep_path, stdout=PIPE, stderr=devnull) 101 cwd=dep_path, stdout=PIPE, stderr=devnull)
84 grep_p = Popen(['grep', '-e', '^commit ', '-e', '^ *git-svn-id: '], 102 grep_p = Popen(['grep', '-e', '^commit ', '-e', '^ *git-svn-id: '],
(...skipping 23 matching lines...) Expand all
108 'latest available revision is %d; you may need to ' 126 'latest available revision is %d; you may need to '
109 '"git fetch origin" to get the latest commits.' % svn_rev) 127 '"git fetch origin" to get the latest commits.' % svn_rev)
110 finally: 128 finally:
111 log_p.terminate() 129 log_p.terminate()
112 grep_p.terminate() 130 grep_p.terminate()
113 raise RuntimeError('No match for revision %d; %s' % (revision, err_msg)) 131 raise RuntimeError('No match for revision %d; %s' % (revision, err_msg))
114 132
115 133
116 def get_git_revision(dep_path, revision): 134 def get_git_revision(dep_path, revision):
117 """Convert the revision argument passed to the script to a git revision.""" 135 """Convert the revision argument passed to the script to a git revision."""
136 svn_revision = None
118 if revision.startswith('r'): 137 if revision.startswith('r'):
119 result = convert_svn_revision(dep_path, revision[1:]) 138 git_revision = convert_svn_revision(dep_path, revision[1:])
139 svn_revision = revision[1:]
120 elif re.search('[a-fA-F]', revision): 140 elif re.search('[a-fA-F]', revision):
121 result = verify_git_revision(dep_path, revision) 141 git_revision = verify_git_revision(dep_path, revision)
142 svn_revision = get_svn_revision(dep_path, git_revision)
122 elif len(revision) > 6: 143 elif len(revision) > 6:
123 result = verify_git_revision(dep_path, revision) 144 git_revision = verify_git_revision(dep_path, revision)
124 if not result: 145 if git_revision:
125 result = convert_svn_revision(dep_path, revision) 146 svn_revision = get_svn_revision(dep_path, git_revision)
147 else:
148 git_revision = convert_svn_revision(dep_path, revision)
149 svn_revision = revision
126 else: 150 else:
127 try: 151 try:
128 result = convert_svn_revision(dep_path, revision) 152 git_revision = convert_svn_revision(dep_path, revision)
153 svn_revision = revision
129 except RuntimeError: 154 except RuntimeError:
130 result = verify_git_revision(dep_path, revision) 155 git_revision = verify_git_revision(dep_path, revision)
131 if not result: 156 if not git_revision:
132 raise 157 raise
133 return result 158 svn_revision = get_svn_revision(dep_path, git_revision)
159 return git_revision, svn_revision
134 160
135 161
136 def ast_err_msg(node): 162 def ast_err_msg(node):
137 return 'ERROR: Undexpected DEPS file AST structure at line %d column %d' % ( 163 return 'ERROR: Undexpected DEPS file AST structure at line %d column %d' % (
138 node.lineno, node.col_offset) 164 node.lineno, node.col_offset)
139 165
140 166
141 def find_deps_section(deps_ast, section): 167 def find_deps_section(deps_ast, section):
142 """Find a top-level section of the DEPS file in the AST.""" 168 """Find a top-level section of the DEPS file in the AST."""
143 try: 169 try:
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
179 line = deps_lines[line_idx] 205 line = deps_lines[line_idx]
180 (prefix, sep, old_rev) = string_node.s.partition('@') 206 (prefix, sep, old_rev) = string_node.s.partition('@')
181 if sep: 207 if sep:
182 start_idx = line.find(prefix + sep, start_idx) + len(prefix + sep) 208 start_idx = line.find(prefix + sep, start_idx) + len(prefix + sep)
183 tail_idx = start_idx + len(old_rev) 209 tail_idx = start_idx + len(old_rev)
184 else: 210 else:
185 start_idx = line.find(prefix, start_idx) 211 start_idx = line.find(prefix, start_idx)
186 tail_idx = start_idx + len(prefix) 212 tail_idx = start_idx + len(prefix)
187 old_rev = prefix 213 old_rev = prefix
188 deps_lines[line_idx] = line[:start_idx] + git_revision + line[tail_idx:] 214 deps_lines[line_idx] = line[:start_idx] + git_revision + line[tail_idx:]
189 return old_rev 215 return line_idx
190 216
191 217
192 def update_binop(deps_lines, deps_ast, binop_node, git_revision): 218 def update_binop(deps_lines, deps_ast, binop_node, git_revision):
193 """Update a binary operation node in the AST with the new git revision.""" 219 """Update a binary operation node in the AST with the new git revision."""
194 # Since the revision part is always last, assume that it's the right-hand 220 # Since the revision part is always last, assume that it's the right-hand
195 # operand that needs to be updated. 221 # operand that needs to be updated.
196 return update_node(deps_lines, deps_ast, binop_node.right, git_revision) 222 return update_node(deps_lines, deps_ast, binop_node.right, git_revision)
197 223
198 224
199 def update_call(deps_lines, deps_ast, call_node, git_revision): 225 def update_call(deps_lines, deps_ast, call_node, git_revision):
(...skipping 22 matching lines...) Expand all
222 if url.endswith('.git'): 248 if url.endswith('.git'):
223 url = url[:-4] 249 url = url[:-4]
224 url += '/+log/%s..%s' % (old_rev[:12], new_rev[:12]) 250 url += '/+log/%s..%s' % (old_rev[:12], new_rev[:12])
225 return dedent('''\ 251 return dedent('''\
226 Rolled %s 252 Rolled %s
227 from revision %s 253 from revision %s
228 to revision %s 254 to revision %s
229 Summary of changes available at: 255 Summary of changes available at:
230 %s\n''' % (dep_name, old_rev, new_rev, url)) 256 %s\n''' % (dep_name, old_rev, new_rev, url))
231 257
232 def update_deps(soln_path, dep_name, new_rev): 258 def update_deps_entry(deps_lines, deps_ast, value_node, new_rev, comment):
259 line_idx = update_node(deps_lines, deps_ast, value_node, new_rev)
260 (content, _, _) = deps_lines[line_idx].partition('#')
261 if comment:
262 deps_lines[line_idx] = '%s # %s' % (content.rstrip(), comment)
263 else:
264 deps_lines[line_idx] = content.rstrip()
265
266 def update_deps(soln_path, dep_name, new_rev, comment):
233 """Update the DEPS file with the new git revision.""" 267 """Update the DEPS file with the new git revision."""
234 commit_msg = '' 268 commit_msg = ''
235 deps_file = os.path.join(soln_path, 'DEPS') 269 deps_file = os.path.join(soln_path, 'DEPS')
236 with open(deps_file) as fh: 270 with open(deps_file) as fh:
237 deps_content = fh.read() 271 deps_content = fh.read()
238 deps_locals = {} 272 deps_locals = {}
239 def _Var(key): 273 def _Var(key):
240 return deps_locals['vars'][key] 274 return deps_locals['vars'][key]
241 deps_locals['Var'] = _Var 275 deps_locals['Var'] = _Var
242 exec deps_content in {}, deps_locals 276 exec deps_content in {}, deps_locals
243 deps_lines = deps_content.splitlines() 277 deps_lines = deps_content.splitlines()
244 deps_ast = ast.parse(deps_content, deps_file) 278 deps_ast = ast.parse(deps_content, deps_file)
245 deps_node = find_deps_section(deps_ast, 'deps') 279 deps_node = find_deps_section(deps_ast, 'deps')
246 assert deps_node, 'Could not find "deps" section of DEPS file' 280 assert deps_node, 'Could not find "deps" section of DEPS file'
247 dep_idx = find_dict_index(deps_node, dep_name) 281 dep_idx = find_dict_index(deps_node, dep_name)
248 if dep_idx is not None: 282 if dep_idx is not None:
249 value_node = deps_node.values[dep_idx] 283 value_node = deps_node.values[dep_idx]
250 update_node(deps_lines, deps_ast, value_node, new_rev) 284 update_deps_entry(deps_lines, deps_ast, value_node, new_rev, comment)
251 commit_msg = generate_commit_message(deps_locals['deps'], dep_name, new_rev) 285 commit_msg = generate_commit_message(deps_locals['deps'], dep_name, new_rev)
252 deps_os_node = find_deps_section(deps_ast, 'deps_os') 286 deps_os_node = find_deps_section(deps_ast, 'deps_os')
253 if deps_os_node: 287 if deps_os_node:
254 for (os_name, os_node) in izip(deps_os_node.keys, deps_os_node.values): 288 for (os_name, os_node) in izip(deps_os_node.keys, deps_os_node.values):
255 dep_idx = find_dict_index(os_node, dep_name) 289 dep_idx = find_dict_index(os_node, dep_name)
256 if dep_idx is not None: 290 if dep_idx is not None:
257 value_node = os_node.values[dep_idx] 291 value_node = os_node.values[dep_idx]
258 if value_node.__class__ is ast.Name and value_node.id == 'None': 292 if value_node.__class__ is ast.Name and value_node.id == 'None':
259 pass 293 pass
260 else: 294 else:
261 update_node(deps_lines, deps_ast, value_node, new_rev) 295 update_deps_entry(deps_lines, deps_ast, value_node, new_rev, comment)
262 commit_msg = generate_commit_message( 296 commit_msg = generate_commit_message(
263 deps_locals['deps_os'][os_name], dep_name, new_rev) 297 deps_locals['deps_os'][os_name], dep_name, new_rev)
264 if commit_msg: 298 if commit_msg:
265 print 'Pinning %s' % dep_name 299 print 'Pinning %s' % dep_name
266 print 'to revision %s' % new_rev 300 print 'to revision %s' % new_rev
267 print 'in %s' % deps_file 301 print 'in %s' % deps_file
268 with open(deps_file, 'w') as fh: 302 with open(deps_file, 'w') as fh:
269 for line in deps_lines: 303 for line in deps_lines:
270 print >> fh, line 304 print >> fh, line
271 with open(os.path.join(soln_path, '.git', 'MERGE_MSG'), 'a') as fh: 305 with open(os.path.join(soln_path, '.git', 'MERGE_MSG'), 'a') as fh:
272 fh.write(commit_msg) 306 fh.write(commit_msg)
273 else: 307 else:
274 print 'Could not find an entry in %s to update.' % deps_file 308 print 'Could not find an entry in %s to update.' % deps_file
275 return 0 if commit_msg else 1 309 return 0 if commit_msg else 1
276 310
277 311
278 def main(argv): 312 def main(argv):
279 if len(argv) != 2 : 313 if len(argv) != 2 :
280 print >> sys.stderr, 'Usage: roll_dep.py <dep path> <svn revision>' 314 print >> sys.stderr, 'Usage: roll_dep.py <dep path> <svn revision>'
281 return 1 315 return 1
282 (dep_path, revision) = argv[0:2] 316 (dep_path, revision) = argv[0:2]
283 dep_path = platform_path(dep_path) 317 dep_path = platform_path(dep_path)
284 assert os.path.isdir(dep_path), 'No such directory: %s' % dep_path 318 assert os.path.isdir(dep_path), 'No such directory: %s' % dep_path
285 gclient_root = find_gclient_root() 319 gclient_root = find_gclient_root()
286 soln = get_solution(gclient_root, dep_path) 320 soln = get_solution(gclient_root, dep_path)
287 soln_path = os.path.relpath(os.path.join(gclient_root, soln['name'])) 321 soln_path = os.path.relpath(os.path.join(gclient_root, soln['name']))
288 dep_name = posix_path(os.path.relpath(dep_path, gclient_root)) 322 dep_name = posix_path(os.path.relpath(dep_path, gclient_root))
289 new_rev = get_git_revision(dep_path, revision) 323 (git_rev, svn_rev) = get_git_revision(dep_path, revision)
290 assert new_rev, 'Could not find git revision matching %s' % revision 324 comment = ('from svn revision %s' % svn_rev) if svn_rev else None
291 return update_deps(soln_path, dep_name, new_rev) 325 assert git_rev, 'Could not find git revision matching %s.' % revision
326 return update_deps(soln_path, dep_name, git_rev, comment)
292 327
293 if __name__ == '__main__': 328 if __name__ == '__main__':
294 sys.exit(main(sys.argv[1:])) 329 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698