Esempio n. 1
0
    def get_package_config_from_repo(
        project: GitProject,
        reference: Optional[str] = None,
        base_project: Optional[GitProject] = None,
        pr_id: int = None,
        fail_when_missing: bool = True,
        spec_file_path: Optional[str] = None,
    ):
        """
        Get the package config and catch the invalid config scenario and possibly no-config scenario
        """

        if not base_project and not project:
            return None

        project_to_search_in = base_project or project
        try:
            package_config: PackageConfig = get_package_config_from_repo(
                project=project_to_search_in,
                ref=reference,
                spec_file_path=spec_file_path,
            )
            if not package_config and fail_when_missing:
                raise PackitConfigException(
                    f"No config file found in {project_to_search_in.full_repo_name} "
                    "on ref '{reference}'"
                )
        except PackitConfigException as ex:
            if pr_id:
                project.pr_comment(
                    pr_id, f"Failed to load packit config file:\n```\n{str(ex)}\n```"
                )
            else:
                # TODO: filter when https://github.com/packit/ogr/issues/308 fixed
                issues = project.get_issue_list()
                if "Invalid packit config" not in [x.title for x in issues]:
                    # TODO: store in DB
                    message = (
                        f"Failed to load packit config file:\n```\n{str(ex)}\n```\n"
                        "For more info, please check out the documentation: "
                        "http://packit.dev/packit-as-a-service/ or contact us - "
                        "[Packit team]"
                        "(https://github.com/orgs/packit/teams/the-packit-team)"
                    )

                    i = project.create_issue(
                        title="[packit] Invalid config", body=message
                    )
                    logger.debug(f"Created issue for invalid packit config: {i.url}")
            raise ex
        return package_config
Esempio n. 2
0
    def _check_pr_event(
        self,
        event: Union[PullRequestGithubEvent, PullRequestCommentGithubEvent,
                     MergeRequestGitlabEvent,
                     MergeRequestCommentGitlabEvent, ],
        project: GitProject,
        service_config: ServiceConfig,
        job_configs: Iterable[JobConfig],
    ) -> bool:
        account_name = event.user_login
        if not account_name:
            raise KeyError(f"Failed to get account_name from {type(event)}")
        namespace = event.target_repo_namespace

        namespace_approved = self.is_approved(namespace)
        user_approved = (project.can_merge_pr(account_name)
                         or project.get_pr(event.pr_id).author == account_name)

        if namespace_approved and user_approved:
            # TODO: clear failing check when present
            return True

        msg = (
            f"Namespace {namespace} is not on our allowlist!"
            if not namespace_approved else
            f"Account {account_name} has no write access nor is author of PR!")
        logger.error(msg)
        if isinstance(
                event,
            (PullRequestCommentGithubEvent, MergeRequestCommentGitlabEvent)):
            project.pr_comment(event.pr_id, msg)
        else:
            for job_config in job_configs:
                job_helper = CoprBuildJobHelper(
                    service_config=service_config,
                    package_config=event.get_package_config(),
                    project=project,
                    metadata=EventData.from_event_dict(event.get_dict()),
                    db_trigger=event.db_trigger,
                    job_config=job_config,
                )
                msg = ("Namespace is not allowed!"
                       if not namespace_approved else "User cannot trigger!")
                job_helper.report_status_to_all(description=msg,
                                                state=CommitStatus.error,
                                                url=FAQ_URL)

        return False
