def main(self, request_id): """Run the command.""" repository_info, tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, tool) api_client, api_root = self.get_api(server_url) request = get_review_request(request_id, api_root) self.setup_tool(tool, api_root) update_fields = { 'public': True, } if (self.options.trivial_publish and tool.capabilities.has_capability('review_requests', 'trivial_publish')): update_fields['trivial'] = True try: draft = request.get_draft() draft.update(**update_fields) except APIError as e: raise CommandError('Error publishing review request (it may ' 'already be published): %s' % e) print('Review request #%s is published.' % request_id)
def main(self, request_id): """Run the command.""" close_type = self.options.close_type self.check_valid_type(close_type) if self.options.server: # Bypass getting the scm_tool to discover the server since it was # specified with --server or in .reviewboardrc repository_info, tool = None, None else: repository_info, tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, tool) api_client, api_root = self.get_api(server_url) request = get_review_request(request_id, api_root) if request.status == close_type: raise CommandError('Review request #%s is already %s.' % (request_id, close_type)) if self.options.description: request = request.update(status=close_type, description=self.options.description) else: request = request.update(status=close_type) print('Review request #%s is set to %s.' % (request_id, request.status))
def main(self, request_id): """Run the command.""" repository_info, tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, tool) api_client, api_root = self.get_api(server_url) request = get_review_request(request_id, api_root) self.setup_tool(tool, api_root) update_fields = { 'public': True, } if (self.options.trivial_publish and tool.capabilities.has_capability( 'review_requests', 'trivial_publish')): update_fields['trivial'] = True try: draft = request.get_draft() draft.update(**update_fields) except APIError as e: raise CommandError('Error publishing review request (it may ' 'already be published): %s' % e) print('Review request #%s is published.' % request_id)
def main(self, request_id): """Run the command.""" close_type = self.options.close_type self.check_valid_type(close_type) if self.options.server: # Bypass getting the scm_tool to discover the server since it was # specified with --server or in .reviewboardrc repository_info, tool = None, None else: repository_info, tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, tool) api_client, api_root = self.get_api(server_url) request = get_review_request(request_id, api_root) if request.status == close_type: raise CommandError('Review request #%s is already %s.' % ( request_id, close_type)) if self.options.description: request = request.update(status=close_type, description=self.options.description) else: request = request.update(status=close_type) print('Review request #%s is set to %s.' % (request_id, request.status))
def main(self, request_id, path_to_file): self.repository_info, self.tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(self.repository_info, self.tool) api_client, api_root = self.get_api(server_url) request = get_review_request(request_id, api_root) try: with open(path_to_file, 'rb') as f: content = f.read() except IOError: raise CommandError('%s is not a valid file.' % path_to_file) # Check if the user specified a custom filename, otherwise # use the original filename. filename = self.options.filename or os.path.basename(path_to_file) try: request.get_file_attachments().upload_attachment( filename, content, self.options.caption) except APIError as e: raise CommandError('Error uploading file: %s' % e) print('Uploaded %s to review request %s.' % (path_to_file, request_id))
def post_request(self, repository_info, server_url, api_root, review_request_id=None, changenum=None, diff_content=None, parent_diff_content=None, commit_id=None, base_commit_id=None, submit_as=None, retries=3): """Creates or updates a review request, and uploads a diff. On success the review request id and url are returned. """ supports_posting_commit_ids = \ self.tool.capabilities.has_capability('review_requests', 'commit_ids') if review_request_id: review_request = get_review_request(review_request_id, api_root) if review_request.status == 'submitted': raise CommandError( "Review request %s is marked as %s. In order to update " "it, please reopen the review request and try again." % (review_request_id, review_request.status)) else: # No review_request_id, so we will create a new review request. try: repository = ( self.options.repository_name or self.options.repository_url or self.get_repository_path(repository_info, api_root)) request_data = { 'repository': repository } if changenum: request_data['changenum'] = changenum elif commit_id and supports_posting_commit_ids: request_data['commit_id'] = commit_id if submit_as: request_data['submit_as'] = submit_as review_request = api_root.get_review_requests().create( **request_data) except APIError, e: if e.error_code == 204 and changenum: # Change number in use. rid = e.rsp['review_request']['id'] review_request = api_root.get_review_request( review_request_id=rid) if not self.options.diff_only: review_request = review_request.update( changenum=changenum) else: raise CommandError("Error creating review request: %s" % e)
def main(self, *args): """Stamp the latest commit with corresponding review request URL""" self.cmd_args = list(args) repository_info, self.tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, self.tool) api_client, api_root = self.get_api(server_url) self.setup_tool(self.tool, api_root=api_root) if not self.tool.can_amend_commit: raise NotImplementedError('rbt stamp is not supported with %s.' % self.tool.name) try: if self.tool.has_pending_changes(): raise CommandError('Working directory is not clean.') except NotImplementedError: pass revisions = get_revisions(self.tool, self.cmd_args) commit_message = self.tool.get_raw_commit_message(revisions) if '\nReviewed at http' in commit_message: raise CommandError('This commit is already stamped.') if not self.options.rid: self.options.rid = guess_existing_review_request_id( repository_info, self.options.repository_name, api_root, api_client, self.tool, revisions, guess_summary=False, guess_description=False, is_fuzzy_match_func=self._ask_review_request_match, no_commit_error=self.no_commit_error) if not self.options.rid: raise CommandError('Could not determine the existing review ' 'request URL to stamp with.') review_request = get_review_request(self.options.rid, api_root) stamp_url = review_request.absolute_url commit_message += '\n\nReviewed at %s' % stamp_url self.tool.amend_commit(commit_message) print('Changes committed to current branch.')
def main(self, request_id): """Run the command.""" repository_info, tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, tool) api_client, api_root = self.get_api(server_url) request = get_review_request(request_id, api_root) try: draft = request.get_draft() draft = draft.update(public=True) except APIError, e: raise CommandError("Error publishing review request (it may " "already be published): %s" % e)
def main(self, request_id): """Run the command.""" repository_info, tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, tool) api_client, api_root = self.get_api(server_url) review_request = get_review_request(request_id, api_root, only_fields='public', only_links='draft') self.setup_tool(tool, api_root) update_fields = { 'public': True, } if (self.options.trivial_publish and tool.capabilities.has_capability('review_requests', 'trivial_publish')): update_fields['trivial'] = True if self.options.change_description is not None: if review_request.public: update_fields['changedescription'] = \ self.options.change_description if (self.options.markdown and tool.capabilities.has_capability('text', 'markdown')): update_fields['changedescription_text_type'] = 'markdown' else: update_fields['changedescription_text_type'] = 'plain' else: logging.error( 'The change description field can only be set when ' 'publishing an update.') try: draft = review_request.get_draft(only_fields='') draft.update(**update_fields) except APIError as e: raise CommandError('Error publishing review request (it may ' 'already be published): %s' % e) print('Review request #%s is published.' % request_id)
def main(self, *args): """Add the review request URL to a commit message.""" self.cmd_args = list(args) repository_info, self.tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, self.tool) api_client, api_root = self.get_api(server_url) self.setup_tool(self.tool, api_root=api_root) if not self.tool.can_amend_commit: raise NotImplementedError('rbt stamp is not supported with %s.' % self.tool.name) try: if self.tool.has_pending_changes(): raise CommandError('Working directory is not clean.') except NotImplementedError: pass revisions = get_revisions(self.tool, self.cmd_args) # Use the ID from the command line options if present. if self.options.rid: review_request = get_review_request(self.options.rid, api_root) review_request_id = self.options.rid review_request_url = review_request.absolute_url else: review_request_id, review_request_url = \ self. determine_review_request( api_client, api_root, repository_info, self.options.repository_name, revisions) if not review_request_url: raise CommandError('Could not determine the existing review ' 'request URL to stamp with.') stamp_commit_with_review_url(revisions, review_request_url, self.tool) print('Successfully stamped change with the URL:') print(review_request_url)
def main(self, request_id, path_to_file): self.repository_info, self.tool = self.initialize_scm_tool(client_name=self.options.repository_type) server_url = self.get_server_url(self.repository_info, self.tool) api_client, api_root = self.get_api(server_url) request = get_review_request(request_id, api_root) try: with open(path_to_file, "rb") as f: content = f.read() except IOError: raise CommandError("%s is not a valid file." % path_to_file) # Check if the user specified a custom filename, otherwise # use the original filename. filename = self.options.filename or os.path.basename(path_to_file) try: request.get_file_attachments().upload_attachment(filename, content, self.options.caption) except APIError as e: raise CommandError("Error uploading file: %s" % e) print("Uploaded %s to review request %s." % (path_to_file, request_id))
def main(self, request_id, path_to_file): self.repository_info, self.tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(self.repository_info, self.tool) api_client, api_root = self.get_api(server_url) request = get_review_request(request_id, api_root) try: f = open(path_to_file, 'r') content = f.read() f.close() except IOError: raise CommandError("%s is not a valid file." % path_to_file) # Check if the user specified a custom filename, otherwise # use the original filename. filename = self.options.filename or os.path.basename(path_to_file) try: request.get_file_attachments() \ .upload_attachment(filename, content, self.options.caption) except APIError, e: raise CommandError("Error uploading file: %s" % e)
def post_request(self, repository_info, repository, server_url, api_root, review_request_id=None, changenum=None, diff_content=None, parent_diff_content=None, commit_id=None, base_commit_id=None, submit_as=None, retries=3, base_dir=None): """Creates or updates a review request, and uploads a diff. On success the review request id and url are returned. """ supports_posting_commit_ids = \ self.tool.capabilities.has_capability('review_requests', 'commit_ids') if review_request_id: review_request = get_review_request( review_request_id, api_root, only_fields='absolute_url,bugs_closed,id,status', only_links='diffs,draft') if review_request.status == 'submitted': raise CommandError( 'Review request %s is marked as %s. In order to update ' 'it, please reopen the review request and try again.' % (review_request_id, review_request.status)) else: # No review_request_id, so we will create a new review request. try: request_data = { 'repository': repository } if changenum: request_data['changenum'] = changenum elif commit_id and supports_posting_commit_ids: request_data['commit_id'] = commit_id if submit_as: request_data['submit_as'] = submit_as review_requests = api_root.get_review_requests( only_fields='', only_links='create') review_request = review_requests.create(**request_data) except APIError as e: if e.error_code == 204 and changenum: # The change number is already in use. Get the review # request for that change and update it instead. rid = e.rsp['review_request']['id'] review_request = api_root.get_review_request( review_request_id=rid, only_fields='absolute_url,bugs_closed,id,status', only_links='diffs,draft') if not self.options.diff_only: review_request = review_request.update( changenum=changenum) else: raise CommandError('Error creating review request: %s' % e) if (not repository_info.supports_changesets or not self.options.change_only): try: diff_kwargs = { 'parent_diff': parent_diff_content, 'base_dir': base_dir, } if (base_commit_id and self.tool.capabilities.has_capability('diffs', 'base_commit_ids')): # Both the Review Board server and SCMClient support # base commit IDs, so pass that along when creating # the diff. diff_kwargs['base_commit_id'] = base_commit_id review_request.get_diffs(only_fields='').upload_diff( diff_content, **diff_kwargs) except APIError as e: error_msg = [ u'Error uploading diff\n\n', ] if e.error_code == 101 and e.http_status == 403: error_msg.append( u'You do not have permissions to modify ' u'this review request\n') elif e.error_code == 219: error_msg.append( u'The generated diff file was empty. This ' u'usually means no files were\n' u'modified in this change.\n') else: error_msg.append(str(e).decode('utf-8') + u'\n') error_msg.append( u'Your review request still exists, but the diff is ' u'not attached.\n') error_msg.append(u'%s\n' % review_request.absolute_url) raise CommandError(u'\n'.join(error_msg)) try: draft = review_request.get_draft(only_fields='commit_id') except APIError as e: raise CommandError('Error retrieving review request draft: %s' % e) # Stamp the commit message with the review request URL before posting # the review, so that we can use the stamped commit message when # guessing the description. This enables the stamped message to be # present on the review if the user has chosen to publish immediately # upon posting. if self.options.stamp_when_posting: if not self.tool.can_amend_commit: print('Cannot stamp review URL onto the commit message; ' 'stamping is not supported with %s.' % self.tool.name) else: try: stamp_commit_with_review_url(self.revisions, review_request.absolute_url, self.tool) print('Stamped review URL onto the commit message.') except AlreadyStampedError: print('Commit message has already been stamped') except Exception as e: logging.debug('Caught exception while stamping the ' 'commit message. Proceeding to post ' 'without stamping.', exc_info=True) print('Could not stamp review URL onto the commit ' 'message.') # If the user has requested to guess the summary or description, # get the commit message and override the summary and description # options. The guessing takes place after stamping so that the # guessed description matches the commit when rbt exits. if not self.options.diff_filename: self.check_guess_fields() # Update the review request draft fields based on options set # by the user, or configuration. update_fields = {} if self.options.target_groups: update_fields['target_groups'] = self.options.target_groups if self.options.target_people: update_fields['target_people'] = self.options.target_people if self.options.depends_on: update_fields['depends_on'] = self.options.depends_on if self.options.summary: update_fields['summary'] = self.options.summary if self.options.branch: update_fields['branch'] = self.options.branch if self.options.bugs_closed: # Append to the existing list of bugs. self.options.bugs_closed = self.options.bugs_closed.strip(', ') bug_set = (set(re.split('[, ]+', self.options.bugs_closed)) | set(review_request.bugs_closed)) self.options.bugs_closed = ','.join(bug_set) update_fields['bugs_closed'] = self.options.bugs_closed if self.options.description: update_fields['description'] = self.options.description if self.options.testing_done: update_fields['testing_done'] = self.options.testing_done if ((self.options.description or self.options.testing_done) and self.options.markdown and self.tool.capabilities.has_capability('text', 'markdown')): # The user specified that their Description/Testing Done are # valid Markdown, so tell the server so it won't escape the text. update_fields['text_type'] = 'markdown' if self.options.change_description: update_fields['changedescription'] = \ self.options.change_description if self.options.publish: update_fields['public'] = True if supports_posting_commit_ids and commit_id != draft.commit_id: update_fields['commit_id'] = commit_id or '' if update_fields: try: draft = draft.update(**update_fields) except APIError as e: raise CommandError(u'\n'.join([ u'Error updating review request draft: %s\n' % e, u'Your review request still exists, but the diff is ' u'not attached.\n', u'%s\n' % review_request.absolute_url, ])) return review_request.id, review_request.absolute_url
def main(self, branch_name=None, *args): """Run the command.""" self.cmd_args = list(args) if branch_name: self.cmd_args.insert(0, branch_name) repository_info, self.tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, self.tool) api_client, api_root = self.get_api(server_url) self.setup_tool(self.tool, api_root=api_root) # Check if repository info on reviewboard server match local ones. repository_info = repository_info.find_server_repository_info(api_root) if (not self.tool.can_merge or not self.tool.can_push_upstream or not self.tool.can_delete_branch): raise CommandError('This command does not support %s repositories.' % self.tool.name) if self.tool.has_pending_changes(): raise CommandError('Working directory is not clean.') if not self.options.destination_branch: raise CommandError('Please specify a destination branch.') if self.options.rid: is_local = branch_name is not None review_request_id = self.options.rid else: review_request = guess_existing_review_request( repository_info, self.options.repository_name, api_root, api_client, self.tool, get_revisions(self.tool, self.cmd_args), guess_summary=False, guess_description=False, is_fuzzy_match_func=self._ask_review_request_match) if not review_request or not review_request.id: raise CommandError('Could not determine the existing review ' 'request URL to land.') review_request_id = review_request.id is_local = True review_request = get_review_request(review_request_id, api_root) if self.options.is_local is not None: is_local = self.options.is_local if is_local: if branch_name is None: branch_name = self.tool.get_current_branch() if branch_name == self.options.destination_branch: raise CommandError('The local branch cannot be merged onto ' 'itself. Try a different local branch or ' 'destination branch.') else: branch_name = None land_error = self.can_land(review_request) if land_error is not None: raise CommandError('Cannot land review request %s: %s' % (review_request_id, land_error)) if self.options.recursive: # The dependency graph shows us which review requests depend on # which other ones. What we are actually after is the order to land # them in, which is the topological sorting order of the converse # graph. It just so happens that if we reverse the topological sort # of a graph, it is a valid topological sorting of the converse # graph, so we don't have to compute the converse graph. dependency_graph = review_request.build_dependency_graph() dependencies = toposort(dependency_graph)[1:] if dependencies: print('Recursively landing dependencies of review request %s.' % review_request_id) for dependency in dependencies: land_error = self.can_land(dependency) if land_error is not None: raise CommandError( 'Aborting recursive land of review request %s.\n' 'Review request %s cannot be landed: %s' % (review_request_id, dependency.id, land_error)) for dependency in reversed(dependencies): self.land(self.options.destination_branch, dependency, None, self.options.squash, self.options.edit, self.options.delete_branch, self.options.dry_run) self.land(self.options.destination_branch, review_request, branch_name, self.options.squash, self.options.edit, self.options.delete_branch, self.options.dry_run) if self.options.push: print('Pushing branch "%s" upstream' % self.options.destination_branch) if not self.options.dry_run: try: self.tool.push_upstream(self.options.destination_branch) except PushError as e: raise CommandError(six.text_type(e))
def post_request(self, repository_info, repository, server_url, api_root, review_request_id=None, changenum=None, diff_content=None, parent_diff_content=None, commit_id=None, base_commit_id=None, submit_as=None, retries=3, base_dir=None): """Creates or updates a review request, and uploads a diff. On success the review request id and url are returned. """ supports_posting_commit_ids = \ self.tool.capabilities.has_capability('review_requests', 'commit_ids') if review_request_id: review_request = get_review_request( review_request_id, api_root, only_fields='absolute_url,bugs_closed,id,status', only_links='diffs,draft') if review_request.status == 'submitted': raise CommandError( 'Review request %s is marked as %s. In order to update ' 'it, please reopen the review request and try again.' % (review_request_id, review_request.status)) else: # No review_request_id, so we will create a new review request. try: request_data = { 'repository': repository } if changenum: request_data['changenum'] = changenum elif commit_id and supports_posting_commit_ids: request_data['commit_id'] = commit_id if submit_as: request_data['submit_as'] = submit_as review_requests = api_root.get_review_requests( only_fields='', only_links='create') review_request = review_requests.create(**request_data) except APIError as e: if e.error_code == 204 and changenum: # Change number in use. rid = e.rsp['review_request']['id'] review_request = api_root.get_review_request( review_request_id=rid, only_fields='absolute_url,bugs_closed,id,status', only_links='diffs,draft') if not self.options.diff_only: review_request = review_request.update( changenum=changenum) else: raise CommandError('Error creating review request: %s' % e) if (not repository_info.supports_changesets or not self.options.change_only): try: diff_kwargs = { 'parent_diff': parent_diff_content, 'base_dir': base_dir, } if (base_commit_id and self.tool.capabilities.has_capability('diffs', 'base_commit_ids')): # Both the Review Board server and SCMClient support # base commit IDs, so pass that along when creating # the diff. diff_kwargs['base_commit_id'] = base_commit_id review_request.get_diffs(only_fields='').upload_diff( diff_content, **diff_kwargs) except APIError as e: error_msg = [ u'Error uploading diff\n\n', ] if e.error_code == 101 and e.http_status == 403: error_msg.append( u'You do not have permissions to modify ' u'this review request\n') elif e.error_code == 219: error_msg.append( u'The generated diff file was empty. This ' u'usually means no files were\n' u'modified in this change.\n') else: error_msg.append(str(e).decode('utf-8') + u'\n') error_msg.append( u'Your review request still exists, but the diff is ' u'not attached.\n') error_msg.append(u'%s\n' % review_request.absolute_url) raise CommandError(u'\n'.join(error_msg)) try: draft = review_request.get_draft(only_fields='commit_id') except APIError as e: raise CommandError('Error retrieving review request draft: %s' % e) # Update the review request draft fields based on options set # by the user, or configuration. update_fields = {} if self.options.target_groups: update_fields['target_groups'] = self.options.target_groups if self.options.target_people: update_fields['target_people'] = self.options.target_people if self.options.depends_on: update_fields['depends_on'] = self.options.depends_on if self.options.summary: update_fields['summary'] = self.options.summary if self.options.branch: update_fields['branch'] = self.options.branch if self.options.bugs_closed: # Append to the existing list of bugs. self.options.bugs_closed = self.options.bugs_closed.strip(', ') bug_set = (set(re.split('[, ]+', self.options.bugs_closed)) | set(review_request.bugs_closed)) self.options.bugs_closed = ','.join(bug_set) update_fields['bugs_closed'] = self.options.bugs_closed if self.options.description: update_fields['description'] = self.options.description if self.options.testing_done: update_fields['testing_done'] = self.options.testing_done if ((self.options.description or self.options.testing_done) and self.options.markdown and self.tool.capabilities.has_capability('text', 'markdown')): # The user specified that their Description/Testing Done are # valid Markdown, so tell the server so it won't escape the text. update_fields['text_type'] = 'markdown' if self.options.change_description: update_fields['changedescription'] = \ self.options.change_description if self.options.publish: update_fields['public'] = True if supports_posting_commit_ids and commit_id != draft.commit_id: update_fields['commit_id'] = commit_id or '' if update_fields: try: draft = draft.update(**update_fields) except APIError as e: raise CommandError(u'\n'.join([ u'Error updating review request draft: %s\n' % e, u'Your review request still exists, but the diff is ' u'not attached.\n', u'%s\n' % review_request.absolute_url, ])) return review_request.id, review_request.absolute_url
def post_request(self, repository_info, repository, server_url, api_root, review_request_id=None, changenum=None, diff_content=None, parent_diff_content=None, commit_id=None, base_commit_id=None, submit_as=None, retries=3, base_dir=None): """Creates or updates a review request, and uploads a diff. On success the review request id and url are returned. """ supports_posting_commit_ids = \ self.tool.capabilities.has_capability('review_requests', 'commit_ids') if review_request_id: review_request = get_review_request( review_request_id, api_root, only_fields='absolute_url,bugs_closed,id,status,public', only_links='diffs,draft') if review_request.status == 'submitted': raise CommandError( 'Review request %s is marked as %s. In order to update ' 'it, please reopen the review request and try again.' % (review_request_id, review_request.status)) else: # No review_request_id, so we will create a new review request. try: # Until we are Python 2.7+ only, the keys in request_data have # to be bytes. See bug 3753 for details. request_data = {b'repository': repository} if changenum: request_data[b'changenum'] = changenum elif commit_id and supports_posting_commit_ids: request_data[b'commit_id'] = commit_id if submit_as: request_data[b'submit_as'] = submit_as if self.tool.can_bookmark: bookmark = self.tool.get_current_bookmark() request_data[b'extra_data__local_bookmark'] = bookmark elif self.tool.can_branch: branch = self.tool.get_current_branch() request_data[b'extra_data__local_branch'] = branch review_requests = api_root.get_review_requests( only_fields='', only_links='create') review_request = review_requests.create(**request_data) except APIError as e: if e.error_code == 204 and changenum: # The change number is already in use. Get the review # request for that change and update it instead. rid = e.rsp['review_request']['id'] review_request = api_root.get_review_request( review_request_id=rid, only_fields='absolute_url,bugs_closed,id,status', only_links='diffs,draft') else: raise CommandError('Error creating review request: %s' % e) if (not repository_info.supports_changesets or not self.options.change_only): try: diff_kwargs = { 'parent_diff': parent_diff_content, 'base_dir': base_dir, } if (base_commit_id and self.tool.capabilities.has_capability( 'diffs', 'base_commit_ids')): # Both the Review Board server and SCMClient support # base commit IDs, so pass that along when creating # the diff. diff_kwargs['base_commit_id'] = base_commit_id review_request.get_diffs(only_fields='').upload_diff( diff_content, **diff_kwargs) except APIError as e: error_msg = [ u'Error uploading diff\n\n', ] if e.error_code == 101 and e.http_status == 403: error_msg.append(u'You do not have permissions to modify ' u'this review request\n') elif e.error_code == 219: error_msg.append( u'The generated diff file was empty. This ' u'usually means no files were\n' u'modified in this change.\n') else: error_msg.append(str(e).decode('utf-8') + u'\n') error_msg.append( u'Your review request still exists, but the diff is ' u'not attached.\n') error_msg.append(u'%s\n' % review_request.absolute_url) raise CommandError(u'\n'.join(error_msg)) try: draft = review_request.get_draft(only_fields='commit_id') except APIError as e: raise CommandError('Error retrieving review request draft: %s' % e) # Stamp the commit message with the review request URL before posting # the review, so that we can use the stamped commit message when # guessing the description. This enables the stamped message to be # present on the review if the user has chosen to publish immediately # upon posting. if self.options.stamp_when_posting: if not self.tool.can_amend_commit: print('Cannot stamp review URL onto the commit message; ' 'stamping is not supported with %s.' % self.tool.name) else: try: stamp_commit_with_review_url(self.revisions, review_request.absolute_url, self.tool) print('Stamped review URL onto the commit message.') except AlreadyStampedError: print('Commit message has already been stamped') except Exception as e: logging.debug( 'Caught exception while stamping the ' 'commit message. Proceeding to post ' 'without stamping.', exc_info=True) print('Could not stamp review URL onto the commit ' 'message.') # If the user has requested to guess the summary or description, # get the commit message and override the summary and description # options. The guessing takes place after stamping so that the # guessed description matches the commit when rbt exits. if not self.options.diff_filename: self.check_guess_fields() # Update the review request draft fields based on options set # by the user, or configuration. update_fields = {} if self.options.target_groups: update_fields['target_groups'] = self.options.target_groups if self.options.target_people: update_fields['target_people'] = self.options.target_people if self.options.depends_on: update_fields['depends_on'] = self.options.depends_on if self.options.summary: update_fields['summary'] = self.options.summary if self.options.branch: update_fields['branch'] = self.options.branch if self.options.bugs_closed: # Append to the existing list of bugs. self.options.bugs_closed = self.options.bugs_closed.strip(', ') bug_set = (set(re.split('[, ]+', self.options.bugs_closed)) | set(review_request.bugs_closed)) self.options.bugs_closed = ','.join(bug_set) update_fields['bugs_closed'] = self.options.bugs_closed if self.options.description: update_fields['description'] = self.options.description if self.options.testing_done: update_fields['testing_done'] = self.options.testing_done if ((self.options.description or self.options.testing_done) and self.options.markdown and self.tool.capabilities.has_capability('text', 'markdown')): # The user specified that their Description/Testing Done are # valid Markdown, so tell the server so it won't escape the text. update_fields['text_type'] = 'markdown' if self.options.publish: update_fields['public'] = True if (self.options.trivial_publish and self.tool.capabilities.has_capability( 'review_requests', 'trivial_publish')): update_fields['trivial'] = True if self.options.change_description is not None: if review_request.public: update_fields['changedescription'] = \ self.options.change_description if (self.options.markdown and self.tool.capabilities.has_capability( 'text', 'markdown')): update_fields['changedescription_text_type'] = 'markdown' else: update_fields['changedescription_text_type'] = 'plain' else: logging.error( 'The change description field can only be set when ' 'publishing an update. Use --description instead.') if supports_posting_commit_ids and commit_id != draft.commit_id: update_fields['commit_id'] = commit_id or '' if update_fields: try: draft = draft.update(**update_fields) except APIError as e: raise CommandError( 'Error updating review request draft: %s\n\n' 'Your review request still exists, but the diff is not ' 'attached.\n\n' '%s\n' % (e, review_request.absolute_url)) return review_request.id, review_request.absolute_url
def post_request(self, repository_info, repository, server_url, api_root, review_request_id=None, changenum=None, diff_content=None, parent_diff_content=None, commit_id=None, base_commit_id=None, submit_as=None, retries=3, base_dir=None): """Creates or updates a review request, and uploads a diff. On success the review request id and url are returned. """ supports_posting_commit_ids = \ self.tool.capabilities.has_capability('review_requests', 'commit_ids') if review_request_id: review_request = get_review_request(review_request_id, api_root) if review_request.status == 'submitted': raise CommandError( 'Review request %s is marked as %s. In order to update ' 'it, please reopen the review request and try again.' % (review_request_id, review_request.status)) else: # No review_request_id, so we will create a new review request. try: request_data = {'repository': repository} if changenum: request_data['changenum'] = changenum elif commit_id and supports_posting_commit_ids: request_data['commit_id'] = commit_id if submit_as: request_data['submit_as'] = submit_as review_request = api_root.get_review_requests().create( **request_data) except APIError as e: if e.error_code == 204 and changenum: # Change number in use. rid = e.rsp['review_request']['id'] review_request = api_root.get_review_request( review_request_id=rid) if not self.options.diff_only: review_request = review_request.update( changenum=changenum) else: raise CommandError('Error creating review request: %s' % e) if (not repository_info.supports_changesets or not self.options.change_only): try: diff_kwargs = { 'parent_diff': parent_diff_content, 'base_dir': base_dir, } if (base_commit_id and self.tool.capabilities.has_capability( 'diffs', 'base_commit_ids')): # Both the Review Board server and SCMClient support # base commit IDs, so pass that along when creating # the diff. diff_kwargs['base_commit_id'] = base_commit_id review_request.get_diffs().upload_diff(diff_content, **diff_kwargs) except APIError as e: error_msg = [ u'Error uploading diff\n\n', ] if e.error_code == 101 and e.http_status == 403: error_msg.append(u'You do not have permissions to modify ' u'this review request\n') elif e.error_code == 219: error_msg.append( u'The generated diff file was empty. This ' u'usually means no files were\n' u'modified in this change.\n') else: error_msg.append(str(e).decode('utf-8') + u'\n') error_msg.append( u'Your review request still exists, but the diff is ' u'not attached.\n') error_msg.append(u'%s\n' % review_request.absolute_url) raise CommandError(u'\n'.join(error_msg)) try: draft = review_request.get_draft() except APIError as e: raise CommandError('Error retrieving review request draft: %s' % e) # Update the review request draft fields based on options set # by the user, or configuration. update_fields = {} if self.options.target_groups: update_fields['target_groups'] = self.options.target_groups if self.options.target_people: update_fields['target_people'] = self.options.target_people if self.options.depends_on: update_fields['depends_on'] = self.options.depends_on if self.options.summary: update_fields['summary'] = self.options.summary if self.options.branch: update_fields['branch'] = self.options.branch if self.options.bugs_closed: # Append to the existing list of bugs. self.options.bugs_closed = self.options.bugs_closed.strip(', ') bug_set = (set(re.split('[, ]+', self.options.bugs_closed)) | set(review_request.bugs_closed)) self.options.bugs_closed = ','.join(bug_set) update_fields['bugs_closed'] = self.options.bugs_closed if self.options.description: update_fields['description'] = self.options.description if self.options.testing_done: update_fields['testing_done'] = self.options.testing_done if ((self.options.description or self.options.testing_done) and self.options.markdown and self.tool.capabilities.has_capability('text', 'markdown')): # The user specified that their Description/Testing Done are # valid Markdown, so tell the server so it won't escape the text. update_fields['text_type'] = 'markdown' if self.options.change_description: update_fields['changedescription'] = \ self.options.change_description if self.options.publish: update_fields['public'] = True if supports_posting_commit_ids and commit_id != draft.commit_id: update_fields['commit_id'] = commit_id or '' if update_fields: try: draft = draft.update(**update_fields) except APIError as e: raise CommandError(u'\n'.join([ u'Error updating review request draft: %s\n' % e, u'Your review request still exists, but the diff is ' u'not attached.\n', u'%s\n' % review_request.absolute_url, ])) return review_request.id, review_request.absolute_url
def main(self, branch_name=None, *args): """Run the command.""" self.cmd_args = list(args) if branch_name: self.cmd_args.insert(0, branch_name) repository_info, self.tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, self.tool) api_client, api_root = self.get_api(server_url) self.setup_tool(self.tool, api_root=api_root) dry_run = self.options.dry_run # Check if repository info on reviewboard server match local ones. repository_info = repository_info.find_server_repository_info(api_root) if (not self.tool.can_merge or not self.tool.can_push_upstream or not self.tool.can_delete_branch): raise CommandError( "This command does not support %s repositories." % self.tool.name) if self.tool.has_pending_changes(): raise CommandError('Working directory is not clean.') if self.options.rid: request_id = self.options.rid is_local = branch_name is not None else: request = guess_existing_review_request( repository_info, self.options.repository_name, api_root, api_client, self.tool, get_revisions(self.tool, self.cmd_args), guess_summary=False, guess_description=False, is_fuzzy_match_func=self._ask_review_request_match) if not request or not request.id: raise CommandError('Could not determine the existing review ' 'request URL to land.') request_id = request.id is_local = True if self.options.is_local is not None: is_local = self.options.is_local destination_branch = self.options.destination_branch if not destination_branch: raise CommandError('Please specify a destination branch.') if is_local: if branch_name is None: branch_name = self.tool.get_current_branch() if branch_name == destination_branch: raise CommandError('The local branch cannot be merged onto ' 'itself. Try a different local branch or ' 'destination branch.') review_request = get_review_request(request_id, api_root) try: is_rr_approved = review_request.approved approval_failure = review_request.approval_failure except AttributeError: # The Review Board server is an old version (pre-2.0) that # doesn't support the `approved` field. Determining it manually. if review_request.ship_it_count == 0: is_rr_approved = False approval_failure = \ 'The review request has not been marked "Ship It!"' else: is_rr_approved = True finally: if not is_rr_approved: raise CommandError(approval_failure) if is_local: review_commit_message = extract_commit_message(review_request) author = review_request.get_submitter() if self.options.squash: print('Squashing branch "%s" into "%s"' % (branch_name, destination_branch)) else: print('Merging branch "%s" into "%s"' % (branch_name, destination_branch)) if not dry_run: try: self.tool.merge( branch_name, destination_branch, review_commit_message, author, self.options.squash, self.options.edit) except MergeError as e: raise CommandError(str(e)) if self.options.delete_branch: print('Deleting merged branch "%s"' % branch_name) if not dry_run: self.tool.delete_branch(branch_name, merged_only=False) else: print('Applying patch from review request %s' % request_id) if not dry_run: self.patch(request_id) if self.options.push: print('Pushing branch "%s" upstream' % destination_branch) if not dry_run: try: self.tool.push_upstream(destination_branch) except PushError as e: raise CommandError(str(e)) print('Review request %s has landed on "%s".' % (request_id, destination_branch))
def main(self, branch_name=None, *args): """Run the command.""" self.cmd_args = list(args) if branch_name: self.cmd_args.insert(0, branch_name) repository_info, self.tool = self.initialize_scm_tool( client_name=self.options.repository_type) server_url = self.get_server_url(repository_info, self.tool) api_client, api_root = self.get_api(server_url) self.setup_tool(self.tool, api_root=api_root) dry_run = self.options.dry_run # Check if repository info on reviewboard server match local ones. repository_info = repository_info.find_server_repository_info(api_root) if (not self.tool.can_merge or not self.tool.can_push_upstream or not self.tool.can_delete_branch): raise CommandError( "This command does not support %s repositories." % self.tool.name) if self.tool.has_pending_changes(): raise CommandError('Working directory is not clean.') if self.options.rid: request_id = self.options.rid is_local = branch_name is not None else: request = guess_existing_review_request( repository_info, self.options.repository_name, api_root, api_client, self.tool, get_revisions(self.tool, self.cmd_args), guess_summary=False, guess_description=False, is_fuzzy_match_func=self._ask_review_request_match) if not request or not request.id: raise CommandError('Could not determine the existing review ' 'request URL to land.') request_id = request.id is_local = True if self.options.is_local is not None: is_local = self.options.is_local destination_branch = self.options.destination_branch if not destination_branch: raise CommandError('Please specify a destination branch.') if is_local: if branch_name is None: branch_name = self.tool.get_current_branch() if branch_name == destination_branch: raise CommandError('The local branch cannot be merged onto ' 'itself. Try a different local branch or ' 'destination branch.') review_request = get_review_request(request_id, api_root) try: is_rr_approved = review_request.approved approval_failure = review_request.approval_failure except AttributeError: # The Review Board server is an old version (pre-2.0) that # doesn't support the `approved` field. Determining it manually. if review_request.ship_it_count == 0: is_rr_approved = False approval_failure = \ 'The review request has not been marked "Ship It!"' else: is_rr_approved = True finally: if not is_rr_approved: raise CommandError(approval_failure) if is_local: review_commit_message = extract_commit_message(review_request) author = review_request.get_submitter() if self.options.squash: print('Squashing branch "%s" into "%s"' % (branch_name, destination_branch)) else: print('Merging branch "%s" into "%s"' % (branch_name, destination_branch)) if not dry_run: try: self.tool.merge(branch_name, destination_branch, review_commit_message, author, self.options.squash, self.options.edit) except MergeError as e: raise CommandError(str(e)) if self.options.delete_branch: print('Deleting merged branch "%s"' % branch_name) if not dry_run: self.tool.delete_branch(branch_name, merged_only=False) else: print('Applying patch from review request %s' % request_id) if not dry_run: self.patch(request_id) if self.options.push: print('Pushing branch "%s" upstream' % destination_branch) if not dry_run: try: self.tool.push_upstream(destination_branch) except PushError as e: raise CommandError(str(e)) print('Review request %s has landed on "%s".' % (request_id, destination_branch))