def run(self) -> HandlerResults: collaborators = self.project.who_can_merge_pr() if self.event.github_login not in collaborators | self.config.admins: msg = "Only collaborators can trigger Packit-as-a-Service" self.project.pr_comment(self.event.pr_id, msg) return HandlerResults(success=True, details={"msg": msg}) cbh = CoprBuildJobHelper(self.config, self.package_config, self.project, self.event) handler_results = cbh.run_copr_build() return handler_results
def handle_pull_request(self): collaborators = self.project.who_can_merge_pr() cbh = CoprBuildJobHelper(self.config, self.package_config, self.project, self.event) if self.event.github_login not in collaborators | self.config.admins: msg = "Only collaborators can trigger Packit-as-a-Service" cbh.report_status_to_all("failure", msg) return HandlerResults(success=False, details={"msg": msg}) handler_results = cbh.run_copr_build() return handler_results
def _check_pr_event( self, event: Union[PullRequestGithubEvent, PullRequestCommentGithubEvent, MergeRequestGitlabEvent, MergeRequestCommentGitlabEvent, ], project: GitProject, service_config: ServiceConfig, job_configs: Iterable[JobConfig], ) -> bool: actor_name = event.actor if not actor_name: raise KeyError( f"Failed to get login of the actor from {type(event)}") project_url = self._strip_protocol_and_add_git(event.project_url) namespace_approved = self.is_approved(project_url) user_approved = (project.can_merge_pr(actor_name) or project.get_pr(event.pr_id).author == actor_name) if namespace_approved and user_approved: # TODO: clear failing check when present return True msg = ( f"Project {project_url} is not on our allowlist!" if not namespace_approved else f"Account {actor_name} has no write access nor is author of PR!") logger.debug(msg) if isinstance( event, (PullRequestCommentGithubEvent, MergeRequestCommentGitlabEvent)): project.get_pr(event.pr_id).comment(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, targets_override=event.targets_override, ) msg = ("Namespace is not allowed!" if not namespace_approved else "User cannot trigger!") job_helper.report_status_to_all(description=msg, state=BaseCommitStatus.neutral, url=FAQ_URL) return False
def run(self) -> HandlerResults: collaborators = self.project.who_can_merge_pr() if self.event.github_login not in collaborators | self.config.admins: self.project.pr_comment(self.event.pr_id, PERMISSIONS_ERROR_WRITE_OR_ADMIN) return HandlerResults( success=True, details={"msg": PERMISSIONS_ERROR_WRITE_OR_ADMIN}) cbh = CoprBuildJobHelper(self.config, self.package_config, self.project, self.event) handler_results = cbh.run_copr_build() return handler_results
def copr_build_helper(self) -> CoprBuildJobHelper: if not self._copr_build_helper: self._copr_build_helper = CoprBuildJobHelper( config=self.config, package_config=self.package_config, project=self.project, event=self.event, job=self.job_config, ) return self._copr_build_helper
def set_srpm_url(self, build_job_helper: CoprBuildJobHelper) -> None: srpm_build = self.build.get_srpm_build() if srpm_build.url is not None: # URL has been already set return srpm_url = build_job_helper.get_build( self.copr_event.build_id).source_package.get("url") srpm_build.set_url(srpm_url)
def copr_build_helper(self) -> CoprBuildJobHelper: if not self._copr_build_helper: self._copr_build_helper = CoprBuildJobHelper( service_config=self.service_config, package_config=self.package_config, project=self.project, metadata=self.data, db_trigger=self.data.db_trigger, job_config=self.job_config, ) return self._copr_build_helper
def run(self) -> TaskResults: user_can_merge_pr = self.project.can_merge_pr(self.data.user_login) if not (user_can_merge_pr or self.data.user_login in self.service_config.admins): self.project.pr_comment(self.db_trigger.pr_id, PERMISSIONS_ERROR_WRITE_OR_ADMIN) return TaskResults( success=True, details={"msg": PERMISSIONS_ERROR_WRITE_OR_ADMIN}) cbh = CoprBuildJobHelper( service_config=self.service_config, package_config=self.package_config, project=self.project, metadata=self.data, db_trigger=self.db_trigger, job_config=self.job_config, ) handler_results = cbh.run_copr_build() return handler_results
def run(self) -> HandlerResults: user_can_merge_pr = self.event.project.can_merge_pr( self.event.user_login) if not (user_can_merge_pr or self.event.user_login in self.config.admins): self.event.project.pr_comment(self.event.pr_id, PERMISSIONS_ERROR_WRITE_OR_ADMIN) return HandlerResults( success=True, details={"msg": PERMISSIONS_ERROR_WRITE_OR_ADMIN}) cbh = CoprBuildJobHelper( config=self.config, package_config=self.event.package_config, project=self.event.project, event=self.event, job=self.job, ) handler_results = cbh.run_copr_build() return handler_results
def copr_build_helper(self) -> CoprBuildJobHelper: if not self._copr_build_helper: self._copr_build_helper = CoprBuildJobHelper( service_config=self.service_config, package_config=self.package_config, project=self.project, metadata=self.data, db_trigger=self.data.db_trigger, job_config=self.job_config, targets_override=self.data.targets_override, pushgateway=self.pushgateway, ) return self._copr_build_helper
def run(self): build_job_helper = CoprBuildJobHelper( service_config=self.service_config, package_config=self.package_config, project=self.project, metadata=self.data, db_trigger=self.db_trigger, job_config=self.job_config, pushgateway=self.pushgateway, ) if self.copr_event.chroot == "srpm-builds": # we don't want to set the check status for this msg = "SRPM build in copr has started." logger.debug(msg) return TaskResults(success=True, details={"msg": msg}) if not self.build: msg = f"Copr build {self.copr_event.build_id} not in CoprBuildDB." logger.warning(msg) return TaskResults(success=False, details={"msg": msg}) self.pushgateway.copr_builds_started.inc() start_time = (datetime.utcfromtimestamp(self.copr_event.timestamp) if self.copr_event.timestamp else None) self.build.set_start_time(start_time) url = get_copr_build_info_url(self.build.id) self.build.set_status("pending") copr_build_logs = self.copr_event.get_copr_build_logs_url() self.build.set_build_logs_url(copr_build_logs) build_job_helper.report_status_to_all_for_chroot( description="RPM build is in progress...", state=BaseCommitStatus.running, url=url, chroot=self.copr_event.chroot, ) msg = f"Build on {self.copr_event.chroot} in copr has started..." return TaskResults(success=True, details={"msg": msg})
def copr_build_helper(self) -> CoprBuildJobHelper: # when reporting state of SRPM build built in Copr targets_override = ({ build.target for build in CoprBuildModel.get_all_by_build_id( str(self.copr_event.build_id)) } if self.copr_event.chroot == COPR_SRPM_CHROOT else None) if not self._copr_build_helper: self._copr_build_helper = CoprBuildJobHelper( service_config=self.service_config, package_config=self.package_config, project=self.project, metadata=self.data, db_trigger=self.db_trigger, job_config=self.job_config, pushgateway=self.pushgateway, targets_override=targets_override, ) return self._copr_build_helper
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 run(self): build_job_helper = CoprBuildJobHelper( service_config=self.service_config, package_config=self.package_config, project=self.project, metadata=self.data, db_trigger=self.db_trigger, job_config=self.job_config, pushgateway=self.pushgateway, ) if self.copr_event.chroot == "srpm-builds": # we don't want to set check for this msg = "SRPM build in copr has finished." logger.debug(msg) return TaskResults(success=True, details={"msg": msg}) if not self.build: # TODO: how could this happen? msg = f"Copr build {self.copr_event.build_id} not in CoprBuildDB." logger.warning(msg) return TaskResults(success=False, details={"msg": msg}) if self.build.status in [ PG_COPR_BUILD_STATUS_FAILURE, PG_COPR_BUILD_STATUS_SUCCESS, ]: msg = (f"Copr build {self.copr_event.build_id} is already" f" processed (status={self.copr_event.build.status}).") logger.info(msg) return TaskResults(success=True, details={"msg": msg}) self.pushgateway.copr_builds_finished.inc() # if the build is needed only for test, it doesn't have the task_accepted_time if self.build.task_accepted_time: copr_build_time = measure_time(end=datetime.now(timezone.utc), begin=self.build.task_accepted_time) self.pushgateway.copr_build_finished_time.observe(copr_build_time) end_time = (datetime.utcfromtimestamp(self.copr_event.timestamp) if self.copr_event.timestamp else None) self.build.set_end_time(end_time) self.set_srpm_url(build_job_helper) url = get_copr_build_info_url(self.build.id) # https://pagure.io/copr/copr/blob/master/f/common/copr_common/enums.py#_42 if self.copr_event.status != COPR_API_SUCC_STATE: failed_msg = "RPMs failed to be built." build_job_helper.report_status_to_all_for_chroot( state=BaseCommitStatus.failure, description=failed_msg, url=url, chroot=self.copr_event.chroot, ) self.build.set_status(PG_COPR_BUILD_STATUS_FAILURE) return TaskResults(success=False, details={"msg": failed_msg}) if (build_job_helper.job_build and build_job_helper.job_build.trigger == JobConfigTriggerType.pull_request and self.copr_event.pr_id and isinstance(self.project, (GithubProject, GitlabProject)) and not self.was_last_packit_comment_with_congratulation() and self.job_config.notifications.pull_request.successful_build): msg = ( f"Congratulations! One of the builds has completed. :champagne:\n\n" "You can install the built RPMs by following these steps:\n\n" "* `sudo yum install -y dnf-plugins-core` on RHEL 8\n" "* `sudo dnf install -y dnf-plugins-core` on Fedora\n" f"* `dnf copr enable {self.copr_event.owner}/{self.copr_event.project_name}`\n" "* And now you can install the packages.\n" "\nPlease note that the RPMs should be used only in a testing environment." ) self.project.get_pr(self.copr_event.pr_id).comment(msg) build_job_helper.report_status_to_build_for_chroot( state=BaseCommitStatus.success, description="RPMs were built successfully.", url=url, chroot=self.copr_event.chroot, ) build_job_helper.report_status_to_test_for_chroot( state=BaseCommitStatus.pending, description="RPMs were built successfully.", url=url, chroot=self.copr_event.chroot, ) self.build.set_status(PG_COPR_BUILD_STATUS_SUCCESS) built_packages = build_job_helper.get_built_packages( int(self.build.build_id), self.build.target) self.build.set_built_packages(built_packages) if (build_job_helper.job_tests and self.copr_event.chroot in build_job_helper.tests_targets): signature( TaskName.testing_farm.value, kwargs={ "package_config": dump_package_config(self.package_config), "job_config": dump_job_config(build_job_helper.job_tests), "event": self.data.get_dict(), "chroot": self.copr_event.chroot, "build_id": self.build.id, }, ).apply_async() else: logger.debug("Testing farm not in the job config.") return TaskResults(success=True, details={})
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)