summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--codereview/__init__.py2
-rw-r--r--codereview/upload_bundle_pb2.py42
-rwxr-xr-xgerrit_upload.py9
-rw-r--r--project.py8
-rw-r--r--subcmds/upload.py47
5 files changed, 88 insertions, 20 deletions
diff --git a/codereview/__init__.py b/codereview/__init__.py
index 82216880..58835553 100644
--- a/codereview/__init__.py
+++ b/codereview/__init__.py
@@ -1 +1 @@
__version__ = 'v1.0-99-g9cd3ea2f' __version__ = 'v1.0-112-gbcd4db5a'
diff --git a/codereview/upload_bundle_pb2.py b/codereview/upload_bundle_pb2.py
index 7cf2f86e..ff91ee1f 100644
--- a/codereview/upload_bundle_pb2.py
+++ b/codereview/upload_bundle_pb2.py
@@ -35,23 +35,27 @@ _UPLOADBUNDLERESPONSE_CODETYPE = descriptor.EnumDescriptor(
35 options=None, 35 options=None,
36 type=None), 36 type=None),
37 descriptor.EnumValueDescriptor( 37 descriptor.EnumValueDescriptor(
38 name='UNKNOWN_PROJECT', index=5, number=2, 38 name='UNKNOWN_EMAIL', index=5, number=11,
39 options=None, 39 options=None,
40 type=None), 40 type=None),
41 descriptor.EnumValueDescriptor( 41 descriptor.EnumValueDescriptor(
42 name='UNKNOWN_BRANCH', index=6, number=3, 42 name='UNKNOWN_PROJECT', index=6, number=2,
43 options=None, 43 options=None,
44 type=None), 44 type=None),
45 descriptor.EnumValueDescriptor( 45 descriptor.EnumValueDescriptor(
46 name='UNKNOWN_BUNDLE', index=7, number=5, 46 name='UNKNOWN_BRANCH', index=7, number=3,
47 options=None, 47 options=None,
48 type=None), 48 type=None),
49 descriptor.EnumValueDescriptor( 49 descriptor.EnumValueDescriptor(
50 name='NOT_BUNDLE_OWNER', index=8, number=6, 50 name='UNKNOWN_BUNDLE', index=8, number=5,
51 options=None, 51 options=None,
52 type=None), 52 type=None),
53 descriptor.EnumValueDescriptor( 53 descriptor.EnumValueDescriptor(
54 name='BUNDLE_CLOSED', index=9, number=8, 54 name='NOT_BUNDLE_OWNER', index=9, number=6,
55 options=None,
56 type=None),
57 descriptor.EnumValueDescriptor(
58 name='BUNDLE_CLOSED', index=10, number=8,
55 options=None, 59 options=None,
56 type=None), 60 type=None),
57 ], 61 ],
@@ -136,6 +140,20 @@ _UPLOADBUNDLEREQUEST = descriptor.Descriptor(
136 message_type=None, enum_type=None, containing_type=None, 140 message_type=None, enum_type=None, containing_type=None,
137 is_extension=False, extension_scope=None, 141 is_extension=False, extension_scope=None,
138 options=None), 142 options=None),
143 descriptor.FieldDescriptor(
144 name='reviewers', full_name='codereview.UploadBundleRequest.reviewers', index=6,
145 number=3, type=9, cpp_type=9, label=3,
146 default_value=[],
147 message_type=None, enum_type=None, containing_type=None,
148 is_extension=False, extension_scope=None,
149 options=None),
150 descriptor.FieldDescriptor(
151 name='cc', full_name='codereview.UploadBundleRequest.cc', index=7,
152 number=4, type=9, cpp_type=9, label=3,
153 default_value=[],
154 message_type=None, enum_type=None, containing_type=None,
155 is_extension=False, extension_scope=None,
156 options=None),
139 ], 157 ],
140 extensions=[ 158 extensions=[
141 ], 159 ],
@@ -165,6 +183,20 @@ _UPLOADBUNDLERESPONSE = descriptor.Descriptor(
165 message_type=None, enum_type=None, containing_type=None, 183 message_type=None, enum_type=None, containing_type=None,
166 is_extension=False, extension_scope=None, 184 is_extension=False, extension_scope=None,
167 options=None), 185 options=None),
186 descriptor.FieldDescriptor(
187 name='invalid_reviewers', full_name='codereview.UploadBundleResponse.invalid_reviewers', index=2,
188 number=12, type=9, cpp_type=9, label=3,
189 default_value=[],
190 message_type=None, enum_type=None, containing_type=None,
191 is_extension=False, extension_scope=None,
192 options=None),
193 descriptor.FieldDescriptor(
194 name='invalid_cc', full_name='codereview.UploadBundleResponse.invalid_cc', index=3,
195 number=13, type=9, cpp_type=9, label=3,
196 default_value=[],
197 message_type=None, enum_type=None, containing_type=None,
198 is_extension=False, extension_scope=None,
199 options=None),
168 ], 200 ],
169 extensions=[ 201 extensions=[
170 ], 202 ],
diff --git a/gerrit_upload.py b/gerrit_upload.py
index d3d4ff3e..17112aac 100755
--- a/gerrit_upload.py
+++ b/gerrit_upload.py
@@ -75,6 +75,7 @@ def UploadBundle(project,
75 dest_branch, 75 dest_branch,
76 src_branch, 76 src_branch,
77 bases, 77 bases,
78 people,
78 replace_changes = None, 79 replace_changes = None,
79 save_cookies=True): 80 save_cookies=True):
80 81
@@ -112,6 +113,10 @@ def UploadBundle(project,
112 req = UploadBundleRequest() 113 req = UploadBundleRequest()
113 req.dest_project = str(dest_project) 114 req.dest_project = str(dest_project)
114 req.dest_branch = str(dest_branch) 115 req.dest_branch = str(dest_branch)
116 for e in people[0]:
117 req.reviewers.append(e)
118 for e in people[1]:
119 req.cc.append(e)
115 for c in revlist: 120 for c in revlist:
116 req.contained_object.append(c) 121 req.contained_object.append(c)
117 if replace_changes: 122 if replace_changes:
@@ -158,6 +163,10 @@ def UploadBundle(project,
158 reason = 'invalid change id' 163 reason = 'invalid change id'
159 elif rsp.status_code == UploadBundleResponse.CHANGE_CLOSED: 164 elif rsp.status_code == UploadBundleResponse.CHANGE_CLOSED:
160 reason = 'one or more changes are closed' 165 reason = 'one or more changes are closed'
166 elif rsp.status_code == UploadBundleResponse.UNKNOWN_EMAIL:
167 emails = [x for x in rsp.invalid_reviewers] + [
168 x for x in rsp.invalid_cc]
169 reason = 'invalid email addresses: %s' % ", ".join(emails)
161 else: 170 else:
162 reason = 'unknown error ' + str(rsp.status_code) 171 reason = 'unknown error ' + str(rsp.status_code)
163 raise UploadError(reason) 172 raise UploadError(reason)
diff --git a/project.py b/project.py
index 39550335..fe3ce34a 100644
--- a/project.py
+++ b/project.py
@@ -142,9 +142,10 @@ class ReviewableBranch(object):
142 R_HEADS + self.name, 142 R_HEADS + self.name,
143 '--') 143 '--')
144 144
145 def UploadForReview(self): 145 def UploadForReview(self, people):
146 self.project.UploadForReview(self.name, 146 self.project.UploadForReview(self.name,
147 self.replace_changes) 147 self.replace_changes,
148 people)
148 149
149 @property 150 @property
150 def tip_url(self): 151 def tip_url(self):
@@ -456,7 +457,7 @@ class Project(object):
456 return rb 457 return rb
457 return None 458 return None
458 459
459 def UploadForReview(self, branch=None, replace_changes=None): 460 def UploadForReview(self, branch=None, replace_changes=None, people=([],[])):
460 """Uploads the named branch for code review. 461 """Uploads the named branch for code review.
461 """ 462 """
462 if branch is None: 463 if branch is None:
@@ -495,6 +496,7 @@ class Project(object):
495 dest_branch = dest_branch, 496 dest_branch = dest_branch,
496 src_branch = R_HEADS + branch.name, 497 src_branch = R_HEADS + branch.name,
497 bases = base_list, 498 bases = base_list,
499 people = people,
498 replace_changes = replace_changes) 500 replace_changes = replace_changes)
499 except proto_client.ClientLoginError: 501 except proto_client.ClientLoginError:
500 raise UploadError('Login failure') 502 raise UploadError('Login failure')
diff --git a/subcmds/upload.py b/subcmds/upload.py
index 11f035d7..49d00187 100644
--- a/subcmds/upload.py
+++ b/subcmds/upload.py
@@ -25,11 +25,17 @@ def _die(fmt, *args):
25 print >>sys.stderr, 'error: %s' % msg 25 print >>sys.stderr, 'error: %s' % msg
26 sys.exit(1) 26 sys.exit(1)
27 27
28def _SplitEmails(values):
29 result = []
30 for str in values:
31 result.extend([s.strip() for s in str.split(',')])
32 return result
33
28class Upload(InteractiveCommand): 34class Upload(InteractiveCommand):
29 common = True 35 common = True
30 helpSummary = "Upload changes for code review" 36 helpSummary = "Upload changes for code review"
31 helpUsage=""" 37 helpUsage="""
32%prog {[<project>]... | --replace <project>} 38%prog [--re --cc] {[<project>]... | --replace <project>}
33""" 39"""
34 helpDescription = """ 40 helpDescription = """
35The '%prog' command is used to send changes to the Gerrit code 41The '%prog' command is used to send changes to the Gerrit code
@@ -44,14 +50,25 @@ at the command line. Projects can be specified either by name, or
44by a relative or absolute path to the project's local directory. If 50by a relative or absolute path to the project's local directory. If
45no projects are specified, '%prog' will search for uploadable 51no projects are specified, '%prog' will search for uploadable
46changes in all projects listed in the manifest. 52changes in all projects listed in the manifest.
53
54If the --reviewers or --cc options are passed, those emails are
55added to the respective list of users, and emails are sent to any
56new users. Users passed to --reviewers must be already registered
57with the code review system, or the upload will fail.
47""" 58"""
48 59
49 def _Options(self, p): 60 def _Options(self, p):
50 p.add_option('--replace', 61 p.add_option('--replace',
51 dest='replace', action='store_true', 62 dest='replace', action='store_true',
52 help='Upload replacement patchesets from this branch') 63 help='Upload replacement patchesets from this branch')
53 64 p.add_option('--re', '--reviewers',
54 def _SingleBranch(self, branch): 65 type='string', action='append', dest='reviewers',
66 help='Request reviews from these people.')
67 p.add_option('--cc',
68 type='string', action='append', dest='cc',
69 help='Also send email to these email addresses.')
70
71 def _SingleBranch(self, branch, people):
55 project = branch.project 72 project = branch.project
56 name = branch.name 73 name = branch.name
57 date = branch.date 74 date = branch.date
@@ -69,11 +86,11 @@ changes in all projects listed in the manifest.
69 sys.stdout.write('(y/n)? ') 86 sys.stdout.write('(y/n)? ')
70 answer = sys.stdin.readline().strip() 87 answer = sys.stdin.readline().strip()
71 if answer in ('y', 'Y', 'yes', '1', 'true', 't'): 88 if answer in ('y', 'Y', 'yes', '1', 'true', 't'):
72 self._UploadAndReport([branch]) 89 self._UploadAndReport([branch], people)
73 else: 90 else:
74 _die("upload aborted by user") 91 _die("upload aborted by user")
75 92
76 def _MultipleBranches(self, pending): 93 def _MultipleBranches(self, pending, people):
77 projects = {} 94 projects = {}
78 branches = {} 95 branches = {}
79 96
@@ -132,7 +149,7 @@ changes in all projects listed in the manifest.
132 todo.append(branch) 149 todo.append(branch)
133 if not todo: 150 if not todo:
134 _die("nothing uncommented for upload") 151 _die("nothing uncommented for upload")
135 self._UploadAndReport(todo) 152 self._UploadAndReport(todo, people)
136 153
137 def _ReplaceBranch(self, project): 154 def _ReplaceBranch(self, project):
138 branch = project.CurrentBranch 155 branch = project.CurrentBranch
@@ -176,13 +193,13 @@ changes in all projects listed in the manifest.
176 sys.exit(1) 193 sys.exit(1)
177 194
178 branch.replace_changes = to_replace 195 branch.replace_changes = to_replace
179 self._UploadAndReport([branch]) 196 self._UploadAndReport([branch], people)
180 197
181 def _UploadAndReport(self, todo): 198 def _UploadAndReport(self, todo, people):
182 have_errors = False 199 have_errors = False
183 for branch in todo: 200 for branch in todo:
184 try: 201 try:
185 branch.UploadForReview() 202 branch.UploadForReview(people)
186 branch.uploaded = True 203 branch.uploaded = True
187 except UploadError, e: 204 except UploadError, e:
188 branch.error = e 205 branch.error = e
@@ -216,6 +233,14 @@ changes in all projects listed in the manifest.
216 def Execute(self, opt, args): 233 def Execute(self, opt, args):
217 project_list = self.GetProjects(args) 234 project_list = self.GetProjects(args)
218 pending = [] 235 pending = []
236 reviewers = []
237 cc = []
238
239 if opt.reviewers:
240 reviewers = _SplitEmails(opt.reviewers)
241 if opt.cc:
242 cc = _SplitEmails(opt.cc)
243 people = (reviewers,cc)
219 244
220 if opt.replace: 245 if opt.replace:
221 if len(project_list) != 1: 246 if len(project_list) != 1:
@@ -233,6 +258,6 @@ changes in all projects listed in the manifest.
233 if not pending: 258 if not pending:
234 print >>sys.stdout, "no branches ready for upload" 259 print >>sys.stdout, "no branches ready for upload"
235 elif len(pending) == 1 and len(pending[0][1]) == 1: 260 elif len(pending) == 1 and len(pending[0][1]) == 1:
236 self._SingleBranch(pending[0][1][0]) 261 self._SingleBranch(pending[0][1][0], people)
237 else: 262 else:
238 self._MultipleBranches(pending) 263 self._MultipleBranches(pending, people)