Esempio n. 3
0
    def get_package_config_from_repo(
        self,
        project: GitProject,
        reference: str,
        pr_id: int = None,
        fail_when_missing: bool = True,
    ):
        """
        Get the package config and catch the invalid config scenario and possibly no-config scenario
        Static because of the easier mocking.
        """
        try:
            package_config: PackageConfig = get_package_config_from_repo(
                project, reference
            )
            if not package_config and fail_when_missing:
                raise PackitConfigException(
                    f"No config file found in {project.full_repo_name}"
                )
        except PackitConfigException as ex:
            if pr_id:
                project.pr_comment(
                    pr_id, f"Failed to load packit config file:\n```\n{str(ex)}\n```"
                )
            else:
                # TODO: filter when https://github.com/packit-service/ogr/issues/308 fixed
                issues = project.get_issue_list()
                if "Invalid packit config" not in [x.title for x in issues]:
                    # TODO: store in DB
                    message = (
                        f"Failed to load packit config file:\n```\n{str(ex)}\n```\n"
                        "For more info, please check out the documentation: "
                        "http://packit.dev/packit-as-a-service/ or contact us - "
                        "[Packit team]"
                        "(https://github.com/orgs/packit-service/teams/the-packit-team)"
                    )

                    i = project.create_issue(
                        title="[packit] Invalid config", body=message
                    )
                    logger.debug(f"Created issue for invalid packit config: {i.url}")
            raise ex
        return package_config
    def check_and_report(self, event: Optional[Any], project: GitProject,
                         config: ServiceConfig) -> bool:
        """
        Check if account is approved and report status back in case of PR
        :param config: service config
        :param event: PullRequest and Release TODO: handle more
        :param project: GitProject
        :return:
        """
        # TODO: modify event hierarchy so we can use some abstract classes instead
        if isinstance(event, ReleaseEvent):
            account_name = event.repo_namespace
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)}")
            if not self.is_approved(account_name):
                logger.info(
                    f"Refusing release event on not whitelisted repo namespace"
                )
                return False
            return True
        if isinstance(
                event,
            (CoprBuildEvent, TestingFarmResultsEvent, DistGitEvent,
             InstallationEvent),
        ):
            return True
        if isinstance(event, (PullRequestEvent, PullRequestCommentEvent)):
            account_name = event.github_login
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)}")
            namespace = event.base_repo_namespace
            # FIXME:
            #  Why check account_name when we whitelist namespace only (in whitelist.add_account())?
            if not (self.is_approved(account_name)
                    or self.is_approved(namespace)):
                msg = f"Neither account {account_name} nor owner {namespace} are on our whitelist!"
                logger.error(msg)
                # TODO also check blacklist,
                # but for that we need to know who triggered the action
                if event.trigger == JobTriggerType.comment:
                    project.pr_comment(event.pr_id, msg)
                else:
                    job_helper = CoprBuildJobHelper(
                        config=config,
                        package_config=event.get_package_config(),
                        project=project,
                        event=event,
                    )
                    msg = "Account is not whitelisted!"  # needs to be shorter
                    job_helper.report_status_to_all(description=msg,
                                                    state="error",
                                                    url=FAQ_URL)
                return False
            # TODO: clear failing check when present
            return True
        if isinstance(event, IssueCommentEvent):
            account_name = event.github_login
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)}")
            namespace = event.base_repo_namespace
            # FIXME:
            #  Why check account_name when we whitelist namespace only (in whitelist.add_account())?
            if not (self.is_approved(account_name)
                    or self.is_approved(namespace)):
                msg = f"Neither account {account_name} nor owner {namespace} are on our whitelist!"
                logger.error(msg)
                project.issue_comment(event.issue_id, msg)
                # TODO also check blacklist,
                # but for that we need to know who triggered the action
                return False
            return True

        msg = f"Failed to validate account: Unrecognized event type {type(event)}."
        logger.error(msg)
        raise PackitException(msg)
