diff --git a/packit_service/worker/events/pagure.py b/packit_service/worker/events/pagure.py index 31decfa0a..313a27af9 100644 --- a/packit_service/worker/events/pagure.py +++ b/packit_service/worker/events/pagure.py @@ -90,14 +90,14 @@ def get_dict(self, default_dict: Optional[Dict] = None) -> dict: return result def get_base_project(self) -> GitProject: - fork = self.project.service.get_project( + project = self.project.service.get_project( namespace=self.base_repo_namespace, repo=self.base_repo_name, username=self.base_repo_owner, - is_fork=True, + is_fork=False, ) - logger.debug(f"Base project: {fork} owned by {self.base_repo_owner}") - return fork + logger.debug(f"Base project: {project} owned by {self.base_repo_owner}") + return project class PullRequestPagureEvent(AddPullRequestDbTrigger, AbstractPagureEvent): diff --git a/packit_service/worker/handlers/distgit.py b/packit_service/worker/handlers/distgit.py index e663f154f..42cf16814 100644 --- a/packit_service/worker/handlers/distgit.py +++ b/packit_service/worker/handlers/distgit.py @@ -10,6 +10,8 @@ from datetime import datetime from typing import Optional, Dict +from fasjson_client import Client +from fasjson_client.errors import APIError from ogr.abstract import PullRequest, PRStatus from packit.api import PackitAPI @@ -23,6 +25,7 @@ from packit_service.config import PackageConfigGetter, ProjectToSync from packit_service.constants import ( CONTACTS_URL, + FASJSON_URL, FILE_DOWNLOAD_FAILURE, MSG_RETRIGGER, ) @@ -39,6 +42,7 @@ ReleaseEvent, AbstractIssueCommentEvent, CheckRerunReleaseEvent, + PullRequestCommentPagureEvent, ) from packit_service.worker.handlers.abstract import ( JobHandler, @@ -380,7 +384,9 @@ def run(self) -> TaskResults: @configured_as(job_type=JobType.koji_build) +@run_for_comment(command="koji-build") @reacts_to(event=PushPagureEvent) +@reacts_to(event=PullRequestCommentPagureEvent) class DownstreamKojiBuildHandler(RetriableJobHandler): """ This handler can submit a build in Koji from a dist-git. @@ -404,6 +410,7 @@ def __init__( ) self.dg_branch = event.get("git_ref") self._pull_request: Optional[PullRequest] = None + self._packit_api = None @property def pull_request(self): @@ -421,10 +428,38 @@ def pull_request(self): self._pull_request = prs[0] return self._pull_request + @property + def packit_api(self): + if not self._packit_api: + self._packit_api = PackitAPI( + self.service_config, + self.job_config, + downstream_local_project=self.local_project, + ) + return self._packit_api + def get_pr_author(self): """Get the login of the author of the PR (if there is any corresponding PR).""" return self.pull_request.author if self.pull_request else None + def is_packager(self, user): + """Check that the given FAS user + is a packager + + Args: + user (str) FAS user account name + Returns: + true if a packager false otherwise + """ + self.packit_api.init_kerberos_ticket() + client = Client(FASJSON_URL) + try: + groups = client.list_user_groups(username=user) + except APIError: + logger.debug(f"Unable to get groups for user {user}.") + return False + return "packager" in [group["groupname"] for group in groups.result] + def pre_check(self) -> bool: if self.data.event_type in (PushPagureEvent.__name__,): if self.data.git_ref not in ( @@ -460,6 +495,17 @@ def pre_check(self) -> bool: f"configuration: {self.job_config.allowed_committers}." ) return False + elif self.data.event_type in (PullRequestCommentPagureEvent.__name__,): + commenter = self.data.actor + logger.debug( + f"Triggering downstream koji build through comment by: {commenter}" + ) + if not self.is_packager(commenter): + logger.info( + f"koji-build retrigger comment event on PR identifier {self.data.pr_id} " + f"done by {commenter} which is not a packager." + ) + return False return True @@ -474,14 +520,14 @@ def run(self) -> TaskResults: if self.service_config.repository_cache else None, ) - packit_api = PackitAPI( - self.service_config, - self.job_config, - downstream_local_project=self.local_project, + branch = ( + self.project.get_pr(self.data.pr_id).target_branch + if self.data.event_type in (PullRequestCommentPagureEvent.__name__,) + else self.dg_branch ) try: - packit_api.build( - dist_git_branch=self.dg_branch, + self.packit_api.build( + dist_git_branch=branch, scratch=self.job_config.scratch, nowait=True, from_upstream=False, @@ -509,12 +555,7 @@ def run(self) -> TaskResults: issue_repo = self.service_config.get_project( url=self.job_config.issue_repository ) - body = ( - f"Koji build on `{self.dg_branch}` branch failed:\n" - "```\n" - f"{ex}\n" - "```" - ) + body = f"Koji build on `{branch}` branch failed:\n" "```\n" f"{ex}\n" "```" PackageConfigGetter.create_issue_if_needed( project=issue_repo, title="Fedora Koji build failed to be triggered", diff --git a/packit_service/worker/jobs.py b/packit_service/worker/jobs.py index 5f50ce4f3..3fccf9ae3 100644 --- a/packit_service/worker/jobs.py +++ b/packit_service/worker/jobs.py @@ -12,7 +12,7 @@ import celery from ogr.exceptions import GithubAppNotInstalledError -from packit.config import JobConfig +from packit.config import JobConfig, JobType, JobConfigTriggerType from packit_service.config import ServiceConfig from packit_service.constants import ( TASK_ACCEPTED, @@ -469,6 +469,27 @@ def is_project_public_or_enabled_private(self) -> bool: return True + def check_explicit_matching(self): + """Force explicit event/jobs matching for triggers + + Returns: + list of jobs + """ + matching_jobs = [] + if isinstance(self.event, PullRequestCommentPagureEvent): + for job in self.event.package_config.jobs: + if ( + job.type == JobType.koji_build + and job.trigger == JobConfigTriggerType.commit + and self.event.job_config_trigger_type + == JobConfigTriggerType.pull_request + ): + # A koji_build job with comment trigger + # can be re-triggered by a Pagure comment in a PR + matching_jobs.append(job) + + return matching_jobs + def get_jobs_matching_event(self) -> List[JobConfig]: """ Get list of non-duplicated all jobs that matches with event's trigger. @@ -481,6 +502,9 @@ def get_jobs_matching_event(self) -> List[JobConfig]: ): jobs_matching_trigger.append(job) + if not jobs_matching_trigger: + jobs_matching_trigger.extend(self.check_explicit_matching()) + return jobs_matching_trigger def get_handlers_for_comment_and_rerun_event(self) -> Set[Type[JobHandler]]: diff --git a/packit_service/worker/parser.py b/packit_service/worker/parser.py index 26b8e592c..f975dc04c 100644 --- a/packit_service/worker/parser.py +++ b/packit_service/worker/parser.py @@ -93,6 +93,7 @@ def parse_event( PushGitlabEvent, PipelineGitlabEvent, PullRequestFlagPagureEvent, + PullRequestCommentPagureEvent, PushPagureEvent, CheckRerunCommitEvent, CheckRerunPullRequestEvent, @@ -136,6 +137,7 @@ def parse_event( Parser.parse_gitlab_push_event, Parser.parse_pipeline_event, Parser.parse_pagure_pr_flag_event, + Parser.parse_pagure_pull_request_comment_event, ), ): if response: @@ -1249,6 +1251,47 @@ def parse_pagure_pr_flag_event(event) -> Optional[PullRequestFlagPagureEvent]: project_namespace=project_namespace, ) + @staticmethod + def parse_pagure_pull_request_comment_event( + event, + ) -> Optional[PullRequestCommentPagureEvent]: + if ".pagure.pull-request.comment." not in (topic := event.get("topic", "")): + return None + logger.info(f"Pagure PR comment event, topic: {topic}") + + action = PullRequestCommentAction.created.value + pr_id = event["pullrequest"]["id"] + base_repo_namespace = event["pullrequest"]["project"]["namespace"] + base_repo_name = event["pullrequest"]["project"]["name"] + base_repo_owner = event["pullrequest"]["repo_from"]["user"]["name"] + target_repo = event["pullrequest"]["repo_from"]["name"] + https_url = event["pullrequest"]["project"]["full_url"] + pagure_login = event["agent"] + commit_sha = event["pullrequest"]["commit_stop"] + + if "added" in event["topic"]: + comment = event["pullrequest"]["comments"][-1]["comment"] + comment_id = event["pullrequest"]["comments"][-1]["id"] + else: + raise ValueError( + f"Unknown comment location in response for {event['topic']}" + ) + + return PullRequestCommentPagureEvent( + action=PullRequestCommentAction[action], + pr_id=pr_id, + base_repo_namespace=base_repo_namespace, + base_repo_name=base_repo_name, + base_repo_owner=base_repo_owner, + base_ref=None, + target_repo=target_repo, + project_url=https_url, + commit_sha=commit_sha, + user_login=pagure_login, + comment=comment, + comment_id=comment_id, + ) + # TODO: Currently not used, merge in Parser or remove # https://github.com/packit/deployment/issues/225 diff --git a/tests/data/fedmsg/pagure_pr_comment.json b/tests/data/fedmsg/pagure_pr_comment.json new file mode 100644 index 000000000..d845a0d13 --- /dev/null +++ b/tests/data/fedmsg/pagure_pr_comment.json @@ -0,0 +1,127 @@ +{ + "agent": "mmassari", + "pullrequest": { + "assignee": null, + "branch": "test-koji-downstream", + "branch_from": "test-koji-downstream-2", + "cached_merge_status": "FFORWARD", + "closed_at": null, + "closed_by": null, + "comments": [ + { + "comment": "/packit koji-build", + "commit": null, + "date_created": "1657783448", + "edited_on": null, + "editor": null, + "filename": null, + "id": 110401, + "line": null, + "notification": false, + "parent": null, + "reactions": {}, + "tree": null, + "user": { + "full_url": "https://src.fedoraproject.org/user/mmassari", + "fullname": "Maja Massarini", + "name": "mmassari", + "url_path": "user/mmassari" + } + } + ], + "commit_start": "beaf90bcecc51968a46663f8d6f092bfdc92e682", + "commit_stop": "beaf90bcecc51968a46663f8d6f092bfdc92e682", + "date_created": "1657783415", + "full_url": "https://src.fedoraproject.org/rpms/python-teamcity-messages/pull-request/36", + "id": 36, + "initial_comment": null, + "last_updated": "1657783448", + "project": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": ["limb"], + "collaborator": [], + "commit": [], + "owner": ["mmassari"], + "ticket": [] + }, + "close_status": [], + "custom_keys": [], + "date_created": "1643654065", + "date_modified": "1650542166", + "description": "The python-teamcity-messages package\\n", + "full_url": "https://src.fedoraproject.org/rpms/python-teamcity-messages", + "fullname": "rpms/python-teamcity-messages", + "id": 54766, + "milestones": {}, + "name": "python-teamcity-messages", + "namespace": "rpms", + "parent": null, + "priorities": {}, + "tags": [], + "url_path": "rpms/python-teamcity-messages", + "user": { + "full_url": "https://src.fedoraproject.org/user/mmassari", + "fullname": "Maja Massarini", + "name": "mmassari", + "url_path": "user/mmassari" + } + }, + "remote_git": null, + "repo_from": { + "access_groups": { + "admin": [], + "collaborator": [], + "commit": [], + "ticket": [] + }, + "access_users": { + "admin": ["limb"], + "collaborator": [], + "commit": [], + "owner": ["mmassari"], + "ticket": [] + }, + "close_status": [], + "custom_keys": [], + "date_created": "1643654065", + "date_modified": "1650542166", + "description": "The python-teamcity-messages package\\n", + "full_url": "https://src.fedoraproject.org/rpms/python-teamcity-messages", + "fullname": "rpms/python-teamcity-messages", + "id": 54766, + "milestones": {}, + "name": "python-teamcity-messages", + "namespace": "rpms", + "parent": null, + "priorities": {}, + "tags": [], + "url_path": "rpms/python-teamcity-messages", + "user": { + "full_url": "https://src.fedoraproject.org/user/mmassari", + "fullname": "Maja Massarini", + "name": "mmassari", + "url_path": "user/mmassari" + } + }, + "status": "Open", + "tags": [], + "threshold_reached": null, + "title": "Not usefull commit", + "uid": "d8b1dd625c674cb9ad8010985bd98351", + "updated_on": "1657783448", + "user": { + "full_url": "https://src.fedoraproject.org/user/mmassari", + "fullname": "Maja Massarini", + "name": "mmassari", + "url_path": "user/mmassari" + } + }, + "topic": "org.fedoraproject.prod.pagure.pull-request.comment.added", + "timestamp": 1657776351.055628 +} diff --git a/tests/unit/test_distgit.py b/tests/unit/test_distgit.py index eb6eb6250..70a7a655f 100644 --- a/tests/unit/test_distgit.py +++ b/tests/unit/test_distgit.py @@ -1,8 +1,16 @@ # Copyright Contributors to the Packit project. # SPDX-License-Identifier: MIT +import json +import pytest + from flexmock import flexmock +from fasjson_client import Client -from packit_service.worker.handlers.distgit import ProposeDownstreamHandler +from packit.api import PackitAPI +from packit_service.worker.handlers.distgit import ( + ProposeDownstreamHandler, + DownstreamKojiBuildHandler, +) from packit_service.worker.events.event import EventData @@ -31,7 +39,8 @@ def test_create_one_issue_for_pr(): url="a url", ) .should_receive("comment") - .once.mock() + .once() + .mock() ] ) flexmock(ProposeDownstreamHandler).should_receive("project").and_return(project) @@ -48,3 +57,33 @@ def test_create_one_issue_for_pr(): "f35": "Propose downstream failed for release 056", } ) + + +PAGURE_PULL_REQUEST_COMMENT_PROCESSED = '{"created_at": 1658228337, "project_url": "https://src.fedoraproject.org/rpms/python-teamcity-messages", "_pr_id": 36, "fail_when_config_file_missing": true, "actor": null, "_package_config_searched": true, "git_ref": null, "identifier": "36", "comment": "/packit koji-build", "comment_id": 110401, "_commit_sha": "beaf90bcecc51968a46663f8d6f092bfdc92e682", "action": "created", "base_repo_namespace": "rpms", "base_repo_name": "python-teamcity-messages", "base_repo_owner": "mmassari", "base_ref": null, "target_repo": "python-teamcity-messages", "user_login": "mmassari", "event_type": "PullRequestCommentPagureEvent", "trigger_id": null, "task_accepted_time": null, "commit_sha": "beaf90bcecc51968a46663f8d6f092bfdc92e682"}' # noqa + + +@pytest.mark.parametrize( + "user_groups,data,check_passed", + [ + pytest.param( + flexmock(result=[{"groupname": "somegroup"}, {"groupname": "packager"}]), + PAGURE_PULL_REQUEST_COMMENT_PROCESSED, + True, + ), + pytest.param( + flexmock(result=[{"groupname": "somegroup"}]), + PAGURE_PULL_REQUEST_COMMENT_PROCESSED, + False, + ), + ], +) +def test_retrigger_downstream_koji_build_pre_check(user_groups, data, check_passed): + data_dict = json.loads(data) + handler = DownstreamKojiBuildHandler(None, None, data_dict) + flexmock(PackitAPI).should_receive("init_kerberos_ticket").and_return(None) + flexmock(Client).should_receive("__getattr__").with_args( + "list_user_groups" + ).and_return(lambda username: user_groups) + + result = handler.pre_check() + assert result == check_passed diff --git a/tests/unit/test_events.py b/tests/unit/test_events.py index c592146e1..062ac9d6b 100644 --- a/tests/unit/test_events.py +++ b/tests/unit/test_events.py @@ -192,6 +192,11 @@ def pagure_pr_flag_updated(self): with open(DATA_DIR / "fedmsg" / "pagure_pr_flag_updated.json") as outfile: return json.load(outfile) + @pytest.fixture() + def pagure_pr_comment_added(self): + with open(DATA_DIR / "fedmsg" / "pagure_pr_comment.json") as outfile: + return json.load(outfile) + @pytest.fixture() def distgit_commit(self): with open(DATA_DIR / "fedmsg" / "distgit_commit.json") as outfile: @@ -772,6 +777,47 @@ def test_parse_pagure_flag(self, pagure_pr_flag_updated): assert event_object.project_name == "packit" assert event_object.project_namespace == "rpms" + def test_parse_pagure_pull_request_comment(self, pagure_pr_comment_added): + event_object = Parser.parse_event(pagure_pr_comment_added) + + assert isinstance(event_object, PullRequestCommentPagureEvent) + assert event_object.pr_id == 36 + assert event_object.base_repo_namespace == "rpms" + assert event_object.base_repo_name == "python-teamcity-messages" + assert event_object.base_repo_owner == "mmassari" + assert event_object.base_ref is None + assert event_object.target_repo == "python-teamcity-messages" + assert event_object.commit_sha == "beaf90bcecc51968a46663f8d6f092bfdc92e682" + assert event_object.user_login == "mmassari" + assert event_object.comment == "/packit koji-build" + assert ( + event_object.project_url + == "https://src.fedoraproject.org/rpms/python-teamcity-messages" + ) + + assert isinstance(event_object.project, PagureProject) + assert event_object.project.full_repo_name == "rpms/python-teamcity-messages" + assert isinstance(event_object.base_project, PagureProject) + assert ( + event_object.base_project.full_repo_name == "rpms/python-teamcity-messages" + ) + + flexmock(PackageConfigGetter).should_receive( + "get_package_config_from_repo" + ).with_args( + base_project=event_object.base_project, + project=event_object.project, + pr_id=36, + reference="beaf90bcecc51968a46663f8d6f092bfdc92e682", + fail_when_missing=False, + ).and_return( + flexmock() + ).once() + flexmock(PagureProject).should_receive("get_web_url").and_return( + "https://src.fedoraproject.org/rpms/python-teamcity-messages" + ) + assert event_object.package_config + @pytest.mark.parametrize("identifier", [None, "foo"]) def test_parse_testing_farm_notification( self, testing_farm_notification, testing_farm_results, identifier @@ -1616,49 +1662,6 @@ def test_update_pull_request_event(self, pagure_pr_update): ) assert event_object.package_config - def test_pull_request_comment_event(self, pagure_pr_comment_added): - centos_event_parser = CentosEventParser() - event_object = centos_event_parser.parse_event(pagure_pr_comment_added) - - assert isinstance(event_object, PullRequestCommentPagureEvent) - assert event_object.pr_id == 16 - assert event_object.base_repo_namespace == "source-git" - assert event_object.base_repo_name == "packit-hello-world" - assert event_object.base_repo_owner == "sakalosj" - assert event_object.base_ref is None - assert event_object.target_repo == "packit-hello-world" - assert event_object.commit_sha == "dfe787d04101728c6ddc213d3f4bf39c969f194c" - assert event_object.user_login == "sakalosj" - assert event_object.comment == "/packit copr-build" - assert ( - event_object.project_url - == "https://git.stg.centos.org/source-git/packit-hello-world" - ) - - assert isinstance(event_object.project, PagureProject) - assert event_object.project.full_repo_name == "source-git/packit-hello-world" - assert isinstance(event_object.base_project, PagureProject) - assert ( - event_object.base_project.full_repo_name - == "fork/sakalosj/source-git/packit-hello-world" - ) - - flexmock(PackageConfigGetter).should_receive( - "get_package_config_from_repo" - ).with_args( - base_project=event_object.base_project, - project=event_object.project, - pr_id=16, - reference="dfe787d04101728c6ddc213d3f4bf39c969f194c", - fail_when_missing=False, - ).and_return( - flexmock() - ).once() - flexmock(PagureProject).should_receive("get_web_url").and_return( - "https://git.stg.centos.org/source-git/packit-hello-world" - ) - assert event_object.package_config - def test_parse_copr_build_event_start( self, copr_build_results_start, copr_build_centos_pr ): diff --git a/tests/unit/test_jobs.py b/tests/unit/test_jobs.py index 6c7034d53..9a33a8b3b 100644 --- a/tests/unit/test_jobs.py +++ b/tests/unit/test_jobs.py @@ -2359,6 +2359,22 @@ def __init__(self): ], [], ), + pytest.param( + PullRequestCommentPagureEvent, + JobConfigTriggerType.pull_request, + [ + JobConfig( + type=JobType.koji_build, + trigger=JobConfigTriggerType.commit, + ), + ], + [ + JobConfig( + type=JobType.koji_build, + trigger=JobConfigTriggerType.commit, + ), + ], + ), ], ) def test_get_jobs_matching_trigger(event_kls, job_config_trigger_type, jobs, result):