diff --git a/easybuild/main.py b/easybuild/main.py index 990a2a5ea6..1c440100ce 100644 --- a/easybuild/main.py +++ b/easybuild/main.py @@ -64,7 +64,7 @@ from easybuild.tools.filetools import locate_files, read_file, register_lock_cleanup_signal_handlers, write_file from easybuild.tools.github import check_github, close_pr, find_easybuild_easyconfig from easybuild.tools.github import add_pr_labels, install_github_token, list_prs, merge_pr, new_branch_github, new_pr -from easybuild.tools.github import new_pr_from_branch +from easybuild.tools.github import new_pr_from_branch, summarize_prs from easybuild.tools.github import sync_branch_with_develop, sync_pr_with_develop, update_branch, update_pr from easybuild.tools.hooks import START, END, load_hooks, run_hook from easybuild.tools.modules import modules_tool @@ -644,6 +644,9 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None): elif options.list_prs: print(list_prs(options.list_prs)) + elif options.summarize_prs: + print(summarize_prs(options.summarize_prs)) + elif options.merge_pr: merge_pr(options.merge_pr) @@ -680,6 +683,7 @@ def main(args=None, logfile=None, do_build=None, testing=False, modtool=None): options.list_prs, options.merge_pr, options.review_pr, + options.summarize_prs, options.terse, search_query, ] diff --git a/easybuild/tools/github.py b/easybuild/tools/github.py index e331f81050..c48e9a5654 100644 --- a/easybuild/tools/github.py +++ b/easybuild/tools/github.py @@ -95,6 +95,7 @@ GITHUB_FRAMEWORK_REPO = 'easybuild-framework' GITHUB_DEVELOP_BRANCH = 'develop' GITHUB_FILE_TYPE = u'file' +GITHUB_PR_GROUPS = ['author', 'labels'] GITHUB_PR_STATE_OPEN = 'open' GITHUB_PR_STATES = [GITHUB_PR_STATE_OPEN, 'closed', 'all'] GITHUB_PR_ORDER_CREATED = 'created' @@ -1418,6 +1419,50 @@ def list_prs(params, per_page=GITHUB_MAX_PER_PAGE, github_user=None): return '\n'.join(lines) +def summarize_prs(params, per_page=GITHUB_MAX_PER_PAGE, github_user=None): + """ + Summarize pull requests according to specified selection/order parameters + + :param params: 4-tuple with selection parameters for PRs (, , , ), + group is one of GITHUB_PR_GROUPS, for the rest see + see https://developer.github.com/v3/pulls/#parameters + """ + group_by = params[0] + + parameters = { + 'state': params[1], + 'sort': params[2], + 'direction': 'asc', # params[3], + 'per_page': per_page, + } + print_msg("Summarizing PRs with parameters: %s" % ', '.join(k + '=' + str(parameters[k]) + for k in sorted(parameters))) + + pr_target_account = build_option('pr_target_account') + pr_target_repo = build_option('pr_target_repo') or GITHUB_EASYCONFIGS_REPO + + pr_data, _ = fetch_pr_data(None, pr_target_account, pr_target_repo, github_user, **parameters) + + summary = {} + for pr in pr_data: + if group_by == 'author': + groups = [pr['user']['login']] + elif group_by == 'labels': + groups = [label['name'] for label in pr['labels']] + else: + groups = [pr[group_by]] + + for group in groups: + if group not in summary: + summary[group] = 1 + else: + summary[group] += 1 + + lines = ["%s: %s" % (key, count) for key, count in sorted(summary.items(), key=lambda kv: kv[1], reverse=True)] + + return '\n'.join(lines) + + def merge_pr(pr): """ Merge specified pull request diff --git a/easybuild/tools/options.py b/easybuild/tools/options.py index 1fe7a7d88f..819f5917aa 100644 --- a/easybuild/tools/options.py +++ b/easybuild/tools/options.py @@ -83,7 +83,7 @@ from easybuild.tools.environment import restore_env, unset_env_vars from easybuild.tools.filetools import CHECKSUM_TYPE_SHA256, CHECKSUM_TYPES, expand_glob_paths, install_fake_vsc from easybuild.tools.filetools import move_file, which -from easybuild.tools.github import GITHUB_PR_DIRECTION_DESC, GITHUB_PR_ORDER_CREATED +from easybuild.tools.github import GITHUB_PR_DIRECTION_DESC, GITHUB_PR_ORDER_CREATED, GITHUB_PR_GROUPS from easybuild.tools.github import GITHUB_PR_STATE_OPEN, GITHUB_PR_STATES, GITHUB_PR_ORDERS, GITHUB_PR_DIRECTIONS from easybuild.tools.github import HAVE_GITHUB_API, HAVE_KEYRING, VALID_CLOSE_PR_REASONS from easybuild.tools.github import fetch_easyblocks_from_pr, fetch_github_token @@ -129,6 +129,8 @@ def terminal_supports_colors(stream): DEFAULT_LIST_PR_ORDER = GITHUB_PR_ORDER_CREATED DEFAULT_LIST_PR_DIREC = GITHUB_PR_DIRECTION_DESC +DEFAULT_SUMMARIZE_PR_GROUP = 'author' + _log = fancylogger.getLogger('options', fname=False) @@ -722,6 +724,10 @@ def github_options(self): 'review-pr-filter': ("Regex used to filter out easyconfigs to diff against in --review-pr", None, 'regex', None), 'review-pr-max': ("Maximum number of easyconfigs to diff against in --review-pr", int, 'store', None), + 'summarize-prs': ("Summarize pull requests", str, 'store_or_None', + ",".join([DEFAULT_SUMMARIZE_PR_GROUP, DEFAULT_LIST_PR_STATE, DEFAULT_LIST_PR_ORDER, + DEFAULT_LIST_PR_DIREC]), + {'metavar': 'GROUP,STATE,ORDER,DIRECTION'}), 'test-report-env-filter': ("Regex used to filter out variables in environment dump of test report", None, 'regex', None), 'update-branch-github': ("Update specified branch in GitHub", str, 'store', None), @@ -959,6 +965,10 @@ def postprocess(self): if self.options.list_prs: self._postprocess_list_prs() + # make sure --list-prs has a valid format + if self.options.summarize_prs: + self._postprocess_summarize_prs() + # handle configuration options that affect other configuration options self._postprocess_config() @@ -1025,6 +1035,34 @@ def _postprocess_list_prs(self): self.options.list_prs = (list_pr_state, list_pr_order, list_pr_direc) + def _postprocess_summarize_prs(self): + """Postprocess --summarize-prs options""" + summarize_pr_parts = self.options.summarize_prs.split(',') + nparts = len(summarize_pr_parts) + + if nparts > 4: + raise EasyBuildError("Argument to --summarize-prs must be in the format 'group[,state[,order[,direction]]]") + + summarize_pr_group = summarize_pr_parts[0] + summarize_pr_state = summarize_pr_parts[1] if nparts > 1 else DEFAULT_LIST_PR_STATE + summarize_pr_order = summarize_pr_parts[2] if nparts > 2 else DEFAULT_LIST_PR_ORDER + summarize_pr_direc = summarize_pr_parts[3] if nparts > 3 else DEFAULT_LIST_PR_DIREC + + if summarize_pr_group not in GITHUB_PR_GROUPS: + raise EasyBuildError("1st item in --summarize-prs ('%s') must be one of %s", + summarize_pr_group, GITHUB_PR_GROUPS) + if summarize_pr_state not in GITHUB_PR_STATES: + raise EasyBuildError("2nd item in --summarize-prs ('%s') must be one of %s", + summarize_pr_state, GITHUB_PR_STATES) + if summarize_pr_order not in GITHUB_PR_ORDERS: + raise EasyBuildError("3rd item in --summarize-prs ('%s') must be one of %s", + summarize_pr_order, GITHUB_PR_ORDERS) + if summarize_pr_direc not in GITHUB_PR_DIRECTIONS: + raise EasyBuildError("4th item in --summarize-prs ('%s') must be one of %s", + summarize_pr_direc, GITHUB_PR_DIRECTIONS) + + self.options.summarize_prs = (summarize_pr_group, summarize_pr_state, summarize_pr_order, summarize_pr_direc) + def _postprocess_include(self): """Postprocess --include options.""" # set up included easyblocks, module naming schemes and toolchains/toolchain components