Esempio n. 5
0
    def check_and_report(self, event: Optional[Any],
                         project: GitProject) -> bool:
        """
        Check if account is approved and report status back in case of PR
        :param event: PullRequest and Release TODO: handle more
        :param project: GitProject
        :return:
        """
        # TODO: modify event hierarchy so we can use some abstract classes instead
        if isinstance(event, ReleaseEvent):
            account_name = event.repo_namespace
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)}")
            if not self.is_approved(account_name):
                logger.info(
                    f"Refusing release event on not whitelisted repo namespace"
                )
                return False
            return True
        if isinstance(
                event,
            (CoprBuildEvent, TestingFarmResultsEvent, DistGitEvent,
             InstallationEvent),
        ):
            return True
        if isinstance(event, (PullRequestEvent, PullRequestCommentEvent)):
            account_name = event.github_login
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)}")
            namespace = event.base_repo_namespace
            if not (self.is_approved(account_name)
                    or self.is_approved(namespace)):
                msg = f"Neither account {account_name} nor owner {namespace} are on our whitelist!"
                logger.error(msg)
                # TODO also check blacklist,
                # but for that we need to know who triggered the action
                if event.trigger == JobTriggerType.comment:
                    project.pr_comment(event.pr_id, msg)
                else:
                    msg = "Account is not whitelisted!"  # needs to be shorter
                    r = BuildStatusReporter(project, event.commit_sha, None)
                    r.report(
                        "failure",
                        msg,
                        url=FAQ_URL,
                        check_names=PRCheckName.get_account_check(),
                    )
                return False
            # TODO: clear failing check when present
            return True
        if isinstance(event, IssueCommentEvent):
            account_name = event.github_login
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)}")
            if not self.is_approved(account_name):
                logger.error(
                    f"User {account_name} is not approved on whitelist!")
                # TODO also check blacklist,
                # but for that we need to know who triggered the action
                msg = "Account is not whitelisted!"
                project.issue_comment(event.issue_id, msg)
                return False
            return True

        msg = f"Failed to validate account: Unrecognized event type {type(event)}."
        logger.error(msg)
        raise PackitException(msg)
Esempio n. 6
0
    def check_and_report(
        self,
        event: Optional[Any],
        project: GitProject,
        service_config: ServiceConfig,
        job_configs: Iterable[JobConfig],
    ) -> bool:
        """
        Check if account is approved and report status back in case of PR
        :param service_config: service config
        :param event: PullRequest and Release TODO: handle more
        :param project: GitProject
        :param job_configs: iterable of jobconfigs - so we know how to update status of the PR
        :return:
        """

        # whitelist checks dont apply to CentOS (Pagure, Gitlab)
        if isinstance(
                event,
            (
                PushPagureEvent,
                PullRequestPagureEvent,
                PullRequestCommentPagureEvent,
                MergeRequestCommentGitlabEvent,
                IssueCommentGitlabEvent,
                MergeRequestGitlabEvent,
                PushGitlabEvent,
            ),
        ):
            logger.info(
                "Centos (Pagure, Gitlab) events don't require whitelist checks."
            )
            return True

        # TODO: modify event hierarchy so we can use some abstract classes instead
        if isinstance(event, (ReleaseEvent, PushGitHubEvent)):
            account_name = event.repo_namespace
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)!r}")
            if not self.is_approved(account_name):
                logger.info(
                    "Refusing release event on not whitelisted repo namespace."
                )
                return False
            return True
        if isinstance(
                event,
            (
                CoprBuildEvent,
                TestingFarmResultsEvent,
                DistGitEvent,
                InstallationEvent,
                KojiBuildEvent,
            ),
        ):
            return True
        if isinstance(event,
                      (PullRequestGithubEvent, PullRequestCommentGithubEvent)):
            account_name = event.user_login
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)}")
            namespace = event.target_repo_namespace
            # FIXME:
            #  Why check account_name when we whitelist namespace only (in whitelist.add_account())?
            if not (self.is_approved(account_name)
                    or self.is_approved(namespace)):
                msg = f"Neither account {account_name} nor owner {namespace} are on our whitelist!"
                logger.error(msg)
                if event.trigger == TheJobTriggerType.pr_comment:
                    project.pr_comment(event.pr_id, msg)
                else:
                    for job_config in job_configs:
                        job_helper = CoprBuildJobHelper(
                            service_config=service_config,
                            package_config=event.get_package_config(),
                            project=project,
                            metadata=EventData.from_event_dict(
                                event.get_dict()),
                            db_trigger=event.db_trigger,
                            job_config=job_config,
                        )
                        msg = "Account is not whitelisted!"  # needs to be shorter
                        job_helper.report_status_to_all(
                            description=msg,
                            state=CommitStatus.error,
                            url=FAQ_URL)
                return False
            # TODO: clear failing check when present
            return True
        if isinstance(event, IssueCommentEvent):
            account_name = event.user_login
            if not account_name:
                raise KeyError(
                    f"Failed to get account_name from {type(event)}")
            namespace = event.repo_namespace
            # FIXME:
            #  Why check account_name when we whitelist namespace only (in whitelist.add_account())?
            if not (self.is_approved(account_name)
                    or self.is_approved(namespace)):
                msg = f"Neither account {account_name} nor owner {namespace} are on our whitelist!"
                logger.error(msg)
                project.issue_comment(event.issue_id, msg)
                return False
            return True

        msg = f"Failed to validate account: Unrecognized event type {type(event)!r}."
        logger.error(msg)
        raise PackitException(msg)
