def get_root(): settings = get_config() review = settings['reviewboard'] client = RBClient(review['server']) client.login(review['user'], review['password']) return client.get_root()
def setUp(self): super(SVNRepositoryInfoTests, self).setUp() self.spy_on(urlopen, call_fake=self._urlopen) self.api_client = RBClient('http://localhost:8080/') self.root_resource = self.api_client.get_root()
def setUp(self): super(SVNRepositoryInfoTests, self).setUp() def _urlopen(url, **kwargs): url = url.get_full_url() try: payload = self.payloads[url] except KeyError: return MockResponse( 404, {}, json.dumps({ 'rsp': { 'stat': 'fail', 'err': { 'code': 100, 'msg': 'Object does not exist', }, }, })) return MockResponse(200, { 'Content-Type': payload['mimetype'], }, json.dumps(payload['rsp'])) self.spy_on(urlopen, call_fake=_urlopen) self.api_client = RBClient('http://localhost:8080/') self.root_resource = self.api_client.get_root()
def setUp(self): super(RepositoryMatchTests, self).setUp() @self.spy_for(urlopen) def _urlopen(url, **kwargs): url = url.get_full_url() try: payload = self.payloads[url] except KeyError: print('Test requested unexpected URL "%s"' % url) return MockResponse( 404, {}, json.dumps({ 'rsp': { 'stat': 'fail', 'err': { 'code': 100, 'msg': 'Object does not exist', }, }, })) return MockResponse(200, { 'Content-Type': payload['mimetype'], }, json.dumps(payload['rsp'])) self.api_client = RBClient('http://localhost:8080/') self.root_resource = self.api_client.get_root()
def rb_client(self): options = {} if os.path.exists(".reviewboardrc"): with open(".reviewboardrc") as reviewboardrc: for line in reviewboardrc: if line.startswith("#"): continue if len(line.strip()) == 0: continue k, v = line.strip().split("=") k = k.strip() v = eval(v.strip()) options[k] = v rbclient = RBClient(options.get('REVIEWBOARD_URL') or 'https://reviews.apache.org/', RetryingSyncTransport) if not rbclient.get_root().get_session()['authenticated']: username = self.opt.reviewboard_username[0] if self.opt.reviewboard_username and \ self.opt.reviewboard_username[0] else raw_input( "Enter review board Username: "******"Enter password for %s: " % username) rbclient.login(username, password) root = rbclient.get_root() root.repository = options.get('REPOSITORY') or None root.branch = options.get('BRANCH') or options.get('TRACKING_BRANCH') root.target_groups = None if options.has_key('TARGET_GROUPS'): root.target_groups = options['TARGET_GROUPS'] return root
def _get_client(self, username=None, password=None, anonymous=False): from rbtools.api.client import RBClient from rbtools.api.transport.sync import SyncTransport class NoCacheTransport(SyncTransport): """API transport with disabled caching.""" def enable_cache(self): pass # TODO consider moving this to __init__. if not self.mr: raise Exception('Could not find MozReview cluster instance') if username is None or password is None: username = os.environ.get('BUGZILLA_USERNAME') password = os.environ.get('BUGZILLA_PASSWORD') # RBClient is persisting login cookies from call to call # in $HOME/.rbtools-cookies. We want to be able to easily switch # between users, so we clear that cookie between calls to the # server and reauthenticate every time. try: os.remove(os.path.join(os.environ.get('HOME'), '.rbtools-cookies')) except Exception: pass if anonymous: return RBClient(self.mr.reviewboard_url, transport_cls=NoCacheTransport) return RBClient(self.mr.reviewboard_url, username=username, password=password, transport_cls=NoCacheTransport)
def main(url, group_names, days_old=7, dry_run=False): """ do something """ try: user = os.environ['RBUSER'] except KeyError: raise SystemExit("please set RBUSER environment variable for reviewboard user") try: passwd = os.environ['RBPASS'] except KeyError: raise SystemExit("please set RBPASS environment variable for reviewboard password") #client = RBClient(url, user, passwd) client = RBClient(url) root = client.get_root() if not root: raise SystemExit("Error - could not get RBClient root.") for g_name in group_names: o = get_group_id_by_name(root, g_name, dry_run=dry_run) if not o: raise SystemExit("ERROR: no group '%s' found." % g_name) logger.debug("Found group '%s' id=%d" % (g_name, o)) reviews = get_reviews_for_groups(root, group_names, dry_run=dry_run) old_reviews = filter_reviews_older_than(root, reviews, days_old, dry_run=dry_run) logger.info("found %d reviews for target groups and last updated %d or more days ago" % (len(old_reviews), days_old)) if len(old_reviews) < 1: logger.info("Found no reviews matching criteria, exiting") return False users = get_submitters_for_reviews(old_reviews) logger.debug("got user information for %d users" % len(users)) recipients = [] for u in users: recipients.append("{u.fullname} <{u.email}>".format(u=users[u])) table = generate_report_html_table(old_reviews, url) body = "<h1>ReviewBoard reminder</h1>\n" body += """<p>You're receiving this message because you have one or more pending code reviews on <a href="{url}">{url}</a> targeted at the '{group_names}' group(s) that have not been updated in over {days_old} days and have not been submitted. At your convenience, please evaluate these reviews and close/submit any that have been merged or discarded. Thank You.</p>\n""".format(url=url, days_old=days_old, group_names=", ".join(group_names)) body += table body += "\n<br />\n" host = node() user = getuser() body += """ <p><em>generated by <a href=\"https://github.com/jantman/misc-scripts/blob/master/reviewboard_reminder_mail.py">reviewboard_reminder_mail.py</a> running on {host} as {user} at {ds}</em></p> """.format(host=host, user=user, ds=datetime.datetime.now().isoformat()) if dry_run: print("Message to send:\n##############################\n{msg}\n#################################\n".format(msg=body)) print("Would send to:\n {to}".format(to=", ".join(recipients))) else: raise SystemExit("Oops - never actually implemented the mail sending...") return True
def ReviewBoardClient(url, username=None, password=None, apikey=None): """Obtain a RBClient instance via a context manager. This exists as a context manager because of gross hacks necessary for dealing with cookies. ``RBClient`` is coded such that it assumes it is being used under a user account and storing cookies is always acceptable. There is no way to disable cookie file writing or to tell it to use a file object (such as BytesIO) as the cookies database. We work around this deficiency by creating a temporary file and using it as the cookies database for the lifetime of the context manager. When the context manager exits, the temporary cookies file is deleted. """ fd, path = tempfile.mkstemp() os.close(fd) try: if username and apikey: rbc = RBClient(url, cookie_file=path, transport_cls=NoCacheTransport) login_resource = rbc.get_path( 'extensions/mozreview.extension.MozReviewExtension/' 'bugzilla-api-key-logins/') login_resource.create(username=username, api_key=apikey) else: rbc = RBClient(url, username=username, password=password, cookie_file=path, transport_cls=NoCacheTransport) yield rbc finally: try: os.unlink(path) except Exception: pass
def __init__(self, configuration, *args): ''' Helper to build an RBTools api root used by MozReview below ''' url, api_key, username = self.requires(configuration, 'url', 'api_key', 'username') # Authenticate client client = RBClient(url, save_cookies=False, allow_caching=False) login_resource = client.get_path( 'extensions/mozreview.extension.MozReviewExtension/' 'bugzilla-api-key-logins/') login_resource.create(username=username, api_key=api_key) self.api = client.get_root() # Report issues from specific analyzers self.analyzers = list( filter( lambda a: a in (CLANG_TIDY, CLANG_FORMAT, MOZLINT), configuration.get('analyzers', [ CLANG_TIDY, ]), )) assert len(self.analyzers) > 0, \ 'No valid analyzers for mozreview' self.publish_success = configuration.get('publish_success', False) assert isinstance(self.publish_success, bool) logger.info('Mozreview report enabled', url=url, username=username, analyzers=self.analyzers)
def process_review_requests(client, channel, nick, review_ids): """ Processes a list of review request ids using a shared client """ logger.info("Starting codereview of: {0}".format(review_ids)) api = RBClient(settings.CODEREVIEW_REVIEWBOARD_API_URL, username=settings.CODEREVIEW_REVIEWBOARD_API_USERNAME, password=settings.CODEREVIEW_REVIEWBOARD_API_PASSWORD) try: api_root = api.get_root() except: logger.exception("Cannot access reviewboard") client.msg( channel, "I can't complete your review {0} because I can't access reviewboard".format(nick) ) return errors = [] for review_id in review_ids: try: do_review(client, channel, nick, api_root, review_id) except: logger.exception("Cannot codereview cr{0}".format(review_id)) errors.append(review_id) if errors: cr_list = ', '.join(map(lambda id: 'cr{0}'.format(id), errors)) client.msg(channel, 'Codereview complete {0}, but I was unable to review: {1}'.format(nick, cr_list)) else: client.msg(channel, 'Codereview complete {0}'.format(nick))
def get_api(server_url, **kwargs): """Returns an RBClient instance and the associated root resource. Hooks should use this method to gain access to the API, instead of instantiating their own client. Args: server_url (unicode): The server URL to retrieve. **kwargs (dict): Additional keyword arguments to pass to the :py:class:`~rbtools.api.client.RBClient` constructor. See :py:meth:`SyncTransport.__init__() <rbtools.api.transport.sync.SyncTransport.__init__>` for arguments that are accepted. Returns: tuple: This returns a 2-tuple of the :py:class:`~rbtools.api.client.RBClient` and :py:class:`<root resource> rbtools.api.resource.Resource`. """ api_client = RBClient(server_url, **kwargs) try: api_root = api_client.get_root() except ServerInterfaceError as e: raise HookError('Could not reach the Review Board server at %s: %s' % (server_url, e)) except APIError as e: raise HookError('Unexpected API Error: %s' % e) return api_client, api_root
def download_reviews(self, dev_group): self.dev_group = dev_group root = RBClient(self.url, api_token=self.token).get_root() reviews = root.get_review_requests(to_groups=dev_group, status='pending', counts_only=True) reviews = root.get_review_requests(to_groups=dev_group, status='pending', max_results=reviews.count) self.reviews = [self.RawReviewInfo(r) for r in reviews]
def ship_it(self, rrid, username, password): """Create and publish a ship-it review""" # TODO: this code is lifted from the reviewboard mach commands. If we # need to make more reviewboard api calls in these tests, we # should make a function in the base class to get a RBClient. class NoCacheTransport(SyncTransport): """API transport with disabled caching.""" def enable_cache(self): pass # RBClient is persisting login cookies from call to call # in $HOME/.rbtools-cookies. We want to be able to easily switch # between users, so we clear that cookie between calls to the # server and reauthenticate every time. try: os.remove(os.path.join(os.environ.get('HOME'), '.rbtools-cookies')) except Exception: pass client = RBClient(self.rburl, username=username, password=password, transport_cls=NoCacheTransport) root = client.get_root() reviews = root.get_reviews(review_request_id=rrid) reviews.create(public=True, ship_it=True)
def ReviewBoardClient(url, username=None, password=None, apikey=None): """Obtain a RBClient instance.""" if username and apikey: rbc = RBClient(url, save_cookies=False, allow_caching=False) login_resource = rbc.get_path("extensions/mozreview.extension.MozReviewExtension/" "bugzilla-api-key-logins/") login_resource.create(username=username, api_key=apikey) else: rbc = RBClient(url, username=username, password=password, save_cookies=False, allow_caching=False) return rbc
def main(): ''' Posts a review to the review with specified id indicating if the build passed or failed ''' parser = argparse.ArgumentParser() parser.add_argument("--cfg", required=True, help="Configuration file") parser.add_argument("--reviewid", required=True, help="Review id to post to") parser.add_argument("--buildurl", required=True, help="The jenkins build url") parser.add_argument("--buildstate", required=True, choices=["SUCCESS", "UNSTABLE", "FAILURE"], help="Indicates if build succeeded (1) or failed (0)") args = parser.parse_args() reviewid = args.reviewid buildurl = args.buildurl buildstate = args.buildstate config = ConfigParser.ConfigParser() try: config.read(args.cfg) client = RBClient(config.get('jrbb', 'reviewboard_server'), username=config.get('jrbb', 'reviewboard_user'), api_token=config.get('jrbb', 'reviewboard_apitoken')) except ConfigParser.NoSectionError: print "Configuration file " + args.cfg + " not found or missing items" exit(1) root = client.get_root() # Get the revision number of the latest diff from the review # pylint: disable=no-member reviewreq = root.get_review_request(review_request_id=reviewid) reviews = reviewreq.get_reviews() if buildstate == 'SUCCESS': msg = 'Successfully built changes. See ' + buildurl else: msg = 'Opps! I could not build these changes. See ' + buildurl reviews.create(body_bottom=msg, public=True) print "Posted to review " + reviewid + " build state=" + buildstate + \ ". Build url=" + buildurl
def build_api_root(url, username, api_key): ''' Helper to build an RBTools api root used by BatchReview ''' logger.info('Authenticate on Mozreview', url=url, username=username) client = RBClient(url, save_cookies=False, allow_caching=False) login_resource = client.get_path( 'extensions/mozreview.extension.MozReviewExtension/' 'bugzilla-api-key-logins/') login_resource.create(username=username, api_key=api_key) return client.get_root()
def __init__(self, url): self.client = RBClient(url) self.root = self.client.get_root() try: self._version = float( self.root.rsp['product']['version'].split()[0]) except: self._version = 0.0 self._templates = self.root.rsp['uri_templates'] self._files = {} self._file_data = {} self._simplefile_data = {}
def get_api(server_url, username, password): """Returns an RBClient instance and the associated root resource. Hooks should use this method to gain access to the API, instead of instantianting their own client. """ api_client = RBClient(server_url, username=username, password=password) try: api_root = api_client.get_root() except ServerInterfaceError, e: raise HookError('Could not reach the Review Board server at %s: %s' % (server_url, e))
def run(self): while True: id = q.get() client = RBClient(reviewboard_url, api_token=api_token) root = client.get_root() review = root.get_review_request(review_request_id=id) comments = review.get_reviews(max_results=200) for i, c in enumerate(comments): if i == len(comments) - 1: break if c.links.user.title == jenkins_uname and c.ship_it: c.update(ship_it=False)
def get_root(self, server_url): """Returns the root resource of an RBClient.""" cookie_file = self.get_cookie() self.rb_api = RBClient(server_url, cookie_file=cookie_file, username=self.options.username, password=self.options.password, auth_callback=self.credentials_prompt) root = None try: root = self.rb_api.get_root() except ServerInterfaceError, e: die("Could not reach the review board server at %s" % server_url)
def main(): ''' Fetches the latest patch diff from the review given the review id passed as a parameter and writes it an output file. ''' parser = argparse.ArgumentParser() parser.add_argument("--reviewid", required=True, help="review id to post to") parser.add_argument("--cfg", required=True, help="Configuration file") parser.add_argument("--out", required=True, help="Output file location (e.g. patch.diff)") args = parser.parse_args() reviewid = args.reviewid config = ConfigParser.ConfigParser() try: config.read(args.cfg) client = RBClient(config.get('jrbb', 'reviewboard_server'), username=config.get('jrbb', 'reviewboard_user'), api_token=config.get('jrbb', 'reviewboard_apitoken')) except ConfigParser.NoSectionError: print "Configuration file " + args.cfg + " not found or missing items" exit(1) root = client.get_root() # Get the revision number of the latest diff from the review # pylint: disable=no-member reviewrequest = root.get_review_request(review_request_id=reviewid) diffrevision = reviewrequest.get_latest_diff().revision print "Latest diff revision for review", reviewid, "is", diffrevision diff = root.get_diff(review_request_id=reviewid, diff_revision=diffrevision) patch = diff.get_patch() print "Retrieved the following patch file" print "-------------------------------------------------------------------" print patch.data print "-------------------------------------------------------------------" outfile = open(args.out, "w") print >> outfile, patch.data outfile.close() print "Patch written to " + args.out
def ProcessReviewRequest(payload, tool_settings): """Execute an automated review on a review request.""" routing_key = ProcessReviewRequest.request.delivery_info['routing_key'] route_parts = routing_key.partition('.') tool_ep = route_parts[0] logger.info("Request to execute review tool '%s' for %s" % (tool_ep, payload['url'])) try: logger.info("Initializing RB API") api_client = RBClient(payload['url'], cookie_file=COOKIE_FILE, agent=AGENT, session=payload['session']) api_root = api_client.get_root() except: logger.error("Could not contact RB server at '%s'" % payload['url']) return False logger.info("Loading requested tool '%s'" % tool_ep) tools = [] for ep in pkg_resources.iter_entry_points(group='reviewbot.tools', name=tool_ep): tools.append(ep.load()) if len(tools) > 1: _update_tool_execution(api_root, payload['request'], status=FAILED, msg="Tool '%s' is ambiguous" % tool_ep) return False elif len(tools) == 0: _update_tool_execution(api_root, payload['request'], status=FAILED, msg="Tool '%s' not found" % tool_ep) return False tool = tools[0] try: logger.info("Initializing review") review = Review(api_root, payload['request'], payload['review_settings']) except Exception, e: _update_tool_execution(api_root, payload['request'], status=FAILED, msg="Error initializing review: %s" % str(e)) return False
def get_open_reviews(args): # get open reviews to a specified user, group, etc. args['status'] = 'pending' args['max_results'] = MAX_RESULTS client = RBClient(RB_URL) root = client.get_root() if not root: print "Error - could not get RBClient root." return False req = root.get_review_requests(**args) print "\n\nGot %d pending/unsubmitted reviews" % req.total_results for review in req: print "%d - %s - %s" % (review.id, review.get_submitter().username, review.summary)
def shipit(use_merge): subprocess.check_call(["git", "fetch"]) if use_merge: subprocess.check_call(["git", "merge", "origin/master"]) else: subprocess.check_call(["git", "rebase", "origin/master"]) # run spec try: if os.path.exists("scripts/test.sh"): subprocess.check_call(["scripts/test.sh"]) except subprocess.CalledProcessError: exit(1) branch = current_branch() rid = get_branch_info("rid") if not rid: print "You don't have a review branch." exit(1) else: rid = int(rid) # git checkout master subprocess.check_call(["git", "checkout", "master"]) # git pull origin master subprocess.check_call(["git", "pull", "origin", "master"]) # git merge current_branch subprocess.check_call(["git", "merge", branch]) # git push origin master subprocess.check_call(["git", "push", "origin", "master"]) # git push origin :current_branch subprocess.check_call(["git", "push", "origin", ":" + branch]) # git branch -D current_branch subprocess.check_call(["git", "branch", "-D", branch]) # close reviewboard request from rbtools.api.client import RBClient client = RBClient('http://reviewboard.nodeswork.com') root = client.get_root() request = root.get_review_request(review_request_id=rid) request.update(status="submitted")
def setUp(self): super(SVNRepositoryInfoTests, self).setUp() def _urlopen(url, **kwargs): url = url.get_full_url() try: payload = self.payloads[url] except KeyError: return MockResponse(404, {}, json.dumps({ 'rsp': { 'stat': 'fail', 'err': { 'code': 100, 'msg': 'Object does not exist', }, }, })) return MockResponse( 200, { 'Content-Type': payload['mimetype'], }, json.dumps(payload['rsp'])) self.spy_on(urlopen, call_fake=_urlopen) self.api_client = RBClient('http://localhost:8080/') self.root_resource = self.api_client.get_root()
def ReviewBoardClient(url, username=None, password=None, apikey=None): """Obtain a RBClient instance.""" if username and apikey: rbc = RBClient(url, save_cookies=False, allow_caching=False) login_resource = rbc.get_path( 'extensions/mozreview.extension.MozReviewExtension/' 'bugzilla-api-key-logins/') login_resource.create(username=username, api_key=apikey) else: rbc = RBClient(url, username=username, password=password, save_cookies=False, allow_caching=False) return rbc
def run(old_value, new_value, ref): diff = call_cmd("git diff %s..%s"%(old_value, new_value)) info(diff) ci_range = "%s..%s"%(old_value, new_value) # get author name cmd = "git log --format=%cn -1 " + new_value author = call_cmd(cmd).strip() if author in AUTHOR_MAP: author = AUTHOR_MAP[author] reviewer = REVIEWER_MAP[author] # get summary desc cmd = "git log --format=%s " + ci_range logs = call_cmd(cmd) summary = logs.split(os.linesep)[0] cmd = "git log --pretty=fuller " + ci_range desc = call_cmd(cmd) summary = summary.replace("\"", "@") desc = desc.replace("\"", "@") repo_branch = ref.split("/")[-1] # 创建review_request client = RBClient(rbcfg["rbserver"], username=rbcfg["rbadmin"], password=rbcfg["rbadminpw"]) root = client.get_root() request_data = { "repository" : rbcfg["rbrepo"], "submit_as" : author, } r = root.get_review_requests().create(**request_data) vl = root.get_diff_validation() basedir = "/" #info("------------------"+diff) vl.validate_diff(rbcfg["rbrepo"], diff, base_dir=basedir) r.get_diffs().upload_diff(diff, base_dir=basedir) draft = r.get_draft() update_data = { "branch" : repo_branch, "summary" : summary, "description" : desc, "target_people" : reviewer, "public" : True, } ret = draft.update(**update_data) info("repo:<%s> rev:<%s> rid:<%s>"%(rbcfg["rbserver"], ci_range, r.id))
def get_rb_client(self): if not self.rb_client: options = {} with open(".reviewboardrc") as reviewboardrc: for line in reviewboardrc: if line.startswith("#"): continue if len(line.strip()) == 0: continue k, v = line.strip().split("=") k = k.strip() v = eval(v.strip()) options[k] = v rbclient = RBClient(options['REVIEWBOARD_URL']) self.repository = options['REPOSITORY'] self.branch = options.get('BRANCH') or options.get('TRACKING_BRANCH') self.target_groups = None if options.has_key('TARGET_GROUPS'): self.target_groups = options['TARGET_GROUPS'] if rbclient.get_root().get_session()['authenticated']: return rbclient.get_root() username = self.opt.reviewboard_username or raw_input("Enter review board Username: "******"Enter password: ") rbclient.login(username, password) self.rb_client = rbclient.get_root() return self.rb_client
def ProcessReviewRequest(payload, tool_settings): """Execute an automated review on a review request.""" routing_key = ProcessReviewRequest.request.delivery_info['routing_key'] route_parts = routing_key.partition('.') tool_ep = route_parts[0] logger.info( "Request to execute review tool '%s' for %s" % ( tool_ep, payload['url'])) try: logger.info("Initializing RB API") api_client = RBClient( payload['url'], cookie_file=COOKIE_FILE, agent=AGENT, session=payload['session']) api_root = api_client.get_root() except: logger.error("Could not contact RB server at '%s'" % payload['url']) return False logger.info("Loading requested tool '%s'" % tool_ep) tools = [] for ep in pkg_resources.iter_entry_points(group='reviewbot.tools', name=tool_ep): tools.append(ep.load()) if len(tools) > 1: _update_tool_execution(api_root, payload['request'], status=FAILED, msg="Tool '%s' is ambiguous" % tool_ep) return False elif len(tools) == 0: _update_tool_execution(api_root, payload['request'], status=FAILED, msg="Tool '%s' not found" % tool_ep) return False tool = tools[0] try: logger.info("Initializing review") review = Review(api_root, payload['request'], payload['review_settings']) except Exception, e: _update_tool_execution(api_root, payload['request'], status=FAILED, msg="Error initializing review: %s" % str(e)) return False
def get_open_reviews(args): """ get open reviews to a specified user, group, etc. """ args['status'] = 'pending' if 'max_results' not in args: args['max_results'] = 100 client = RBClient(RB_URL) root = client.get_root() if not root: print "Error - could not get RBClient root." return False req = root.get_review_requests(**args) ret = {'total': req.total_results, 'reviews': []} for review in req: ret['reviews'].append("(%s) %s <%s/r/%d/>" % (review.get_submitter().username, review.summary, RB_URL, review.id)) return ret
def update_tools_list(panel, payload): """Update the RB server with installed tools. This will detect the installed analysis tool plugins and inform Review Board of them. """ logging.info("Request to refresh installed tools from '%s'" % payload['url']) logging.info("Iterating Tools") tools = [] for ep in pkg_resources.iter_entry_points(group='reviewbot.tools'): entry_point = ep.name tool_class = ep.load() tool = tool_class() logging.info("Tool: %s" % entry_point) if tool.check_dependencies(): tools.append({ 'name': tool_class.name, 'entry_point': entry_point, 'version': tool_class.version, 'description': tool_class.description, 'tool_options': json.dumps(tool_class.options), }) else: logging.warning("%s dependency check failed." % ep.name) logging.info("Done iterating Tools") tools = json.dumps(tools) hostname = panel.hostname try: api_client = RBClient( payload['url'], cookie_file=COOKIE_FILE, agent=AGENT, session=payload['session']) api_root = api_client.get_root() except Exception, e: logging.error("Could not reach RB server: %s" % str(e)) return {'error': 'Could not reach RB server.'}
def _make_api_client(self, server_url): """Return an RBClient object for the server. The RBClient will be instantiated with the proper arguments for talking to the provided Review Board server url. """ return RBClient(server_url, username=self.options.username, password=self.options.password, auth_callback=self.credentials_prompt)
def get_rb_client(self): options = {} with open(".reviewboardrc") as reviewboardrc: for line in reviewboardrc: k, v = line.split("=") k = k.strip() v = eval(v.strip()) options[k] = v rbclient = RBClient(options['REVIEWBOARD_URL']) self.repository = options['REPOSITORY'] self.branch = options['BRANCH'] self.target_groups = None if options.has_key('TARGET_GROUPS'): self.target_groups = options['TARGET_GROUPS'] if rbclient.get_root().get_session()['authenticated']: return rbclient username = raw_input("Enter review board Username: "******"Enter password: ") rbclient.login(username, password) return rbclient
def rb_client(self): options = {} if os.path.exists(".reviewboardrc"): with open(".reviewboardrc") as reviewboardrc: for line in reviewboardrc: if line.startswith("#"): continue if len(line.strip()) == 0: continue k, v = line.strip().split("=") k = k.strip() v = eval(v.strip()) options[k] = v rbclient = RBClient( options.get('REVIEWBOARD_URL') or 'https://reviews.apache.org/', RetryingSyncTransport) if not rbclient.get_root().get_session()['authenticated']: username = self.opt.reviewboard_username[0] if self.opt.reviewboard_username and \ self.opt.reviewboard_username[0] else raw_input( "Enter review board Username: "******"Enter password for %s: " % username) rbclient.login(username, password) root = rbclient.get_root() root.repository = options.get('REPOSITORY') or None root.branch = options.get('BRANCH') or options.get('TRACKING_BRANCH') root.target_groups = None if options.has_key('TARGET_GROUPS'): root.target_groups = options['TARGET_GROUPS'] return root
def fetch_repositories(url, user=None, token=None): """Fetch repositories from Review Board. Args: url (unicode): The configured url for the connection. user (unicode): The configured user for the connection. token (unicode): The configured API token for the user. """ logging.info('Fetching repositories from Review Board: %s', url) # TODO: merge with COOKIE_FILE/AGENT in tasks.py root = RBClient(url, username=user, api_token=token, cookie_file='reviewbot-cookies.txt', agent='ReviewBot').get_root() for tool_type in ('Mercurial', 'Git'): repos = root.get_repositories(tool=tool_type, only_links='', only_fields='path,mirror_path,name') for repo in repos.all_items: repo_source = None for path in (repo.path, repo.mirror_path): if (os.path.exists(path) or path.startswith('http') or path.startswith('git')): repo_source = path break if repo_source: init_repository(repo.name, tool_type.lower(), repo_source) else: logging.warn('Cannot find usable path for repository: %s', repo.name)
def get_open_reviews(args): """ get open reviews to a specified user, group, etc. """ args['status'] = 'pending' if 'max_results' not in args: args['max_results'] = 100 client = RBClient(REVIEWBOARD_URL) # If we have a username and password, login if REVIEWBOARD_USERNAME and REVIEWBOARD_PASSWORD: client.login(REVIEWBOARD_USERNAME, REVIEWBOARD_PASSWORD) root = client.get_root() if not root: logger.error(u'Could not get RBClient root') return None try: req = root.get_review_requests(**args) except APIError: logger.exception(u'Error querying API') return None ret = {'total': req.total_results, 'reviews': []} review_fmt = u"[{user}] {summary} ({url}/r/{id})" for review in req: ret['reviews'].append(review_fmt.format(user=review.get_submitter().username, summary=review.summary, url=REVIEWBOARD_URL, id=review.id)) return ret
def process_review_requests(client, channel, nick, review_ids): """ Processes a list of review request ids using a shared client """ logger.info("Starting codereview of: {0}".format(review_ids)) api = RBClient(settings.CODEREVIEW_REVIEWBOARD_API_URL, username=settings.CODEREVIEW_REVIEWBOARD_API_USERNAME, password=settings.CODEREVIEW_REVIEWBOARD_API_PASSWORD) try: api_root = api.get_root() except: logger.exception("Cannot access reviewboard") client.msg( channel, "I can't complete your review {0} because I can't access reviewboard" .format(nick)) return errors = [] for review_id in review_ids: try: do_review(client, channel, nick, api_root, review_id) except: logger.exception("Cannot codereview cr{0}".format(review_id)) errors.append(review_id) if errors: cr_list = ', '.join(map(lambda id: 'cr{0}'.format(id), errors)) client.msg( channel, 'Codereview complete {0}, but I was unable to review: {1}'.format( nick, cr_list)) else: client.msg(channel, 'Codereview complete {0}'.format(nick))
def _make_api_client(self, server_url): """Return an RBClient object for the server. The RBClient will be instantiated with the proper arguments for talking to the provided Review Board server url. """ return RBClient(server_url, username=self.options.username, password=self.options.password, api_token=self.options.api_token, auth_callback=self.credentials_prompt, otp_token_callback=self.otp_token_prompt, disable_proxy=not self.options.enable_proxy, verify_ssl=not self.options.disable_ssl_verification, allow_caching=not self.options.disable_cache, cache_location=self.options.cache_location, in_memory_cache=self.options.in_memory_cache, save_cookies=self.options.save_cookies, ext_auth_cookies=self.options.ext_auth_cookies)
def run(): look_args = " -r %s %s"%(rev, repo) # 是否为感兴趣的分支 cmd = "svnlook dirs-changed %s"%look_args changed_dir = call_cmd(cmd).split(os.linesep)[0].strip() # 取第一行即可 repo_branch = _process_branch(changed_dir) if not repo_branch: exit() # 是否有代码文件 interested = False cmd = "svnlook changed %s"%look_args files = call_cmd(cmd) for f in files.split(os.linesep): if f.strip().endswith(rbcfg["filter_suffixs"]): interested = True break if not interested: exit() # 提取rbt post信息 cmd = "svnlook author %s"%look_args author = call_cmd(cmd).strip() if author in AUTHOR_MAP: author = AUTHOR_MAP[author] reviewer = REVIEWER_MAP[author] cmd = "svnlook log %s"%look_args log = call_cmd(cmd) summary = desc = log.strip().replace(os.linesep, "&").replace("\"", "@") summary = "rev:%s-[%s]"%(rev, summary) cmd = "svnlook diff %s"%look_args diff = call_cmd(cmd) diff = _process_diff(diff) #info("\n"+diff) # 创建review_request client = RBClient(rbcfg["rbserver"], username=rbcfg["rbadmin"], password=rbcfg["rbadminpw"]) root = client.get_root() request_data = { "repository" : rbcfg["rbrepo"], #"commit_id" : rev, "submit_as" : author, } r = root.get_review_requests().create(**request_data) vl = root.get_diff_validation() basedir = "/" #info("------------------"+diff) vl.validate_diff(rbcfg["rbrepo"], diff, base_dir=basedir) r.get_diffs().upload_diff(diff, base_dir=basedir) draft = r.get_draft() update_data = { "branch" : repo_branch, "summary" : summary, "description" : desc, "target_people" : reviewer, "public" : True, } ret = draft.update(**update_data) info("repo:<%s> rev:<%s> rid:<%s>"%(rbcfg["rbserver"], rev, r.id))
def get_api_root(user, password): client = RBClient('http://localhost/', username=user, password=password) root = client.get_root() return root
def create_using_reviewboard_url(cls, reviewboard_url, **rb_client_kwargs): rb_client = RBClient(reviewboard_url, **rb_client_kwargs) return cls(rb_client)
def update_tools_list(panel, payload): """Update the list of installed tools. This will detect the installed analysis tool plugins and inform Review Board of them. Args: panel (celery.worker.control.Panel): The worker control panel. payload (dict): The payload as assembled by the extension. Returns: bool: Whether the task completed successfully. """ logger.info('Request to refresh installed tools from "%s"', payload['url']) logger.info('Iterating Tools') tools = [] for ep in pkg_resources.iter_entry_points(group='reviewbot.tools'): entry_point = ep.name tool_class = ep.load() tool = tool_class() logger.info('Tool: %s' % entry_point) if tool.check_dependencies(): tools.append({ 'name': tool_class.name, 'entry_point': entry_point, 'version': tool_class.version, 'description': tool_class.description, 'tool_options': json.dumps(tool_class.options), 'timeout': tool_class.timeout, 'working_directory_required': tool_class.working_directory_required, }) else: logger.warning('%s dependency check failed.', ep.name) logger.info('Done iterating Tools') hostname = panel.hostname try: api_client = RBClient( payload['url'], cookie_file=COOKIE_FILE, agent=AGENT, session=payload['session']) api_root = api_client.get_root() except Exception as e: logger.exception('Could not reach RB server: %s', e) return { 'status': 'error', 'error': 'Could not reach Review Board server: %s' % e, } try: api_tools = _get_extension_resource(api_root).get_tools() api_tools.create(hostname=hostname, tools=json.dumps(tools)) except Exception as e: logger.exception('Problem POSTing tools: %s', e) return { 'status': 'error', 'error': 'Problem uploading tools: %s' % e, } return { 'status': 'ok', 'tools': tools, }
def RunTool(server_url='', session='', username='', review_request_id=-1, diff_revision=-1, status_update_id=-1, review_settings={}, tool_options={}, repository_name='', base_commit_id='', *args, **kwargs): """Execute an automated review on a review request. Args: server_url (unicode): The URL of the Review Board server. session (unicode): The encoded session identifier. username (unicode): The name of the user who owns the ``session``. review_request_id (int): The ID of the review request being reviewed (ID for use in the API, which is the "display_id" field). diff_revision (int): The ID of the diff revision being reviewed. status_update_id (int): The ID of the status update for this invocation of the tool. review_settings (dict): Settings for how the review should be created. tool_options (dict): The tool-specific settings. repository_name (unicode): The name of the repository to clone to run the tool, if the tool requires full working directory access. base_commit_id (unicode): The ID of the commit that the patch should be applied to. args (tuple): Any additional positional arguments (perhaps used by a newer version of the Review Bot extension). kwargs (dict): Any additional keyword arguments (perhaps used by a newer version of the Review Bot extension). Returns: bool: Whether the task completed successfully. """ try: routing_key = RunTool.request.delivery_info['routing_key'] route_parts = routing_key.partition('.') tool_name = route_parts[0] log_detail = ('(server=%s, review_request_id=%s, diff_revision=%s)' % (server_url, review_request_id, diff_revision)) logger.info('Running tool "%s" %s', tool_name, log_detail) try: logger.info('Initializing RB API %s', log_detail) api_client = RBClient(server_url, cookie_file=COOKIE_FILE, agent=AGENT, session=session) api_root = api_client.get_root() except Exception as e: logger.error('Could not contact Review Board server: %s %s', e, log_detail) return False logger.info('Loading requested tool "%s" %s', tool_name, log_detail) tools = [ entrypoint.load() for entrypoint in pkg_resources.iter_entry_points( group='reviewbot.tools', name=tool_name) ] if len(tools) == 0: logger.error('Tool "%s" not found %s', tool_name, log_detail) return False elif len(tools) > 1: logger.error('Tool "%s" is ambiguous (found %s) %s', tool_name, ', '.join(tool.name for tool in tools), log_detail) return False else: tool = tools[0] repository = None try: logger.info('Creating status update %s', log_detail) status_update = api_root.get_status_update( review_request_id=review_request_id, status_update_id=status_update_id) except Exception as e: logger.exception('Unable to create status update: %s %s', e, log_detail) return False if tool.working_directory_required: if not base_commit_id: logger.error('Working directory is required but the diffset ' 'has no base_commit_id %s', log_detail) status_update.update( state=ERROR, description='Diff does not include parent commit ' 'information.') return False try: repository = repositories[repository_name] except KeyError: logger.error('Unable to find configured repository "%s" %s', repository_name, log_detail) return False try: logger.info('Initializing review %s', log_detail) review = Review(api_root, review_request_id, diff_revision, review_settings) status_update.update(description='running...') except Exception as e: logger.exception('Failed to initialize review: %s %s', e, log_detail) status_update.update(state=ERROR, description='internal error.') return False try: logger.info('Initializing tool "%s %s" %s', tool.name, tool.version, log_detail) t = tool() except Exception as e: logger.exception('Error initializing tool "%s": %s %s', tool.name, e, log_detail) status_update.update(state=ERROR, description='internal error.') return False try: logger.info('Executing tool "%s" %s', tool.name, log_detail) t.execute(review, settings=tool_options, repository=repository, base_commit_id=base_commit_id) logger.info('Tool "%s" completed successfully %s', tool.name, log_detail) except Exception as e: logger.exception('Error executing tool "%s": %s %s', tool.name, e, log_detail) status_update.update(state=ERROR, description='internal error.') return False if t.output: file_attachments = \ api_root.get_user_file_attachments(username=username) attachment = \ file_attachments.upload_attachment('tool-output', t.output) status_update.update(url=attachment.absolute_url, url_text='Tool console output') try: if not review.has_comments: status_update.update(state=DONE_SUCCESS, description='passed.') else: logger.info('Publishing review %s', log_detail) review_id = review.publish().id status_update.update(state=DONE_FAILURE, description='failed.', review_id=review_id) except Exception as e: logger.exception('Error when publishing review: %s %s', e, log_detail) status_update.update(state=ERROR, description='internal error.') return False logger.info('Review completed successfully %s', log_detail) return True finally: cleanup_tempfiles()
class SVNRepositoryInfoTests(SpyAgency, SCMClientTests): """Unit tests for rbtools.clients.svn.SVNRepositoryInfo.""" payloads = { 'http://localhost:8080/api/': { 'mimetype': 'application/vnd.reviewboard.org.root+json', 'rsp': { 'uri_templates': {}, 'links': { 'self': { 'href': 'http://localhost:8080/api/', 'method': 'GET', }, 'repositories': { 'href': 'http://localhost:8080/api/repositories/', 'method': 'GET', }, }, 'stat': 'ok', }, }, 'http://localhost:8080/api/repositories/?tool=Subversion': { 'mimetype': 'application/vnd.reviewboard.org.repositories+json', 'rsp': { 'repositories': [ { # This one doesn't have a mirror_path, to emulate # Review Board 1.6. 'id': 1, 'name': 'SVN Repo 1', 'path': 'https://svn1.example.com/', 'links': { 'info': { 'href': ('https://localhost:8080/api/' 'repositories/1/info/'), 'method': 'GET', }, }, }, { 'id': 2, 'name': 'SVN Repo 2', 'path': 'https://svn2.example.com/', 'mirror_path': 'svn+ssh://svn2.example.com/', 'links': { 'info': { 'href': ('https://localhost:8080/api/' 'repositories/2/info/'), 'method': 'GET', }, }, }, ], 'links': { 'next': { 'href': ('http://localhost:8080/api/repositories/' '?tool=Subversion&page=2'), 'method': 'GET', }, }, 'total_results': 3, 'stat': 'ok', }, }, 'http://localhost:8080/api/repositories/?tool=Subversion&page=2': { 'mimetype': 'application/vnd.reviewboard.org.repositories+json', 'rsp': { 'repositories': [ { 'id': 3, 'name': 'SVN Repo 3', 'path': 'https://svn3.example.com/', 'mirror_path': 'svn+ssh://svn3.example.com/', 'links': { 'info': { 'href': ('https://localhost:8080/api/' 'repositories/3/info/'), 'method': 'GET', }, }, }, ], 'total_results': 3, 'stat': 'ok', }, }, 'https://localhost:8080/api/repositories/1/info/': { 'mimetype': 'application/vnd.reviewboard.org.repository-info+json', 'rsp': { 'info': { 'uuid': 'UUID-1', 'url': 'https://svn1.example.com/', 'root_url': 'https://svn1.example.com/', }, 'stat': 'ok', }, }, 'https://localhost:8080/api/repositories/2/info/': { 'mimetype': 'application/vnd.reviewboard.org.repository-info+json', 'rsp': { 'info': { 'uuid': 'UUID-2', 'url': 'https://svn2.example.com/', 'root_url': 'https://svn2.example.com/', }, 'stat': 'ok', }, }, 'https://localhost:8080/api/repositories/3/info/': { 'mimetype': 'application/vnd.reviewboard.org.repository-info+json', 'rsp': { 'info': { 'uuid': 'UUID-3', 'url': 'https://svn3.example.com/', 'root_url': 'https://svn3.example.com/', }, 'stat': 'ok', }, }, } def setUp(self): super(SVNRepositoryInfoTests, self).setUp() def _urlopen(url, **kwargs): url = url.get_full_url() try: payload = self.payloads[url] except KeyError: return MockResponse( 404, {}, json.dumps({ 'rsp': { 'stat': 'fail', 'err': { 'code': 100, 'msg': 'Object does not exist', }, }, })) return MockResponse(200, { 'Content-Type': payload['mimetype'], }, json.dumps(payload['rsp'])) self.spy_on(urlopen, call_fake=_urlopen) self.api_client = RBClient('http://localhost:8080/') self.root_resource = self.api_client.get_root() def test_find_server_repository_info_with_path_match(self): """Testing SVNRepositoryInfo.find_server_repository_info with path matching """ info = SVNRepositoryInfo('https://svn1.example.com/', '/', '') repo_info = info.find_server_repository_info(self.root_resource) self.assertEqual(repo_info, info) self.assertEqual(repo_info.repository_id, 1) def test_find_server_repository_info_with_mirror_path_match(self): """Testing SVNRepositoryInfo.find_server_repository_info with mirror_path matching """ info = SVNRepositoryInfo('svn+ssh://svn2.example.com/', '/', '') repo_info = info.find_server_repository_info(self.root_resource) self.assertEqual(repo_info, info) self.assertEqual(repo_info.repository_id, 2) def test_find_server_repository_info_with_uuid_match(self): """Testing SVNRepositoryInfo.find_server_repository_info with UUID matching """ info = SVNRepositoryInfo('svn+ssh://blargle/', '/', 'UUID-3') repo_info = info.find_server_repository_info(self.root_resource) self.assertNotEqual(repo_info, info) self.assertEqual(repo_info.repository_id, 3) def test_relative_paths(self): """Testing SVNRepositoryInfo._get_relative_path""" info = SVNRepositoryInfo('http://svn.example.com/svn/', '/', '') self.assertEqual(info._get_relative_path('/foo', '/bar'), None) self.assertEqual(info._get_relative_path('/', '/trunk/myproject'), None) self.assertEqual(info._get_relative_path('/trunk/myproject', '/'), '/trunk/myproject') self.assertEqual(info._get_relative_path('/trunk/myproject', ''), '/trunk/myproject') self.assertEqual(info._get_relative_path('/trunk/myproject', '/trunk'), '/myproject') self.assertEqual( info._get_relative_path('/trunk/myproject', '/trunk/myproject'), '/')
class SVNRepositoryInfoTests(SpyAgency, SCMClientTests): """Unit tests for rbtools.clients.svn.SVNRepositoryInfo.""" payloads = { 'http://localhost:8080/api/': { 'mimetype': 'application/vnd.reviewboard.org.root+json', 'rsp': { 'uri_templates': {}, 'links': { 'self': { 'href': 'http://localhost:8080/api/', 'method': 'GET', }, 'repositories': { 'href': 'http://localhost:8080/api/repositories/', 'method': 'GET', }, }, 'stat': 'ok', }, }, 'http://localhost:8080/api/repositories/?tool=Subversion': { 'mimetype': 'application/vnd.reviewboard.org.repositories+json', 'rsp': { 'repositories': [ { # This one doesn't have a mirror_path, to emulate # Review Board 1.6. 'id': 1, 'name': 'SVN Repo 1', 'path': 'https://svn1.example.com/', 'links': { 'info': { 'href': ('https://localhost:8080/api/' 'repositories/1/info/'), 'method': 'GET', }, }, }, { 'id': 2, 'name': 'SVN Repo 2', 'path': 'https://svn2.example.com/', 'mirror_path': 'svn+ssh://svn2.example.com/', 'links': { 'info': { 'href': ('https://localhost:8080/api/' 'repositories/2/info/'), 'method': 'GET', }, }, }, ], 'links': { 'next': { 'href': ('http://localhost:8080/api/repositories/' '?tool=Subversion&page=2'), 'method': 'GET', }, }, 'total_results': 3, 'stat': 'ok', }, }, 'http://localhost:8080/api/repositories/?tool=Subversion&page=2': { 'mimetype': 'application/vnd.reviewboard.org.repositories+json', 'rsp': { 'repositories': [ { 'id': 3, 'name': 'SVN Repo 3', 'path': 'https://svn3.example.com/', 'mirror_path': 'svn+ssh://svn3.example.com/', 'links': { 'info': { 'href': ('https://localhost:8080/api/' 'repositories/3/info/'), 'method': 'GET', }, }, }, ], 'total_results': 3, 'stat': 'ok', }, }, 'https://localhost:8080/api/repositories/1/info/': { 'mimetype': 'application/vnd.reviewboard.org.repository-info+json', 'rsp': { 'info': { 'uuid': 'UUID-1', 'url': 'https://svn1.example.com/', 'root_url': 'https://svn1.example.com/', }, 'stat': 'ok', }, }, 'https://localhost:8080/api/repositories/2/info/': { 'mimetype': 'application/vnd.reviewboard.org.repository-info+json', 'rsp': { 'info': { 'uuid': 'UUID-2', 'url': 'https://svn2.example.com/', 'root_url': 'https://svn2.example.com/', }, 'stat': 'ok', }, }, 'https://localhost:8080/api/repositories/3/info/': { 'mimetype': 'application/vnd.reviewboard.org.repository-info+json', 'rsp': { 'info': { 'uuid': 'UUID-3', 'url': 'https://svn3.example.com/', 'root_url': 'https://svn3.example.com/', }, 'stat': 'ok', }, }, } def setUp(self): super(SVNRepositoryInfoTests, self).setUp() def _urlopen(url, **kwargs): url = url.get_full_url() try: payload = self.payloads[url] except KeyError: return MockResponse(404, {}, json.dumps({ 'rsp': { 'stat': 'fail', 'err': { 'code': 100, 'msg': 'Object does not exist', }, }, })) return MockResponse( 200, { 'Content-Type': payload['mimetype'], }, json.dumps(payload['rsp'])) self.spy_on(urlopen, call_fake=_urlopen) self.api_client = RBClient('http://localhost:8080/') self.root_resource = self.api_client.get_root() def test_find_server_repository_info_with_path_match(self): """Testing SVNRepositoryInfo.find_server_repository_info with path matching """ info = SVNRepositoryInfo('https://svn1.example.com/', '/', '') repo_info = info.find_server_repository_info(self.root_resource) self.assertEqual(repo_info, info) self.assertEqual(repo_info.repository_id, 1) def test_find_server_repository_info_with_mirror_path_match(self): """Testing SVNRepositoryInfo.find_server_repository_info with mirror_path matching """ info = SVNRepositoryInfo('svn+ssh://svn2.example.com/', '/', '') repo_info = info.find_server_repository_info(self.root_resource) self.assertEqual(repo_info, info) self.assertEqual(repo_info.repository_id, 2) def test_find_server_repository_info_with_uuid_match(self): """Testing SVNRepositoryInfo.find_server_repository_info with UUID matching """ info = SVNRepositoryInfo('svn+ssh://blargle/', '/', 'UUID-3') repo_info = info.find_server_repository_info(self.root_resource) self.assertNotEqual(repo_info, info) self.assertEqual(repo_info.repository_id, 3) def test_relative_paths(self): """Testing SVNRepositoryInfo._get_relative_path""" info = SVNRepositoryInfo('http://svn.example.com/svn/', '/', '') self.assertEqual(info._get_relative_path('/foo', '/bar'), None) self.assertEqual(info._get_relative_path('/', '/trunk/myproject'), None) self.assertEqual(info._get_relative_path('/trunk/myproject', '/'), '/trunk/myproject') self.assertEqual( info._get_relative_path('/trunk/myproject', ''), '/trunk/myproject') self.assertEqual( info._get_relative_path('/trunk/myproject', '/trunk'), '/myproject') self.assertEqual( info._get_relative_path('/trunk/myproject', '/trunk/myproject'), '/')
class AnyRB(object): def __init__(self, event): self.client = RBClient(settings.RB_API_URL, username=settings.RB_API_USERNAME, password=settings.RB_API_PASSWORD) self.event = event def upload_review(self): if len(self.event.file_set.all()) == 0: return None files_diff = [] empty = True import magic for f in self.event.file_set.all(): mime_type = magic.from_buffer(f.file.read(1024), mime=True) if mime_type[:4] != 'text': continue empty = False file_content = [] for line in f.file: try: file_content.append(line.decode('utf-8')) except: file_content.append(line.decode('cp1251')) from difflib import unified_diff fname = f.filename() from_name = u'a/{0}'.format(fname) to_name = u'b/{0}'.format(fname) diff = [(u'diff --git {0} {1}'.format(from_name, to_name))] from_name = u'/dev/null' diff_content = unified_diff('', file_content, fromfile=from_name, tofile=to_name.encode('utf-8')) for line in diff_content: line = line.strip() if isinstance(line, str): diff.append(line.decode('utf-8')) else: diff.append(line) files_diff.append(u'\n'.join(diff)) files_diff = u'\n'.join(files_diff) if empty: return None review_request = self.get_review_request() if review_request is None: review_request = self.create_review_request() if review_request is None: return review_request review_request.get_diffs().upload_diff(files_diff.encode('utf-8')) draft = review_request.get_or_create_draft() issue = self.event.issue summary = u'[{0}][{1}] {2}'.format(issue.student.get_full_name(), issue.task.course.get_user_group(issue.student), issue.task.title) description_template = u'Задача: "{0}", ' + \ u'курс: [{1}](http://{2}{3})\n' + \ u'Студент: [{4}](http://{2}{5})\n' + \ u'[Обсуждение задачи](http://{2}{6})' description = description_template.format( issue.task.title, issue.task.course, Site.objects.get_current().domain, issue.task.course.get_absolute_url(), issue.student.get_full_name(), issue.student.get_absolute_url(), issue.get_absolute_url() ) draft = draft.update(summary=summary, description=description, description_text_type='markdown', target=settings.RB_API_USERNAME, public=True, ) return review_request.id # def get_or_create_review_request(self): # root = self.client.get_root() # review_request = None # try: # review_id = self.event.issue.get_byname('review_id') # review_request = root.get_review_request(review_request_id=review_id) # except (AttributeError, ValueError): # course_id = self.event.issue.task.course.id # repository_name = str(self.event.issue.id) # os.symlink(settings.RB_SYMLINK_DIR,os.path.join(settings.RB_SYMLINK_DIR, repository_name)) # repository = root.get_repositories().create( # name=repository_name, # path=settings.RB_SYMLINK_DIR+repository_name+'/.git', # tool='Git', # public=False) # root.get_repository(repository_id=repository.id).update(grant_type='add', # grant_entity='user', # grant_name=self.event.author) # root.get_repository(repository_id=repository.id).update(grant_type='add', # grant_entity='group', # grant_name='teachers') # root.get_repository(repository_id=repository.id).update(grant_type='add', # grant_entity='group', # grant_name='teachers_{0}'.format(course_id)) # review_request = root.get_review_requests().create(repository=repository.id) # self.event.issue.set_byname('review_id', review_request.id, self.event.author) # return review_request def get_review_request(self): root = self.client.get_root() review_request = None try: review_id = self.event.issue.get_byname('review_id') except (AttributeError, ValueError): logger.info("Issue '%s' has not review_id.", self.event.issue.id) return None try: review_request = root.get_review_request(review_request_id=review_id) return review_request except Exception as e: logger.info("Issue '%s' has not RB review_request. Exception: '%s'.", self.event.issue.id, e) return None def create_review_request(self): root = self.client.get_root() review_request = None course_id = self.event.issue.task.course.id repository_name = str(self.event.issue.id) repository_path = os.path.join(settings.RB_SYMLINK_DIR, repository_name) os.symlink(settings.RB_SYMLINK_DIR,repository_path) try: repository = root.get_repositories().create( name=repository_name, path=os.path.join(repository_path,'.git'), tool='Git', public=False) root.get_repository(repository_id=repository.id).update(grant_type='add', grant_entity='user', grant_name=self.event.issue.student) root.get_repository(repository_id=repository.id).update(grant_type='add', grant_entity='group', grant_name='teachers_{0}'.format(course_id)) review_request = root.get_review_requests().create(repository=repository.id) self.event.issue.set_byname('review_id', review_request.id, self.event.author) except Exception as e: logger.exception("Exception while creating review_request. Exception: '%s'. Issue: '%s'", e, self.event.issue.id) return None return review_request
#!/usr/bin/python import sys import re import rbtools from rbtools.api.client import RBClient if __name__ == '__main__': review_id = sys.argv[1] root = RBClient('http://rb.hotel.com').get_root() r1 = root.get_review_request(review_request_id=review_id) summary = r1['summary'] bugs_closed_str = ",".join(list(r1['bugs_closed'])) bugs_closed_str = "[%s]"%(bugs_closed_str) #summary=re.sub( bugs_closed_str, '', summary) summary=summary.replace( bugs_closed_str, '') diffs = r1.get_diffs() changed_files = diffs[-1].get_files() changed_files_str = "" for file in changed_files: file_name = file['source_file'] #'/branches/sh-i53-bugs/front_end/modules/bulk/bulk_en.properties' #assumption is the file pattern is like this nth = [ m.start() for m in re.finditer(r"/",file_name)] file_name_pat = file_name[nth[2]+1:] changed_files_str += file_name_pat + " "
def __init__(self, event): self.client = RBClient(settings.RB_API_URL, username=settings.RB_API_USERNAME, password=settings.RB_API_PASSWORD) self.event = event
def main(url, group_names, days_old=7, dry_run=False): """ do something """ try: user = os.environ['RBUSER'] except KeyError: raise SystemExit( "please set RBUSER environment variable for reviewboard user") try: passwd = os.environ['RBPASS'] except KeyError: raise SystemExit( "please set RBPASS environment variable for reviewboard password") #client = RBClient(url, user, passwd) client = RBClient(url) root = client.get_root() if not root: raise SystemExit("Error - could not get RBClient root.") for g_name in group_names: o = get_group_id_by_name(root, g_name, dry_run=dry_run) if not o: raise SystemExit("ERROR: no group '%s' found." % g_name) logger.debug("Found group '%s' id=%d" % (g_name, o)) reviews = get_reviews_for_groups(root, group_names, dry_run=dry_run) old_reviews = filter_reviews_older_than(root, reviews, days_old, dry_run=dry_run) logger.info( "found %d reviews for target groups and last updated %d or more days ago" % (len(old_reviews), days_old)) if len(old_reviews) < 1: logger.info("Found no reviews matching criteria, exiting") return False users = get_submitters_for_reviews(old_reviews) logger.debug("got user information for %d users" % len(users)) recipients = [] for u in users: recipients.append("{u.fullname} <{u.email}>".format(u=users[u])) table = generate_report_html_table(old_reviews, url) body = "<h1>ReviewBoard reminder</h1>\n" body += """<p>You're receiving this message because you have one or more pending code reviews on <a href="{url}">{url}</a> targeted at the '{group_names}' group(s) that have not been updated in over {days_old} days and have not been submitted. At your convenience, please evaluate these reviews and close/submit any that have been merged or discarded. Thank You.</p>\n""".format(url=url, days_old=days_old, group_names=", ".join(group_names)) body += table body += "\n<br />\n" host = node() user = getuser() body += """ <p><em>generated by <a href=\"https://github.com/jantman/misc-scripts/blob/master/reviewboard_reminder_mail.py">reviewboard_reminder_mail.py</a> running on {host} as {user} at {ds}</em></p> """.format(host=host, user=user, ds=datetime.datetime.now().isoformat()) if dry_run: print( "Message to send:\n##############################\n{msg}\n#################################\n" .format(msg=body)) print("Would send to:\n {to}".format(to=", ".join(recipients))) else: raise SystemExit( "Oops - never actually implemented the mail sending...") return True
def __init__(self, config_path=None, reviewboard_url=None, reviewboard_user=None, reviewboard_password=None, pulse_host=None, pulse_port=None, pulse_userid=None, pulse_password=None, exchange=None, queue=None, routing_key=None, pulse_timeout=None, pulse_ssl=False, repo_root=None, logger=None): if logger is None: self.logger = logging.getLogger('mozreviewbot') else: self.logger = logger # We use options passed into __init__ preferentially. If any of these # are not specified, we next check the configuration file, if any. # Finally, we use environment variables. if config_path and not os.path.isfile(config_path): # ConfigParser doesn't seem to throw if it is unable to find the # config file so we'll explicitly check that it exists. self.logger.error('could not locate config file: %s' % ( config_path)) config_path = None if config_path: try: config = ConfigParser() config.read(config_path) reviewboard_url = (reviewboard_url or config.get('reviewboard', 'url')) reviewboard_user = (reviewboard_user or config.get('reviewboard', 'user')) reviewboard_password = (reviewboard_password or config.get('reviewboard', 'password')) pulse_host = pulse_host or config.get('pulse', 'host') pulse_port = pulse_port or config.get('pulse', 'port') pulse_userid = pulse_userid or config.get('pulse', 'userid') pulse_password = pulse_password or config.get('pulse', 'password') exchange = exchange or config.get('pulse', 'exchange') queue = queue or config.get('pulse', 'queue') routing_key = routing_key or config.get('pulse', 'routing_key') pulse_timeout = pulse_timeout or config.get('pulse', 'timeout') if pulse_ssl is None: pulse_ssl = config.get('pulse', 'ssl') except NoSectionError as e: self.logger.error('configuration file missing section: %s' % e.section) try: repo_root = repo_root or config.get('hg', 'repo_root') except (NoOptionError, NoSectionError): # Subclasses do not need to define repo root if they do not # plan on using the hg functionality. pass # keep config around in case any subclasses would like to extract # options from it. self.config = config else: self.config = None reviewboard_url = reviewboard_url or os.environ.get('REVIEWBOARD_URL') pulse_host = pulse_host or os.environ.get('PULSE_HOST') pulse_port = pulse_port or os.environ.get('PULSE_PORT') self.rbclient = RBClient(reviewboard_url, username=reviewboard_user, password=reviewboard_password) self.api_root = self.rbclient.get_root() self.conn = Connection(hostname=pulse_host, port=pulse_port, userid=pulse_userid, password=pulse_password, ssl=pulse_ssl) self.exchange = Exchange(exchange, type='topic', durable=True) self.queue = Queue(name=queue, exchange=self.exchange, durable=True, routing_key=routing_key, exclusive=False, auto_delete=False) self.pulse_timeout = float(pulse_timeout) self.repo_root = repo_root self.hg = None for DIR in os.environ['PATH'].split(os.pathsep): p = os.path.join(DIR, 'hg') if os.path.exists(p): self.hg = p