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

Side by Side Diff: gcl.py

Issue 6719004: refactor parsing of change descriptions, fix TBR= (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: remove TestableChangeDescription; the base class is already testable Created 9 years, 9 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 | gclient_utils.py » ('j') | gclient_utils.py » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2010 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 """\ 6 """\
7 Wrapper script around Rietveld's upload.py that simplifies working with groups 7 Wrapper script around Rietveld's upload.py that simplifies working with groups
8 of files. 8 of files.
9 """ 9 """
10 10
(...skipping 272 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 rietveld: rietveld server for this change 283 rietveld: rietveld server for this change
284 """ 284 """
285 # Kept for unit test support. This is for the old format, it's deprecated. 285 # Kept for unit test support. This is for the old format, it's deprecated.
286 _SEPARATOR = "\n-----\n" 286 _SEPARATOR = "\n-----\n"
287 287
288 def __init__(self, name, issue, patchset, description, files, local_root, 288 def __init__(self, name, issue, patchset, description, files, local_root,
289 rietveld, needs_upload=False): 289 rietveld, needs_upload=False):
290 self.name = name 290 self.name = name
291 self.issue = int(issue) 291 self.issue = int(issue)
292 self.patchset = int(patchset) 292 self.patchset = int(patchset)
293 self._description = None 293 self._change_desc = None
294 self._subject = None
295 self._reviewers = None
296 self._set_description(description) 294 self._set_description(description)
297 if files is None: 295 if files is None:
298 files = [] 296 files = []
299 self._files = files 297 self._files = files
300 self.patch = None 298 self.patch = None
301 self._local_root = local_root 299 self._local_root = local_root
302 self.needs_upload = needs_upload 300 self.needs_upload = needs_upload
303 self.rietveld = rietveld 301 self.rietveld = rietveld
304 if not self.rietveld: 302 if not self.rietveld:
305 # Set the default value. 303 # Set the default value.
306 self.rietveld = GetCodeReviewSetting('CODE_REVIEW_SERVER') 304 self.rietveld = GetCodeReviewSetting('CODE_REVIEW_SERVER')
307 305
308 def _get_description(self): 306 def _get_description(self):
309 return self._description 307 return self._change_desc.description
310 308
311 def _set_description(self, description): 309 def _set_description(self, description):
312 # TODO(dpranke): Cloned from git_cl.py. These should be shared. 310 self._change_desc = presubmit_support.ChangeDescription(
313 if not description: 311 description=description)
314 self._description = description
315 return
316
317 parsed_lines = []
318 reviewers_re = re.compile(REVIEWERS_REGEX)
319 reviewers = ''
320 subject = ''
321 for l in description.splitlines():
322 if not subject:
323 subject = l
324 matched_reviewers = reviewers_re.match(l)
325 if matched_reviewers:
326 reviewers = matched_reviewers.group(1).split(',')
327 parsed_lines.append(l)
328
329 if len(subject) > 100:
330 subject = subject[:97] + '...'
331
332 self._subject = subject
333 self._reviewers = reviewers
334 self._description = '\n'.join(parsed_lines)
335 312
336 description = property(_get_description, _set_description) 313 description = property(_get_description, _set_description)
337 314
338 @property 315 @property
339 def reviewers(self): 316 def reviewers(self):
340 return self._reviewers 317 return self._change_desc.reviewers
341 318
342 @property 319 @property
343 def subject(self): 320 def subject(self):
344 return self._subject 321 return self._change_desc.subject
345 322
346 def NeedsUpload(self): 323 def NeedsUpload(self):
347 return self.needs_upload 324 return self.needs_upload
348 325
349 def GetFileNames(self): 326 def GetFileNames(self):
350 """Returns the list of file names included in this change.""" 327 """Returns the list of file names included in this change."""
351 return [f[1] for f in self._files] 328 return [f[1] for f in self._files]
352 329
353 def GetFiles(self): 330 def GetFiles(self):
354 """Returns the list of files included in this change with their status.""" 331 """Returns the list of files included in this change with their status."""
(...skipping 16 matching lines...) Expand all
371 """Returns a list of files added in this change.""" 348 """Returns a list of files added in this change."""
372 return [f[1] for f in self.GetFiles() if f[0].startswith("A")] 349 return [f[1] for f in self.GetFiles() if f[0].startswith("A")]
373 350
374 def Save(self): 351 def Save(self):
375 """Writes the changelist information to disk.""" 352 """Writes the changelist information to disk."""
376 data = json.dumps({ 353 data = json.dumps({
377 'issue': self.issue, 354 'issue': self.issue,
378 'patchset': self.patchset, 355 'patchset': self.patchset,
379 'needs_upload': self.NeedsUpload(), 356 'needs_upload': self.NeedsUpload(),
380 'files': self.GetFiles(), 357 'files': self.GetFiles(),
381 'description': self.description, 358 'description': self._change_desc.description,
382 'rietveld': self.rietveld, 359 'rietveld': self.rietveld,
383 }, sort_keys=True, indent=2) 360 }, sort_keys=True, indent=2)
384 gclient_utils.FileWrite(GetChangelistInfoFile(self.name), data) 361 gclient_utils.FileWrite(GetChangelistInfoFile(self.name), data)
385 362
386 def Delete(self): 363 def Delete(self):
387 """Removes the changelist information from disk.""" 364 """Removes the changelist information from disk."""
388 os.remove(GetChangelistInfoFile(self.name)) 365 os.remove(GetChangelistInfoFile(self.name))
389 366
390 def CloseIssue(self): 367 def CloseIssue(self):
391 """Closes the Rietveld issue for this changelist.""" 368 """Closes the Rietveld issue for this changelist."""
(...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after
732 if (files.get('') or (show_unknown_files and len(unknown_files))): 709 if (files.get('') or (show_unknown_files and len(unknown_files))):
733 print "\n--- Not in any changelist:" 710 print "\n--- Not in any changelist:"
734 for item in files.get('', []): 711 for item in files.get('', []):
735 print "".join(item) 712 print "".join(item)
736 if show_unknown_files: 713 if show_unknown_files:
737 for filename in unknown_files: 714 for filename in unknown_files:
738 print "? %s" % filename 715 print "? %s" % filename
739 return 0 716 return 0
740 717
741 718
742 def GetEditor():
743 editor = os.environ.get("SVN_EDITOR")
744 if not editor:
745 editor = os.environ.get("EDITOR")
746
747 if not editor:
748 if sys.platform.startswith("win"):
749 editor = "notepad"
750 else:
751 editor = "vi"
752
753 return editor
754
755
756 def GenerateDiff(files, root=None): 719 def GenerateDiff(files, root=None):
757 return SVN.GenerateDiff(files, root=root) 720 return SVN.GenerateDiff(files, root=root)
758 721
759 722
760 def OptionallyDoPresubmitChecks(change_info, committing, args): 723 def OptionallyDoPresubmitChecks(change_info, committing, args):
761 if FilterFlag(args, "--no_presubmit") or FilterFlag(args, "--force"): 724 if FilterFlag(args, "--no_presubmit") or FilterFlag(args, "--force"):
762 return presubmit_support.PresubmitOutput() 725 return presubmit_support.PresubmitOutput()
763 return DoPresubmitChecks(change_info, committing, True) 726 return DoPresubmitChecks(change_info, committing, True)
764 727
765 728
(...skipping 325 matching lines...) Expand 10 before | Expand all | Expand 10 after
1091 description = change_info.description 1054 description = change_info.description
1092 1055
1093 other_files = GetFilesNotInCL() 1056 other_files = GetFilesNotInCL()
1094 1057
1095 # Edited files (as opposed to files with only changed properties) will have 1058 # Edited files (as opposed to files with only changed properties) will have
1096 # a letter for the first character in the status string. 1059 # a letter for the first character in the status string.
1097 file_re = re.compile(r"^[a-z].+\Z", re.IGNORECASE) 1060 file_re = re.compile(r"^[a-z].+\Z", re.IGNORECASE)
1098 affected_files = [x for x in other_files if file_re.match(x[0])] 1061 affected_files = [x for x in other_files if file_re.match(x[0])]
1099 unaffected_files = [x for x in other_files if not file_re.match(x[0])] 1062 unaffected_files = [x for x in other_files if not file_re.match(x[0])]
1100 1063
1101 if not change_info.reviewers: 1064 reviewers = change_info.reviewers
1065 if not reviewers:
1102 files_for_review = affected_files[:] 1066 files_for_review = affected_files[:]
1103 files_for_review.extend(change_info.GetFiles()) 1067 files_for_review.extend(change_info.GetFiles())
1104 suggested_reviewers = suggest_reviewers(change_info, files_for_review) 1068 reviewers = suggest_reviewers(change_info, files_for_review)
1105 if suggested_reviewers:
1106 reviewers_re = re.compile(REVIEWERS_REGEX)
1107 if not any(reviewers_re.match(l) for l in description.splitlines()):
1108 description += '\n\nR=' + ','.join(suggested_reviewers)
1109
1110 description = description.rstrip() + '\n'
1111 1069
1112 separator1 = ("\n---All lines above this line become the description.\n" 1070 separator1 = ("\n---All lines above this line become the description.\n"
1113 "---Repository Root: " + change_info.GetLocalRoot() + "\n" 1071 "---Repository Root: " + change_info.GetLocalRoot() + "\n"
1114 "---Paths in this changelist (" + change_info.name + "):\n") 1072 "---Paths in this changelist (" + change_info.name + "):\n")
1115 separator2 = "\n\n---Paths modified but not in any changelist:\n\n" 1073 separator2 = "\n\n---Paths modified but not in any changelist:\n\n"
1116 text = (description + separator1 + '\n' + 1074
1117 '\n'.join([f[0] + f[1] for f in change_info.GetFiles()])) 1075 footer = (separator1 + '\n' +
1076 '\n'.join([f[0] + f[1] for f in change_info.GetFiles()]))
1118 1077
1119 if change_info.Exists(): 1078 if change_info.Exists():
1120 text += (separator2 + 1079 footer += (separator2 +
1121 '\n'.join([f[0] + f[1] for f in affected_files]) + '\n') 1080 '\n'.join([f[0] + f[1] for f in affected_files]) + '\n')
1122 else: 1081 else:
1123 text += ('\n'.join([f[0] + f[1] for f in affected_files]) + '\n' + 1082 footer += ('\n'.join([f[0] + f[1] for f in affected_files]) + '\n' +
1124 separator2) 1083 separator2)
1125 text += '\n'.join([f[0] + f[1] for f in unaffected_files]) + '\n' 1084 footer += '\n'.join([f[0] + f[1] for f in unaffected_files]) + '\n'
1126 1085
1127 handle, filename = tempfile.mkstemp(text=True) 1086 change_desc = presubmit_support.ChangeDescription(description=description,
1128 os.write(handle, text) 1087 reviewers=reviewers)
1129 os.close(handle)
1130 1088
1131 # Open up the default editor in the system to get the CL description. 1089 # These next few lines are equivalent to change_desc.UserUpdate(). We
1132 try: 1090 # call them individually to avoid passing a lot of state back and forth.
1133 if not silent: 1091 original_description = change_desc.description
1134 cmd = '%s %s' % (GetEditor(), filename) 1092
1135 if sys.platform == 'win32' and os.environ.get('TERM') == 'msys': 1093 text = change_desc.EditableDescription() + footer
M-A Ruel 2011/03/22 17:24:16 Why not? result = change_desc... if not silent:
1136 # Msysgit requires the usage of 'env' to be present. 1094
1137 cmd = 'env ' + cmd 1095 if not silent:
1138 # shell=True to allow the shell to handle all forms of quotes in $EDITOR. 1096 result = change_desc.editor(text)
1139 subprocess.check_call(cmd, shell=True) 1097 else:
1140 result = gclient_utils.FileRead(filename, 'r') 1098 result = text
1141 finally:
1142 os.remove(filename)
1143 1099
1144 if not result: 1100 if not result:
1145 return 0 1101 return 0
1146 1102
1147 split_result = result.split(separator1, 1) 1103 split_result = result.split(separator1, 1)
1148 if len(split_result) != 2: 1104 if len(split_result) != 2:
1149 ErrorExit("Don't modify the text starting with ---!\n\n" + result) 1105 ErrorExit("Don't modify the text starting with ---!\n\n" + result)
1150 1106
1151 # Update the CL description if it has changed. 1107 # Update the CL description if it has changed.
1152 new_description = split_result[0] 1108 new_description = split_result[0]
1153 cl_files_text = split_result[1] 1109 cl_files_text = split_result[1]
1154 if new_description != description or override_description: 1110 change_desc.Parse(new_description)
1155 change_info.description = new_description 1111 if change_desc.description != original_description or override_description:
1156 change_info.needs_upload = True 1112 change_info.needs_upload = True
1157 1113
1158 new_cl_files = [] 1114 new_cl_files = []
1159 for line in cl_files_text.splitlines(): 1115 for line in cl_files_text.splitlines():
1160 if not len(line): 1116 if not len(line):
1161 continue 1117 continue
1162 if line.startswith("---"): 1118 if line.startswith("---"):
1163 break 1119 break
1164 status = line[:7] 1120 status = line[:7]
1165 filename = line[7:] 1121 filename = line[7:]
1166 new_cl_files.append((status, filename)) 1122 new_cl_files.append((status, filename))
1167 1123
1168 if (not len(change_info.GetFiles()) and not change_info.issue and 1124 if (not len(change_info.GetFiles()) and not change_info.issue and
1169 not len(new_description) and not new_cl_files): 1125 not len(change_desc.description) and not new_cl_files):
1170 ErrorExit("Empty changelist not saved") 1126 ErrorExit("Empty changelist not saved")
1171 1127
1172 change_info._files = new_cl_files 1128 change_info._files = new_cl_files
1173 change_info.Save() 1129 change_info.Save()
1174 if svn_info.get('URL', '').startswith('http:'): 1130 if svn_info.get('URL', '').startswith('http:'):
1175 Warn("WARNING: Creating CL in a read-only checkout. You will not be " 1131 Warn("WARNING: Creating CL in a read-only checkout. You will not be "
1176 "able to commit it!") 1132 "able to commit it!")
1177 1133
1178 print change_info.name + " changelist saved." 1134 print change_info.name + " changelist saved."
1179 if change_info.MissingTests(): 1135 if change_info.MissingTests():
(...skipping 287 matching lines...) Expand 10 before | Expand all | Expand 10 after
1467 if e.code != 500: 1423 if e.code != 500:
1468 raise 1424 raise
1469 print >> sys.stderr, ( 1425 print >> sys.stderr, (
1470 'AppEngine is misbehaving and returned HTTP %d, again. Keep faith ' 1426 'AppEngine is misbehaving and returned HTTP %d, again. Keep faith '
1471 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e)) 1427 'and retry or visit go/isgaeup.\n%s') % (e.code, str(e))
1472 return 1 1428 return 1
1473 1429
1474 1430
1475 if __name__ == "__main__": 1431 if __name__ == "__main__":
1476 sys.exit(main(sys.argv[1:])) 1432 sys.exit(main(sys.argv[1:]))
OLDNEW
« no previous file with comments | « no previous file | gclient_utils.py » ('j') | gclient_utils.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698