Esempio n. 7
0
    def update_or_create_dist_git_pr(
        self,
        project: GitProject,
        pr_id: int,
        pr_url: str,
        top_commit: str,
        title: str,
        source_ref: str,
        pagure_fork_token: str,
        pagure_package_token: str,
    ) -> None:
        # Sadly, pagure does not support editing initial comments of a PR via the API
        # https://pagure.io/pagure/issue/4111
        # Short-term solution: keep adding comments
        # and get updated info about sg PR ID and commit desc
        for pr in project.get_pr_list():

            sg_pr_id_match = project.search_in_pr(
                pr_id=pr.id,
                filter_regex=DG_PR_COMMENT_KEY_SG_PR + r":\s*(\d+)",
                reverse=True,
                description=True,
            )
            if not sg_pr_id_match:
                logger.debug(f"No automation comment found in dist-git PR: {pr.id}.")
                continue

            sg_pr_id = sg_pr_id_match[1]
            if sg_pr_id_match[1] != str(pr_id):
                logger.debug(
                    f"Dist-git PR `{pr.id}` does not match " f"source-git PR `{pr_id}`."
                )
                continue

            commit_match = project.search_in_pr(
                pr_id=pr.id,
                filter_regex=DG_PR_COMMENT_KEY_SG_COMMIT + r":\s*(\d+)",
                reverse=True,
                description=True,
            )
            if not commit_match:
                logger.debug(
                    f"Dist-git PR `{pr.id}` does not contain top-commit of the "
                    f"source-git PR `{pr_id}`."
                )
                continue

            logger.debug(f"Adding a new comment with update to existing PR.")
            msg = (
                f"New changes were pushed to the upstream pull request\n\n"
                f"[{DG_PR_COMMENT_KEY_SG_PR}: {pr_id}]({pr_url})\n"
                f"{DG_PR_COMMENT_KEY_SG_COMMIT}: {top_commit}"
            )
            # FIXME: consider storing the data above as a git note of the top commit
            project.change_token(pagure_package_token)
            project.pr_comment(pr.id, msg)
            logger.info(f"new comment added on PR {pr.id} ({pr.url})")
            break
        else:
            logger.debug(f"Matching dist-git PR not found => creating a new one.")
            msg = (
                f"This pull request contains changes from upstream "
                f"and is meant to integrate them into Fedora\n\n"
                f"[{DG_PR_COMMENT_KEY_SG_PR}: {pr_id}]({pr_url})\n"
                f"{DG_PR_COMMENT_KEY_SG_COMMIT}: {top_commit}"
            )
            # This pagure call requires token from the package's FORK
            project_fork = project.get_fork()
            project_fork.change_token(pagure_fork_token)
            dist_git_pr_id = project_fork.pr_create(
                title=f"[source-git] {title}",
                body=msg,
                source_branch=source_ref,
                target_branch="master",
            ).id
            logger.info(f"PR created: {dist_git_pr_id}")