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.
This commit is contained in:
parent
e94e1e7162
commit
0ec16b0195
2 changed files with 91 additions and 125 deletions
|
@ -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
|
# This is the same format as used by the distgit_bugzilla_sync cron/app
|
||||||
email_overrides_file = "/path/to/email_overrides.toml"
|
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]
|
[consumer_config.default.pdc_config]
|
||||||
# Configuration to talk to PDC, as understood by pdc-client.
|
# Configuration to talk to PDC, as understood by pdc-client.
|
||||||
server = "https://pdc.fedoraproject.org/rest_api/v1/"
|
server = "https://pdc.fedoraproject.org/rest_api/v1/"
|
||||||
|
|
|
@ -6,14 +6,16 @@ https://pagure.io/releng/fedora-scm-requests/issues.
|
||||||
Authors: Michal Konecny <mkonecny@redhat.com>
|
Authors: Michal Konecny <mkonecny@redhat.com>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
import fnmatch
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
from pagure_messages.issue_schema import IssueNewV1
|
from pagure_messages.issue_schema import IssueNewV1
|
||||||
|
|
||||||
from toddlers.base import ToddlerBase
|
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
|
from toddlers.exceptions import ValidationError
|
||||||
|
|
||||||
# Regex for branch name validation
|
# Regex for branch name validation
|
||||||
|
@ -67,6 +69,9 @@ class SCMRequestProcessor(ToddlerBase):
|
||||||
# Pagure object connected to dist-git
|
# Pagure object connected to dist-git
|
||||||
dist_git = None
|
dist_git = None
|
||||||
|
|
||||||
|
# Path to temporary dir
|
||||||
|
temp_dir = None
|
||||||
|
|
||||||
def accepts_topic(self, topic: str) -> bool:
|
def accepts_topic(self, topic: str) -> bool:
|
||||||
"""Returns a boolean whether this toddler is interested in messages
|
"""Returns a boolean whether this toddler is interested in messages
|
||||||
from this specific topic.
|
from this specific topic.
|
||||||
|
@ -113,6 +118,7 @@ class SCMRequestProcessor(ToddlerBase):
|
||||||
self.monitoring_choices = config.get("monitoring_choices")
|
self.monitoring_choices = config.get("monitoring_choices")
|
||||||
self.pagure_namespace_to_component = config.get("pagure_namespace_to_component")
|
self.pagure_namespace_to_component = config.get("pagure_namespace_to_component")
|
||||||
self.pagure_namespace_to_product = config.get("pagure_namespace_to_product")
|
self.pagure_namespace_to_product = config.get("pagure_namespace_to_product")
|
||||||
|
self.temp_dir = config.get("temp_dir")
|
||||||
|
|
||||||
_log.info("Setting up PDC client")
|
_log.info("Setting up PDC client")
|
||||||
pdc.set_pdc(config)
|
pdc.set_pdc(config)
|
||||||
|
@ -518,18 +524,19 @@ class SCMRequestProcessor(ToddlerBase):
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
if create_git_branch:
|
|
||||||
assert_git_repo_initialized_remotely(namespace, repo)
|
|
||||||
|
|
||||||
branch_name = issue_body_json['branch'].strip()
|
branch_name = issue_body_json['branch'].strip()
|
||||||
if is_epel(branch_name) and not valid_epel_package(repo, branch_name):
|
if re.match(EPEL_REGEX, branch_name) and not self.valid_epel_package(repo, branch_name):
|
||||||
if not force:
|
self.pagure_io.close_issue(
|
||||||
prompt_to_close_bad_ticket(issue_json, INVALID_EPEL_ERROR)
|
issue["id"],
|
||||||
return
|
namespace=PROJECT_NAMESPACE,
|
||||||
|
message=INVALID_EPEL_ERROR,
|
||||||
|
reason="Invalid"
|
||||||
|
)
|
||||||
|
return
|
||||||
# Pagure uses plural names for namespaces, but PDC does not use the
|
# Pagure uses plural names for namespaces, but PDC does not use the
|
||||||
# plural version for branch types
|
# plural version for branch types
|
||||||
branch_type = pdc.component_type_to_singular(namespace)
|
branch_type = namespace.strip().rstrip('s')
|
||||||
click.echo('- Checking if {0} already exists in PDC.'.format(branch_name))
|
_log.info('- Checking if {0} already exists in PDC.'.format(branch_name))
|
||||||
pdc_branch = pdc.get_branch(repo, branch_name, branch_type)
|
pdc_branch = pdc.get_branch(repo, branch_name, branch_type)
|
||||||
if pdc_branch:
|
if pdc_branch:
|
||||||
ticket_text = \
|
ticket_text = \
|
||||||
|
@ -540,107 +547,84 @@ class SCMRequestProcessor(ToddlerBase):
|
||||||
"``git checkout -b <branch_name> && git push -u origin <branch_name>``.\n" \
|
"``git checkout -b <branch_name> && git push -u origin <branch_name>``.\n" \
|
||||||
"``<branch_name>`` is the name of the branch you requested. \n" \
|
"``<branch_name>`` 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."
|
"You only need to do this once and you can then use fedpkg as you normally do."
|
||||||
prompt_to_close_bad_ticket(
|
self.pagure_io.close_issue(
|
||||||
issue_json, ticket_text)
|
issue["id"],
|
||||||
|
namespace=PROJECT_NAMESPACE,
|
||||||
|
message=ticket_text,
|
||||||
|
reason="Invalid"
|
||||||
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
issue_id = issue_json['id']
|
issue_id = issue['id']
|
||||||
issue_title = issue_json['title'].strip()
|
issue_title = issue['title'].strip()
|
||||||
issue_owner = issue_json['user']['name']
|
issue_owner = issue['user']['name']
|
||||||
issue_ui_url = pagure.get_pagure_issue_url(issue_id)
|
issue_ui_url = issue["full_url"]
|
||||||
|
|
||||||
# Check if the branch requestor is one of the maintainers or part of the groups
|
# 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))
|
_log.info('- 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']))
|
|
||||||
|
|
||||||
# Get the list of FAS groups who can maintain the package
|
# Get the list of maintainers of the package
|
||||||
access_groups = set(contributors['groups']['admin']) | \
|
maintainers = set(contributors['users']['admin']) | \
|
||||||
set(contributors['groups']['commit']) | \
|
set(contributors['users']['commit']) | \
|
||||||
set(g['user']
|
set(u['user']
|
||||||
for g in contributors['groups']['collaborators']
|
for u in contributors['users']['collaborators']
|
||||||
if fnmatch.fnmatch(branch_name, g['branches']))
|
if fnmatch.fnmatch(branch_name, u['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
|
|
||||||
|
|
||||||
if auto_approve and \
|
# Get the list of FAS groups who can maintain the package
|
||||||
not ticket_requires_approval('new_branch', issue_body_json):
|
access_groups = set(contributors['groups']['admin']) | \
|
||||||
click.echo('- Auto-approving the new branch request for "{0}" on '
|
set(contributors['groups']['commit']) | \
|
||||||
'{1}/{2}'.format(branch_name, namespace, repo))
|
set(g['user']
|
||||||
action = 'approve'
|
for g in contributors['groups']['collaborators']
|
||||||
else:
|
if fnmatch.fnmatch(branch_name, g['branches']))
|
||||||
click.echo('\nTicket #{0}'.format(issue_id))
|
group_member = False
|
||||||
click.echo(' Ticket Title: {0}'.format(issue_title))
|
for access_group in access_groups:
|
||||||
click.echo(' Ticket Opened By: {0}'.format(issue_owner))
|
# Check if the requestor is part of any of the FAS groups who can maintain the package
|
||||||
click.echo(' Ticket URL: {0}'.format(issue_ui_url))
|
if fedora_account.user_member_of(fedora_account.get_user_by_username(issue_owner), access_group):
|
||||||
click.echo(' Action: New Branch')
|
group_member = True
|
||||||
click.echo(' Namespace: {0}'.format(namespace))
|
break
|
||||||
click.echo(' Name: {0}'.format(repo))
|
if issue_owner not in maintainers and not group_member:
|
||||||
click.echo(' Branch: {0}'.format(branch_name))
|
self.pagure_io.close_issue(
|
||||||
click.echo(' SLs: {0}'.format(', '.join(sla_list)))
|
issue["id"],
|
||||||
click.echo(' Git Branch: {0}'.format(bool_to_word(
|
namespace=PROJECT_NAMESPACE,
|
||||||
create_git_branch)))
|
message='{0} is not a maintainer of the {1} package'.format(issue_owner, repo),
|
||||||
|
reason="Invalid"
|
||||||
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
|
|
||||||
return
|
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,
|
def validate_review_bug(self, bug_id: str, pkg: str, branch: str,
|
||||||
require_auth: bool = False, check_fas: bool = False,
|
require_auth: bool = False, check_fas: bool = False,
|
||||||
namespace: str = 'rpms', pagure_user: Optional[str] = None
|
namespace: str = 'rpms', pagure_user: Optional[str] = None
|
||||||
|
@ -923,25 +907,3 @@ class SCMRequestProcessor(ToddlerBase):
|
||||||
'git branch can\'t be created.')
|
'git branch can\'t be created.')
|
||||||
pagure.new_branch(
|
pagure.new_branch(
|
||||||
namespace, repo, branch, from_commit=git_obj.first_commit(namespace, repo))
|
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))
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue