def GET(self, path): page = web.ctx.site.get(path) if not page: raise web.seeother(path) i = web.input(page=0) offset = 20 * safeint(i.page) limit = 20 history = get_changes(dict(key=path, limit=limit, offset=offset)) return render.history(page, history)
def main(argv=None): if argv is None: argv = sys.argv optparser = optparse.OptionParser() optparser.add_option( '-p', '--project', default='projects/nova.json', help='JSON file describing the project to generate stats for') optparser.add_option( '-a', '--all', action='store_true', help='Generate stats across all known projects (*.json)') optparser.add_option( '-u', '--user', default=getpass.getuser(), help='gerrit user') optparser.add_option( '-k', '--key', default=None, help='ssh key for gerrit') optparser.add_option('-s', '--stable', action='store_true', help='Include stable branch commits') options, args = optparser.parse_args() projects = utils.get_projects_info(options.project, options.all) if not projects: print "Please specify a project." sys.exit(1) changes = utils.get_changes(projects, options.user, options.key, only_open=True) approved_and_rebased = set() for change in changes: if 'rowCount' in change: continue if not options.stable and 'stable' in change['branch']: continue if change['status'] != 'NEW': # Filter out WORKINPROGRESS continue for patch_set in change['patchSets'][:-1]: if (utils.patch_set_approved(patch_set) and not utils.patch_set_approved(change['patchSets'][-1])): if has_negative_feedback(change['patchSets'][-1]): continue approved_and_rebased.add("%s %s" % (change['url'], change['subject'])) for x in approved_and_rebased: print x print "total %d" % len(approved_and_rebased)
def proposals(): proposals_ = get_proposals() for p in proposals_: c = get_changes(p['id']) adds = 0 rems = 0; subs = [] for x in c: if x['type'] == 'addition': adds += 1 else: rems += 1 s = {'subject': x['subject'], 'ontology': get_prefix_from_uri(x['context'])} if s not in subs: subs.append(s) p['additions'] = adds p['removals'] = rems p['subjects'] = subs return render_template('proposals.html', proposals=proposals_)
def main(argv=None): if argv is None: argv = sys.argv optparser = optparse.OptionParser() optparser.add_option('-p', '--project', default='projects/nova.json', help='JSON file describing the project to generate stats for') optparser.add_option('-a', '--all', action='store_true', help='Generate stats across all known projects (*.json)') optparser.add_option('-u', '--user', default='russellb', help='gerrit user') optparser.add_option('-k', '--key', default=None, help='ssh key for gerrit') optparser.add_option('-s', '--stable', action='store_true', help='Include stable branch commits') optparser.add_option('-l', '--longest-waiting', type='int', default=5, help='Show n changesets that have waited the longest)') optparser.add_option('-m', '--waiting-more', type='int', default=7, help='Show number of changesets that have waited more than n days)') optparser.add_option('-H', '--html', action='store_true', help='Use HTML output instead of plain text') options, args = optparser.parse_args() projects = utils.get_projects_info(options.project, options.all) if not projects: print "Please specify a project." sys.exit(1) changes = utils.get_changes(projects, options.user, options.key, only_open=True) waiting_on_submitter = [] waiting_on_reviewer = [] now = datetime.datetime.utcnow() now_ts = calendar.timegm(now.timetuple()) for change in changes: if 'rowCount' in change: continue if not options.stable and 'stable' in change['branch']: continue if change['status'] != 'NEW': # Filter out WORKINPROGRESS continue latest_patch = change['patchSets'][-1] waiting_for_review = True approvals = latest_patch.get('approvals', []) approvals.sort(key=lambda a:a['grantedOn']) for review in approvals: if review['type'] not in ('CRVW', 'VRIF'): continue if review['value'] in ('-1', '-2'): waiting_for_review = False break change['age'] = get_age_of_patch(latest_patch, now_ts) change['age2'] = get_age_of_patch(change['patchSets'][0], now_ts) patch = find_oldest_no_nack(change) change['age3'] = get_age_of_patch(patch, now_ts) if patch else 0 if waiting_for_review: waiting_on_reviewer.append(change) else: waiting_on_submitter.append(change) age_sorted = sorted(waiting_on_reviewer, key=lambda change: change['age']) age2_sorted = sorted(waiting_on_reviewer, key=lambda change: change['age2']) age3_sorted = sorted(waiting_on_reviewer, key=lambda change: change['age3']) stats = gen_stats(projects, waiting_on_reviewer, waiting_on_submitter, age_sorted, age2_sorted, age3_sorted, options) if options.html: print_stats_html(stats) else: print_stats_txt(stats)
def main(argv=None): if argv is None: argv = sys.argv optparser = optparse.OptionParser() optparser.add_option('-p', '--project', default='projects/nova.json', help='JSON file describing the project to generate stats for') optparser.add_option('-a', '--all', action='store_true', help='Generate stats across all known projects (*.json)') optparser.add_option('-d', '--days', type='int', default=14, help='Number of days to consider') optparser.add_option('-u', '--user', default=getpass.getuser(), help='gerrit user') optparser.add_option('-k', '--key', default=None, help='ssh key for gerrit') options, args = optparser.parse_args() projects = utils.get_projects_info(options.project, options.all) if not projects: print "Please specify a project." sys.exit(1) reviewers = {} cut_off = datetime.datetime.now() - datetime.timedelta(days=options.days) ts = calendar.timegm(cut_off.timetuple()) for project in projects: changes = utils.get_changes([project], options.user, options.key) for change in changes: for patchset in change.get('patchSets', []): process_patchset(project, patchset, reviewers, ts) reviewers = [(v, k) for k, v in reviewers.iteritems() if k.lower() not in ('jenkins', 'smokestack')] reviewers.sort(reverse=True, key=lambda r:r[0]['total']) if options.all: print 'Reviews for the last %d days in projects: %s' % (options.days, [project['name'] for project in projects]) else: print 'Reviews for the last %d days in %s' % (options.days, projects[0]['name']) if options.all: print '** -- Member of at least one core reviewer team' else: print '** -- %s-core team member' % projects[0]['name'] table = prettytable.PrettyTable( ('Reviewer', 'Reviews -2 -1 +1 +2 +/- %', 'Disagreements*')) total = 0 for k, v in reviewers: in_core_team = False for project in projects: if v in project['core-team']: in_core_team = True break name = '%s%s' % (v, ' **' if in_core_team else '') plus = float(k['votes']['2'] + k['votes']['1']) minus = float(k['votes']['-2'] + k['votes']['-1']) ratio = (plus / (plus + minus)) * 100 r = '%7d %3d %3d %3d %3d %5.1f%%' % (k['total'], k['votes']['-2'], k['votes']['-1'], k['votes']['1'], k['votes']['2'], ratio) dratio = ((float(k['disagreements']) / plus) * 100) if plus else 0.0 d = '%3d (%5.1f%%)' % (k['disagreements'], dratio) table.add_row((name, r, d)) total += k['total'] print table print '\nTotal reviews: %d' % total print 'Total reviewers: %d' % len(reviewers) print '\n(*) Disagreements are defined as a +1 or +2 vote on a patch ' \ 'where a core team member later gave a -1 or -2 vote, or a ' \ 'negative vote overridden with a postive one afterwards.' return 0
def main(): parser = ArgumentParser( description="Get reviews for open bugs against a milestone") parser.add_argument( '-p', '--project', default='projects/nova.json', help='JSON file describing the project to generate stats for') parser.add_argument( '-m', '--milestone', default='', help='Only show bugs targeted to a specified milestone') parser.add_argument( '-u', '--user', default=getpass.getuser(), help='gerrit user') parser.add_argument('-k', '--key', default=None, help='ssh key for gerrit') args = parser.parse_args() projects = utils.get_projects_info(args.project, False) project_name = projects[0]['name'] if not projects: print "Please specify a project." return 1 launchpad = Launchpad.login_with('openstack-releasing', 'production') proj = launchpad.projects[project_name] statuses = ['New', 'Incomplete', 'Confirmed', 'Triaged', 'In Progress'] if args.milestone: milestone = proj.getMilestone(name=args.milestone) bugtasks = proj.searchTasks(status=statuses, milestone=milestone) else: bugtasks = proj.searchTasks(status=statuses) bugs_by_id = {} for bt in bugtasks: bugs_by_id[str(bt.bug.id)] = bt milestones = {} changes = utils.get_changes(projects, args.user, args.key, only_open=True) bug_regex = re.compile('bug/(\d+)') for change in changes: if 'topic' not in change: continue match = bug_regex.match(change['topic']) if not match: continue bugid = match.group(1) try: bugtask = bugs_by_id[bugid] milestone = str(bugtask.milestone).split('/')[-1] if milestone == 'None': milestone = 'Untargeted' except KeyError: milestone = 'Bug does not exist for this project' milestones.setdefault(milestone, []) milestones[milestone].append((change['url'], bugid)) print 'Reviews for bugs grouped by milestone for project: %s\n' % ( project_name) for milestone, reviews in milestones.items(): if args.milestone and milestone != args.milestone: continue print 'Milestone: %s' % milestone for review, bugid in reviews: print '--> %s -- https://bugs.launchpad.net/%s/+bug/%s' \ % (review, project_name, bugid) print
def main(argv=None): if argv is None: argv = sys.argv optparser = optparse.OptionParser() optparser.add_option( '-p', '--project', default='projects/nova.json', help='JSON file describing the project to generate stats for') optparser.add_option( '-a', '--all', action='store_true', help='Generate stats across all known projects (*.json)') optparser.add_option( '-u', '--user', default=getpass.getuser(), help='gerrit user') optparser.add_option( '-k', '--key', default=None, help='ssh key for gerrit') optparser.add_option( '-s', '--stable', action='store_true', help='Include stable branch commits') optparser.add_option( '-l', '--longest-waiting', type='int', default=5, help='Show n changesets that have waited the longest)') optparser.add_option( '-m', '--waiting-more', type='int', default=7, help='Show number of changesets that have waited more than n days)') optparser.add_option( '-H', '--html', action='store_true', help='Use HTML output instead of plain text') optparser.add_option( '--server', default='review.openstack.org', help='Gerrit server to connect to') optparser.add_option( '--debug', action='store_true', help='Show extra debug output') optparser.add_option( '--projects-dir', default='./projects', help='Directory where to locate the project files') options, args = optparser.parse_args() logging.basicConfig(level=logging.ERROR) if options.debug: logging.root.setLevel(logging.DEBUG) projects = utils.get_projects_info(options.project, options.all, base_dir=options.projects_dir) if not projects: print "Please specify a project." sys.exit(1) changes = utils.get_changes(projects, options.user, options.key, only_open=True, server=options.server) waiting_on_submitter = [] waiting_on_reviewer = [] now = datetime.datetime.utcnow() now_ts = calendar.timegm(now.timetuple()) for change in changes: if 'rowCount' in change: continue if not options.stable and 'stable' in change['branch']: continue if change['status'] != 'NEW': # Filter out WORKINPROGRESS continue latest_patch = change['patchSets'][-1] if utils.patch_set_approved(latest_patch): # Ignore patches already approved and just waiting to merge continue waiting_for_review = True approvals = latest_patch.get('approvals', []) approvals.sort(key=lambda a: a['grantedOn']) for review in approvals: if review['type'] not in ('CRVW', 'VRIF', 'Code-Review', 'Verified'): continue if review['value'] in ('-1', '-2'): waiting_for_review = False break change['age'] = get_age_of_patch(latest_patch, now_ts) change['age2'] = get_age_of_patch(change['patchSets'][0], now_ts) patch = find_oldest_no_nack(change) change['age3'] = get_age_of_patch(patch, now_ts) if patch else 0 if waiting_for_review: waiting_on_reviewer.append(change) else: waiting_on_submitter.append(change) stats = gen_stats(projects, waiting_on_reviewer, waiting_on_submitter, options) if options.html: print_stats_html(stats) else: print_stats_txt(stats)
for mod in __all__: models[mod] = importlib.import_module('models.' + mod).model print('Imported Models: {}'.format(__all__)) ################################# # MONITOR FOR NEW CHANGES TO PACS # monitors every 1 second ################################# # get last change Seq # to set start point of loop n_last = get_changes('last')['Last'] while True: changes = get_changes('since={}'.format(n_last)) for item in changes['Changes']: if item['ChangeType'] == 'NewStudy': study = get_study(item['ID']) print('-'*50) print('PROCESSING STUDY: ' + item['ID']) print('Accession: ' + study.get('MainDicomTags').get('AccessionNumber')) print('-'*50) # wait until study "is stable" (i.e. has not recieved new instances in a while) ##Waits 1 min (can be configured) to see if a new instance will upload while study['IsStable'] == False:
'--days', type='int', default=14, help='Number of days to consider') optparser.add_option('-u', '--user', default='russellb', help='gerrit user') optparser.add_option('-k', '--key', default=None, help='ssh key for gerrit') options, args = optparser.parse_args() projects = utils.get_projects_info(options.project, options.all) if not projects: print "Please specify a project." sys.exit(1) all_changes = utils.get_changes(projects, options.user, options.key) reviews = [] for change in all_changes: # print json.dumps(change, sort_keys=True, indent=4) for patchset in change.get('patchSets', []): for review in patchset.get('approvals', []): reviews += [review] cut_off = datetime.datetime.now() - datetime.timedelta(days=options.days) ts = calendar.timegm(cut_off.timetuple()) reviews = filter(lambda x: x['grantedOn'] > ts, reviews) def round_to_day(ts):
def post_receive(sBZUrl, sBZUser=None, sBZPasswd=None, sFormatSpec=None, oBugRegex=None, sSeparator=None, logger=None, bz_init=None, sRefPrefix=None, bIncludeDiffStat=True, aasPushes=None): """ a post-recieve hook handler which extracts bug ids and adds the commit info to the comment. If multiple bug ids are found, the comment is added to each of those bugs. sBZUrl is the base URL for the Bugzilla installation. If sBZUser and sBZPasswd are None, then it uses the ~/.bugz_cookie cookiejar. oBugRegex specifies the regex used to search for the bug id in the commit messages. It MUST provide a named group called 'bug' which contains the bug id (all digits only). If oBugRegex is None, a default bug regex is used, which is: r"bug\s*(?:#|)\s*(?P<bug>\d+)" This matches forms such as: - bug 123 - bug #123 - BUG # 123 - Bug#123 - bug123 The format spec is appended to "--pretty=format:" and passed to "git whatchanged". See the git whatchanged manpage for more info on the format spec. Newlines are automatically converted to the "--pretty" equivalent, which is '%n'. If sFormatSpec is None, a default format spec is used. The separator is a string that would never occur in a commit message. If sSeparator is None, a default separator is used, which should be good enough for everyone. If a logger is provided, it would be used for all the logging. If logger is None, logging will be disabled. The logger must be a Python logging.Logger instance. The function bz_init(url, username, password) is invoked to instantiate the bugz.bugzilla.Bugz instance. If this is None, the default method is used. sRefPrefix is the string prefix of the git reference. If a git reference does not start with this, its commits will be ignored. 'refs/heads/' by default. aasPushes is a list of (sOldRev, sNewRev, sRefName) tuples, for when these aren't read from stdin (gerrit integration). """ if sFormatSpec is None: sFormatSpec = sDefaultFormatSpec if sSeparator is None: sSeparator = sDefaultSeparator if oBugRegex is None: oBugRegex = oDefaultBugRegex if logger is None: logger = NullLogger if bz_init is None: bz_init = init_bugzilla if sRefPrefix is None: sRefPrefix = sDefaultRefPrefix oBZ = bz_init(sBZUrl, sBZUser, sBZPasswd) def gPushes(): for sLine in iter(sys.stdin.readline, ""): yield sLine.strip().split(" ") if not aasPushes: aasPushes = gPushes() sPrevRev = None for asPush in aasPushes: (sOldRev, sNewRev, sRefName) = asPush if not sRefName.startswith(sRefPrefix): logger.debug("ignoring ref: '%s'" % (sRefName, )) continue if sPrevRev is None: sPrevRev = sOldRev logger.debug("oldrev: '%s', newrev: '%s'" % (sOldRev, sNewRev)) asChangeLogs = get_changes(sOldRev, sNewRev, sFormatSpec, sSeparator, bIncludeDiffStat, sRefName, sRefPrefix) for sMessage in asChangeLogs: logger.debug("Considering commit:\n%s" % (sMessage, )) oMatch = re.search(oBugRegex, sMessage) if oMatch is None: logger.info("Bug id not found in commit:\n%s" % (sMessage, )) continue for oMatch in re.finditer(oBugRegex, sMessage): iBugId = int(oMatch.group("bug")) logger.debug("Found bugid %d" % (iBugId, )) try: oBZ.modify(iBugId, comment=sMessage) except Exception, e: logger.exception("Could not add comment to bug %d" % (iBugId, ))
def update(oBugRegex=None, asAllowedStatuses=None, sSeparator=None, sBZUrl=None, sBZUser=None, sBZPasswd=None, logger=None, bz_init=None, sRefPrefix=None, bRequireBugNumber=True): """ an update hook handler which rejects commits without a bug reference. This looks at the sys.argv array, so make sure you don't modify it before calling this function. oBugRegex specifies the regex used to search for the bug id in the commit messages. It MUST provide a named group called 'bug' which contains the bug id (all digits only). If oBugRegex is None, a default bug regex is used, which is: r"bug\s*(?:#|)\s*(?P<bug>\d+)" This matches forms such as: - bug 123 - bug #123 - BUG # 123 - Bug#123 - bug123 asAllowedStatuses is an array containing allowed statuses for the found bugs. If a bug is not in one of these states, the commit will be rejected. If asAllowedStatuses is None, status checking is diabled. The separator is a string that would never occur in a commit message. If sSeparator is None, a default separator is used, which should be good enough for everyone. sBZUrl specifies the base URL for the Bugzilla installation. sBZUser and sBZPasswd are the bugzilla credentials. If a logger is provided, it would be used for all the logging. If logger is None, logging will be disabled. The logger must be a Python logging.Logger instance. The function bz_init(url, username, password) is invoked to instantiate the bugz.bugzilla.Bugz instance. If this is None, the default method is used. sRefPrefix is the string prefix of the git reference. If a git reference does not start with this, its commits will be ignored. 'refs/heads/' by default. bRequireBugNumber, if True, requires that a bug number appears in the commit message (otherwise it will be rejected). """ if oBugRegex is None: oBugRegex = oDefaultBugRegex if sSeparator is None: sSeparator = sDefaultSeparator if logger is None: logger = NullLogger if bz_init is None: bz_init = init_bugzilla if sRefPrefix is None: sRefPrefix = sDefaultRefPrefix sFormatSpec = sDefaultFormatSpec if asAllowedStatuses is not None: # sanity checking if sBZUrl is None: raise ValueError("Bugzilla info required for status checks") # create and cache bugzilla instance oBZ = bz_init(sBZUrl, sBZUser, sBZPasswd) # check auth try: oBZ.auth() except: logger.error("Could not login to Bugzilla", exc_info=1) notify_and_exit( "Could not login to Bugzilla. Check your auth details and settings" ) (sRefName, sOldRev, sNewRev) = sys.argv[1:4] if not sRefName.startswith(sRefPrefix): logger.debug("ignoring ref: '%s'" % (sRefName, )) return logger.debug("oldrev: '%s', newrev: '%s'" % (sOldRev, sNewRev)) asChangeLogs = get_changes(sOldRev, sNewRev, sFormatSpec, sSeparator, False, sRefName, sRefPrefix) for sMessage in asChangeLogs: logger.debug("Checking for bug refs in commit:\n%s" % (sMessage, )) oMatch = re.search(oBugRegex, sMessage) if oMatch is None: if bRequireBugNumber: logger.error("No bug ref found in commit:\n%s" % (sMessage, )) notify_and_exit("No bug ref found in commit:\n%s" % (sMessage, )) else: logger.debug("No bug ref found, but none required.") else: if asAllowedStatuses is not None: # check all bug statuses for oMatch in re.finditer(oBugRegex, sMessage): iBugId = int(oMatch.group("bug")) logger.debug("Found bug id %d" % (iBugId, )) try: sStatus = get_bug_status(oBZ, iBugId) if sStatus is None: notify_and_exit("Bug %d does not exist" % (iBugId, )) except Exception, e: logger.exception("Could not get status for bug %d" % (iBugId, )) notify_and_exit("Could not get staus for bug %d" % (iBugId, )) logger.debug("status for bug %d is %s" % (iBugId, sStatus)) if sStatus not in asAllowedStatuses: logger.info( "Cannot accept commit for bug %d in state %s" % (iBugId, sStatus)) notify_and_exit("Bug %d['%s'] is not in %s" % (iBugId, sStatus, asAllowedStatuses))
optparser.add_option('-n', '--no-stable', action='store_true', help='Exclude changesets for stable branch)') optparser.add_option('-l', '--longest-waiting', type='int', default=5, help='Show n changesets that have waited the longest)') optparser.add_option('-m', '--waiting-more', type='int', default=7, help='Show number of changesets that have waited more than n days)') options, args = optparser.parse_args() projects = utils.get_projects_info(options.project, options.all) if not projects: print "Please specify a project." sys.exit(1) changes = utils.get_changes(projects, options.user, options.key, only_open=True) waiting_on_submitter = [] waiting_on_reviewer = [] now = datetime.datetime.utcnow() now_ts = calendar.timegm(now.timetuple()) def sec_to_period_string(seconds): days = seconds / (3600 * 24) hours = (seconds / 3600) - (days * 24) minutes = (seconds / 60) - (days * 24 * 60) - (hours * 60) return '%d days, %d hours, %d minutes' % (days, hours, minutes)
def main(argv=None): if argv is None: argv = sys.argv optparser = optparse.OptionParser() optparser.add_option( '-p', '--project', default='projects/nova.json', help='JSON file describing the project to generate stats for') optparser.add_option( '-a', '--all', action='store_true', help='Generate stats across all known projects (*.json)') optparser.add_option( '-o', '--output', default='-', help='Where to write output. If - stdout is used and only one output' 'format may be given. Otherwise the output format is appended to' 'the output parameter to generate file names.') optparser.add_option( '--outputs', default=['txt'], action='append', help='Select what outputs to generate. (txt,csv).') optparser.add_option( '-d', '--days', type='int', default=14, help='Number of days to consider') optparser.add_option( '-u', '--user', default=getpass.getuser(), help='gerrit user') optparser.add_option( '-k', '--key', default=None, help='ssh key for gerrit') options, args = optparser.parse_args() projects = utils.get_projects_info(options.project, options.all) if not projects: print "Please specify a project." sys.exit(1) reviewers = {} cut_off = datetime.datetime.now() - datetime.timedelta(days=options.days) ts = calendar.timegm(cut_off.timetuple()) for project in projects: changes = utils.get_changes([project], options.user, options.key) for change in changes: for patchset in change.get('patchSets', []): process_patchset(project, patchset, reviewers, ts) reviewers = [(v, k) for k, v in reviewers.iteritems() if k.lower() not in ('jenkins', 'smokestack')] reviewers.sort(reverse=True, key=lambda r: r[0]['total']) # Do logical processing of reviewers. reviewer_data = [] total = 0 for k, v in reviewers: in_core_team = False for project in projects: if v in project['core-team']: in_core_team = True break name = '%s%s' % (v, ' **' if in_core_team else '') plus = float(k['votes']['2'] + k['votes']['1']) minus = float(k['votes']['-2'] + k['votes']['-1']) ratio = ((plus / (plus + minus)) * 100) if plus + minus > 0 else 0 r = (k['total'], k['votes']['-2'], k['votes']['-1'], k['votes']['1'], k['votes']['2'], k['votes']['A'], "%5.1f%%" % ratio) dratio = ((float(k['disagreements']) / plus) * 100) if plus else 0.0 d = (k['disagreements'], "%5.1f%%" % dratio) reviewer_data.append((name, r, d)) total += k['total'] # And output. writers = { 'csv': write_csv, 'txt': write_pretty, } if options.output == '-': if len(options.outputs) != 1: raise Exception("Can only output one format to stdout.") for output in options.outputs: if options.output == '-': file_obj = sys.stdout on_done = None else: file_obj = open(options.output + '.' + output, 'wt') on_done = file_obj.close try: writer = writers[output] if options.all: file_obj.write( 'Reviews for the last %d days in projects: %s\n' % (options.days, [project['name'] for project in projects])) else: file_obj.write('Reviews for the last %d days in %s\n' % (options.days, projects[0]['name'])) if options.all: file_obj.write( '** -- Member of at least one core reviewer team\n') else: file_obj.write( '** -- %s-core team member\n' % projects[0]['name']) writer(reviewer_data, file_obj) file_obj.write('\nTotal reviews: %d\n' % total) file_obj.write('Total reviewers: %d\n' % len(reviewers)) file_obj.write( '\n(*) Disagreements are defined as a +1 or +2 vote on a ' \ 'patch where a core team member later gave a -1 or -2 vote' \ ', or a negative vote overridden with a positive one ' \ 'afterwards.\n') finally: if on_done: on_done() return 0
def update( oBugRegex=None, bRequireBugNumber=True, asAllowedStatuses=None, sSeparator=None, sBZUrl=None, sBZUser=None, sBZPasswd=None, logger=None, bz_init=None, sRefPrefix=None, ): """ an update hook handler which rejects commits without a bug reference. This looks at the sys.argv array, so make sure you don't modify it before calling this function. oBugRegex specifies the regex used to search for the bug id in the commit messages. It MUST provide a named group called 'bug' which contains the bug id (all digits only). If oBugRegex is None, a default bug regex is used, which is: r"bug\s*(?:#|)\s*(?P<bug>\d+)" This matches forms such as: - bug 123 - bug #123 - BUG # 123 - Bug#123 - bug123 bRequireBugNumber, if True, requires that a bug number appears in the commit message (otherwise it will be rejected). asAllowedStatuses is an array containing allowed statuses for the found bugs. If a bug is not in one of these states, the commit will be rejected. If asAllowedStatuses is None, status checking is diabled. The separator is a string that would never occur in a commit message. If sSeparator is None, a default separator is used, which should be good enough for everyone. sBZUrl specifies the base URL for the Bugzilla installation. sBZUser and sBZPasswd are the bugzilla credentials. If a logger is provided, it would be used for all the logging. If logger is None, logging will be disabled. The logger must be a Python logging.Logger instance. The function bz_init(url, username, password) is invoked to instantiate the bugz.bugzilla.Bugz instance. If this is None, the default method is used. sRefPrefix is the string prefix of the git reference. If a git reference does not start with this, its commits will be ignored. 'refs/heads/' by default. """ if oBugRegex is None: oBugRegex = oDefaultBugRegex if sSeparator is None: sSeparator = sDefaultSeparator if logger is None: logger = NullLogger if bz_init is None: bz_init = init_bugzilla if sRefPrefix is None: sRefPrefix = sDefaultRefPrefix sFormatSpec = sDefaultFormatSpec if asAllowedStatuses is not None: # sanity checking if sBZUrl is None: raise ValueError("Bugzilla info required for status checks") # create and cache bugzilla instance oBZ = bz_init(sBZUrl, sBZUser, sBZPasswd) # check auth try: oBZ.auth() except: logger.error("Could not login to Bugzilla", exc_info=1) notify_and_exit("Could not login to Bugzilla. Check your auth details and settings") (sRefName, sOldRev, sNewRev) = sys.argv[1:4] if not sRefName.startswith(sRefPrefix): logger.debug("ignoring ref: '%s'" % (sRefName,)) return logger.debug("oldrev: '%s', newrev: '%s'" % (sOldRev, sNewRev)) asChangeLogs = get_changes(sOldRev, sNewRev, sFormatSpec, sSeparator) for sMessage in asChangeLogs: logger.debug("Checking for bug refs in commit:\n%s" % (sMessage,)) oMatch = re.search(oBugRegex, sMessage) if oMatch is None: if bRequireBugNumber: logger.error("No bug ref found in commit:\n%s" % (sMessage,)) notify_and_exit("No bug ref found in commit:\n%s" % (sMessage,)) else: logger.debug("No bug ref found, but none required.") else: if asAllowedStatuses is not None: # check all bug statuses for oMatch in re.finditer(oBugRegex, sMessage): iBugId = int(oMatch.group("bug")) logger.debug("Found bug id %d" % (iBugId,)) try: sStatus = get_bug_status(oBZ, iBugId) if sStatus is None: notify_and_exit("Bug %d does not exist" % (iBugId,)) except Exception, e: logger.exception("Could not get status for bug %d" % (iBugId,)) notify_and_exit("Could not get staus for bug %d" % (iBugId,)) logger.debug("status for bug %d is %s" % (iBugId, sStatus)) if sStatus not in asAllowedStatuses: logger.info("Cannot accept commit for bug %d in state %s" % (iBugId, sStatus)) notify_and_exit("Bug %d['%s'] is not in %s" % (iBugId, sStatus, asAllowedStatuses))
optparser.add_option('-p', '--project', default='nova.json', help='JSON file describing the project to generate stats for') optparser.add_option('-a', '--all', action='store_true', help='Generate stats across all known projects (*.json)') optparser.add_option('-u', '--user', default='russellb', help='gerrit user') optparser.add_option('-k', '--key', default=None, help='ssh key for gerrit') options, args = optparser.parse_args() projects = utils.get_projects_info(options.project, options.all) if not projects: print "Please specify a project." sys.exit(1) changes = utils.get_changes(projects, options.user, options.key, only_open=True) waiting_on_submitter = [] waiting_on_reviewer = [] now = datetime.datetime.utcnow() now_ts = calendar.timegm(now.timetuple()) for change in changes: if 'rowCount' in change: continue latest_patch = change['patchSets'][-1] waiting_for_review = True for review in latest_patch.get('approvals', []): if review['type'] == 'CRVW' and (review['value'] != '-1' or review['value'] == '-2'):
def post_receive( sBZUrl, sBZUser=None, sBZPasswd=None, sFormatSpec=None, oBugRegex=None, sSeparator=None, logger=None, bz_init=None, sRefPrefix=None, ): """ a post-recieve hook handler which extracts bug ids and adds the commit info to the comment. If multiple bug ids are found, the comment is added to each of those bugs. sBZUrl is the base URL for the Bugzilla installation. If sBZUser and sBZPasswd are None, then it uses the ~/.bugz_cookie cookiejar. oBugRegex specifies the regex used to search for the bug id in the commit messages. It MUST provide a named group called 'bug' which contains the bug id (all digits only). If oBugRegex is None, a default bug regex is used, which is: r"bug\s*(?:#|)\s*(?P<bug>\d+)" This matches forms such as: - bug 123 - bug #123 - BUG # 123 - Bug#123 - bug123 The format spec is appended to "--pretty=format:" and passed to "git whatchanged". See the git whatchanged manpage for more info on the format spec. If sFormatSpec is None, a default format spec is used. The separator is a string that would never occur in a commit message. If sSeparator is None, a default separator is used, which should be good enough for everyone. If a logger is provided, it would be used for all the logging. If logger is None, logging will be disabled. The logger must be a Python logging.Logger instance. The function bz_init(url, username, password) is invoked to instantiate the bugz.bugzilla.Bugz instance. If this is None, the default method is used. sRefPrefix is the string prefix of the git reference. If a git reference does not start with this, its commits will be ignored. 'refs/heads/' by default. """ if sFormatSpec is None: sFormatSpec = sDefaultFormatSpec if sSeparator is None: sSeparator = sDefaultSeparator if oBugRegex is None: oBugRegex = oDefaultBugRegex if logger is None: logger = NullLogger if bz_init is None: bz_init = init_bugzilla if sRefPrefix is None: sRefPrefix = sDefaultRefPrefix oBZ = bz_init(sBZUrl, sBZUser, sBZPasswd) sPrevRev = None for sLine in iter(sys.stdin.readline, ""): (sOldRev, sNewRev, sRefName) = sLine.strip().split(" ") if not sRefName.startswith(sRefPrefix): logger.debug("ignoring ref: '%s'" % (sRefName,)) continue if sPrevRev is None: sPrevRev = sOldRev logger.debug("oldrev: '%s', newrev: '%s'" % (sOldRev, sNewRev)) asChangeLogs = get_changes(sOldRev, sNewRev, sFormatSpec, sSeparator) for sMessage in asChangeLogs: logger.debug("Considering commit:\n%s" % (sMessage,)) oMatch = re.search(oBugRegex, sMessage) if oMatch is None: logger.info("Bug id not found in commit:\n%s" % (sMessage,)) continue for oMatch in re.finditer(oBugRegex, sMessage): iBugId = int(oMatch.group("bug")) logger.debug("Found bugid %d" % (iBugId,)) try: oBZ.modify(iBugId, comment=sMessage) except Exception, e: logger.exception("Could not add comment to bug %d" % (iBugId,))
help='Generate stats across all known projects (*.json)') optparser.add_option('-d', '--days', type='int', default=14, help='Number of days to consider') optparser.add_option('-u', '--user', default='russellb', help='gerrit user') optparser.add_option('-k', '--key', default=None, help='ssh key for gerrit') options, args = optparser.parse_args() projects = utils.get_projects_info(options.project, options.all) if not projects: print "Please specify a project." sys.exit(1) all_changes = utils.get_changes(projects, options.user, options.key) reviews = [] for change in all_changes: # print json.dumps(change, sort_keys=True, indent=4) for patchset in change.get('patchSets', []): for review in patchset.get('approvals', []): reviews += [review] cut_off = datetime.datetime.now() - datetime.timedelta(days=options.days) ts = calendar.timegm(cut_off.timetuple()) reviews = filter(lambda x:x['grantedOn'] > ts, reviews) def round_to_day(ts): SECONDS_PER_DAY = 60*60*24