From 0ec16b0195d1796e905abbd6749d8d6fbf97f649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Kone=C4=8Dn=C3=BD?= Date: Mon, 14 Mar 2022 17:13:03 +0100 Subject: [PATCH] Migrate create_new_branch to toddler in scm_request_processor The method is migrated from fedscm_admin to scm_request_processor. Now we need to continue with writing test for scm_request_processor. --- toddlers.toml.example | 4 + toddlers/plugins/scm_request_processor.py | 212 +++++++++------------- 2 files changed, 91 insertions(+), 125 deletions(-) diff --git a/toddlers.toml.example b/toddlers.toml.example index 63954e3..804b8ab 100644 --- a/toddlers.toml.example +++ b/toddlers.toml.example @@ -101,6 +101,10 @@ dist_git_token = "private random string to change" # This is the same format as used by the distgit_bugzilla_sync cron/app email_overrides_file = "/path/to/email_overrides.toml" +# Path to temporary directory +# Will be used for creating temporary files and directories +temp_dir = "path/to/temporary/folder" + [consumer_config.default.pdc_config] # Configuration to talk to PDC, as understood by pdc-client. server = "https://pdc.fedoraproject.org/rest_api/v1/" diff --git a/toddlers/plugins/scm_request_processor.py b/toddlers/plugins/scm_request_processor.py index 3b52564..abc64a3 100644 --- a/toddlers/plugins/scm_request_processor.py +++ b/toddlers/plugins/scm_request_processor.py @@ -6,14 +6,16 @@ https://pagure.io/releng/fedora-scm-requests/issues. Authors: Michal Konecny """ +import fnmatch import logging import re +from tempfile import TemporaryDirectory import arrow from pagure_messages.issue_schema import IssueNewV1 from toddlers.base import ToddlerBase -from toddlers.utils import bugzilla_system, fedora_account, pagure, pdc +from toddlers.utils import bugzilla_system, fedora_account, pagure, pdc, git from toddlers.exceptions import ValidationError # Regex for branch name validation @@ -67,6 +69,9 @@ class SCMRequestProcessor(ToddlerBase): # Pagure object connected to dist-git dist_git = None + # Path to temporary dir + temp_dir = None + def accepts_topic(self, topic: str) -> bool: """Returns a boolean whether this toddler is interested in messages from this specific topic. @@ -113,6 +118,7 @@ class SCMRequestProcessor(ToddlerBase): self.monitoring_choices = config.get("monitoring_choices") self.pagure_namespace_to_component = config.get("pagure_namespace_to_component") self.pagure_namespace_to_product = config.get("pagure_namespace_to_product") + self.temp_dir = config.get("temp_dir") _log.info("Setting up PDC client") pdc.set_pdc(config) @@ -518,18 +524,19 @@ class SCMRequestProcessor(ToddlerBase): ) return - if create_git_branch: - assert_git_repo_initialized_remotely(namespace, repo) - branch_name = issue_body_json['branch'].strip() - if is_epel(branch_name) and not valid_epel_package(repo, branch_name): - if not force: - prompt_to_close_bad_ticket(issue_json, INVALID_EPEL_ERROR) - return + if re.match(EPEL_REGEX, branch_name) and not self.valid_epel_package(repo, branch_name): + self.pagure_io.close_issue( + issue["id"], + namespace=PROJECT_NAMESPACE, + message=INVALID_EPEL_ERROR, + reason="Invalid" + ) + return # Pagure uses plural names for namespaces, but PDC does not use the # plural version for branch types - branch_type = pdc.component_type_to_singular(namespace) - click.echo('- Checking if {0} already exists in PDC.'.format(branch_name)) + branch_type = namespace.strip().rstrip('s') + _log.info('- Checking if {0} already exists in PDC.'.format(branch_name)) pdc_branch = pdc.get_branch(repo, branch_name, branch_type) if pdc_branch: ticket_text = \ @@ -540,107 +547,84 @@ class SCMRequestProcessor(ToddlerBase): "``git checkout -b && git push -u origin ``.\n" \ "```` is the name of the branch you requested. \n" \ "You only need to do this once and you can then use fedpkg as you normally do." - prompt_to_close_bad_ticket( - issue_json, ticket_text) + self.pagure_io.close_issue( + issue["id"], + namespace=PROJECT_NAMESPACE, + message=ticket_text, + reason="Invalid" + ) return - issue_id = issue_json['id'] - issue_title = issue_json['title'].strip() - issue_owner = issue_json['user']['name'] - issue_ui_url = pagure.get_pagure_issue_url(issue_id) + issue_id = issue['id'] + issue_title = issue['title'].strip() + issue_owner = issue['user']['name'] + issue_ui_url = issue["full_url"] # Check if the branch requestor is one of the maintainers or part of the groups - click.echo('- Checking if {0} is one of the maintainers of the package'.format(issue_owner)) - if force: - msg = ' WARNING: Checking of maintainers is skipped' - click.secho(msg, fg='yellow') - else: - # Get the list of maintainers of the package - maintainers = set(contributors['users']['admin']) | \ - set(contributors['users']['commit']) | \ - set(u['user'] - for u in contributors['users']['collaborators'] - if fnmatch.fnmatch(branch_name, u['branches'])) + _log.info('- Checking if {0} is one of the maintainers of the package'.format(issue_owner)) - # Get the list of FAS groups who can maintain the package - access_groups = set(contributors['groups']['admin']) | \ - set(contributors['groups']['commit']) | \ - set(g['user'] - for g in contributors['groups']['collaborators'] - if fnmatch.fnmatch(branch_name, g['branches'])) - group_member = False - for access_group in access_groups: - # Check if the requestor is part of any of the FAS groups who can maintain the package - if FAS_CLIENT.user_member_of(FAS_CLIENT.get_fas_user(issue_owner), access_group): - group_member = True - break - if issue_owner not in maintainers and not group_member: - prompt_to_close_bad_ticket( - issue_json, '{0} is not a maintainer of the {1} package'.format(issue_owner, repo) - ) - return + # Get the list of maintainers of the package + maintainers = set(contributors['users']['admin']) | \ + set(contributors['users']['commit']) | \ + set(u['user'] + for u in contributors['users']['collaborators'] + if fnmatch.fnmatch(branch_name, u['branches'])) - if auto_approve and \ - not ticket_requires_approval('new_branch', issue_body_json): - click.echo('- Auto-approving the new branch request for "{0}" on ' - '{1}/{2}'.format(branch_name, namespace, repo)) - action = 'approve' - else: - click.echo('\nTicket #{0}'.format(issue_id)) - click.echo(' Ticket Title: {0}'.format(issue_title)) - click.echo(' Ticket Opened By: {0}'.format(issue_owner)) - click.echo(' Ticket URL: {0}'.format(issue_ui_url)) - click.echo(' Action: New Branch') - click.echo(' Namespace: {0}'.format(namespace)) - click.echo(' Name: {0}'.format(repo)) - click.echo(' Branch: {0}'.format(branch_name)) - click.echo(' SLs: {0}'.format(', '.join(sla_list))) - click.echo(' Git Branch: {0}'.format(bool_to_word( - create_git_branch))) - - if not auto_approve: - action = prompt_for_ticket_action() - else: - action = 'approve' - - if action == 'approve': - pagure_url = get_config_item(CONFIG, 'pagure_dist_git_url') - dist_git_url = '{0}/{1}/{2}'.format( - pagure_url.rstrip('/'), namespace, repo) - # If the global component already exists, this will not try to create - # it - pdc.new_global_component(repo, dist_git_url) - # Pagure uses plural names for namespaces, but PDC does not use the - # plural version for branch types - branch_type = pdc.component_type_to_singular(namespace) - - pdc.new_branch(repo, branch_name, branch_type) - for sla, eol in issue_body_json['sls'].items(): - pdc.new_sla_to_branch( - sla, eol, repo, branch_name, branch_type) - - if create_git_branch: - new_git_branch(namespace, repo, branch_name) - new_branch_comment = ('The branch was created in PDC and git. It ' - 'may take up to 10 minutes before you have ' - 'write access on the branch.') - else: - new_branch_comment = ( - 'The branch in PDC was created. Pagure is still processing ' - 'the request, but in about 10 minutes, you may create the ' - 'branch in Pagure using git.') - comment_and_close_ticket(issue_id, bug_id, new_branch_comment, - prompt_for_comment=False) - elif action == 'deny': - comment_body = click.prompt( - 'Please enter a comment explaining the denial') - pagure.close_issue(issue_id, comment_body, 'Denied') - if bug_id: - BUGZILLA_CLIENT.comment(bug_id, comment_body) - else: - # This means the admin is skipping this ticket for now + # Get the list of FAS groups who can maintain the package + access_groups = set(contributors['groups']['admin']) | \ + set(contributors['groups']['commit']) | \ + set(g['user'] + for g in contributors['groups']['collaborators'] + if fnmatch.fnmatch(branch_name, g['branches'])) + group_member = False + for access_group in access_groups: + # Check if the requestor is part of any of the FAS groups who can maintain the package + if fedora_account.user_member_of(fedora_account.get_user_by_username(issue_owner), access_group): + group_member = True + break + if issue_owner not in maintainers and not group_member: + self.pagure_io.close_issue( + issue["id"], + namespace=PROJECT_NAMESPACE, + message='{0} is not a maintainer of the {1} package'.format(issue_owner, repo), + reason="Invalid" + ) return + _log.info("Ticket passed all validations. Creating repository.") + # Create the PDC entry + dist_git_url = '{0}/{1}/{2}'.format( + self.dist_git._pagure_url.rstrip('/'), namespace, repo) + # If the global component already exists, this will not try to create + # it + pdc.new_global_component(repo, dist_git_url) + + pdc.new_branch(repo, branch_name, branch_type) + for sla, eol in issue_body_json['sls'].items(): + pdc.new_sla_to_branch( + sla, eol, repo, branch_name, branch_type) + + if create_git_branch: + with TemporaryDirectory(dir=self.temp_dir) as tmp_dir: + repo = git.clone_repo(dist_git_url, tmp_dir) + default_branch = self.dist_git.get_default_branch(namespace, repo) + commit = repo.first_commit(default_branch) + self.dist_git.new_branch(namespace, repo, branch_name, from_commit=commit) + new_branch_comment = ('The branch was created in PDC and git. It ' + 'may take up to 10 minutes before you have ' + 'write access on the branch.') + else: + new_branch_comment = ( + 'The branch in PDC was created. Pagure is still processing ' + 'the request, but in about 10 minutes, you may create the ' + 'branch in Pagure using git.') + self.pagure_io.close_issue( + issue["id"], + namespace=PROJECT_NAMESPACE, + message=new_branch_comment, + reason="Processed" + ) + def validate_review_bug(self, bug_id: str, pkg: str, branch: str, require_auth: bool = False, check_fas: bool = False, namespace: str = 'rpms', pagure_user: Optional[str] = None @@ -923,25 +907,3 @@ class SCMRequestProcessor(ToddlerBase): 'git branch can\'t be created.') pagure.new_branch( namespace, repo, branch, from_commit=git_obj.first_commit(namespace, repo)) - - - def ticket_requires_approval(issue_type, issue): - """ - Determines if the issue requires approval from an administrator. - :param issue_type: a string determining the issue type - :param issue: a dictionary of the issue - :return: a boolean - """ - # Only some new_branch requests can be auto-approved - if issue_type != 'new_branch': - return True - - namespace = issue['namespace'].strip() - # Only some RPM requests can be auto-approved for now - if namespace != 'rpms': - return True - - branch_name = issue['branch'].strip() - standard_branch = is_valid_standard_branch(branch_name) - return not (standard_branch and not re.match(r'^(el|epel)[0-9]+$', - branch_name))