diff options
| -rw-r--r-- | codereview/__init__.py | 2 | ||||
| -rw-r--r-- | codereview/upload_bundle_pb2.py | 42 | ||||
| -rwxr-xr-x | gerrit_upload.py | 9 | ||||
| -rw-r--r-- | project.py | 8 | ||||
| -rw-r--r-- | subcmds/upload.py | 47 |
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) |
| @@ -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 | ||
| 28 | def _SplitEmails(values): | ||
| 29 | result = [] | ||
| 30 | for str in values: | ||
| 31 | result.extend([s.strip() for s in str.split(',')]) | ||
| 32 | return result | ||
| 33 | |||
| 28 | class Upload(InteractiveCommand): | 34 | class 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 = """ |
| 35 | The '%prog' command is used to send changes to the Gerrit code | 41 | The '%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 | |||
| 44 | by a relative or absolute path to the project's local directory. If | 50 | by a relative or absolute path to the project's local directory. If |
| 45 | no projects are specified, '%prog' will search for uploadable | 51 | no projects are specified, '%prog' will search for uploadable |
| 46 | changes in all projects listed in the manifest. | 52 | changes in all projects listed in the manifest. |
| 53 | |||
| 54 | If the --reviewers or --cc options are passed, those emails are | ||
| 55 | added to the respective list of users, and emails are sent to any | ||
| 56 | new users. Users passed to --reviewers must be already registered | ||
| 57 | with 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) |
