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
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
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)
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)
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)
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}")