def run(self) -> HandlerResults: logger.debug(f"Received testing-farm result:\n{self.event.result}") logger.debug( f"Received testing-farm test results:\n{self.event.tests}") if self.event.result == TestingFarmResult.passed: status = "success" passed = True else: status = "failure" passed = False if (len(self.event.tests) == 1 and self.event.tests[0].name == "/install/copr-build"): logger.debug("No-fmf scenario discovered.") short_msg = "Installation passed" if passed else "Installation failed" else: short_msg = self.event.message r = BuildStatusReporter(self.project, self.event.commit_sha) r.report( state=status, description=short_msg, url=self.event.log_url, check_names=PRCheckName.get_testing_farm_check( self.event.copr_chroot), ) return HandlerResults(success=True, details={})
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: """ account_name = None if isinstance(event, PullRequestEvent): account_name = event.base_repo_namespace if isinstance(event, ReleaseEvent): account_name = event.repo_namespace if account_name: 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 if event.trigger == JobTriggerType.pull_request: r = BuildStatusReporter(project, event.commit_sha, None) msg = "Account is not whitelisted!" r.report( "failure", msg, url=FAQ_URL, check_name=PRCheckName.get_build_check(), ) return False return True
def handle_pull_request(self): if not self.job.metadata.get("targets"): logger.error( "'targets' value is required in packit config for copr_build job" ) collaborators = self.project.who_can_merge_pr() r = BuildStatusReporter(self.project, self.event.commit_sha) if self.event.github_login not in collaborators: msg = "Only collaborators can trigger Packit-as-a-Service" r.set_status("failure", msg, PRCheckName.get_build_check()) return HandlerResults(success=False, details={"msg": msg}) cbh = CoprBuildHandler(self.config, self.package_config, self.project, self.event) handler_results = cbh.run_copr_build() if handler_results["success"]: # Testing farm is triggered just once copr build is finished as it uses copr builds # todo: utilize fedmsg for this. test_job_config = self.get_tests_for_build() if test_job_config: testing_farm_handler = GithubTestingFarmHandler( self.config, test_job_config, self.event) testing_farm_handler.run() else: logger.debug("Testing farm not in the job config.") return HandlerResults(success=True, details={})
def run(self) -> HandlerResults: self.local_project = LocalProject( git_project=self.project, working_dir=self.config.command_handler_work_dir ) r = BuildStatusReporter(self.project, self.event.commit_sha) if self.event.result == TestingFarmResult.passed: status = "success" else: status = "failure" r.report( status, self.event.message, None, self.event.log_url, check_names=PRCheckName.get_testing_farm_check(self.event.copr_chroot), ) return HandlerResults(success=True, details={})
def handle_pull_request(self): if not self.job.metadata.get("targets"): msg = "'targets' value is required in packit config for copr_build job" self.project.pr_comment(self.event.pr_id, msg) return HandlerResults(success=False, details={"msg": msg}) collaborators = self.project.who_can_merge_pr() r = BuildStatusReporter(self.project, self.event.commit_sha) if self.event.github_login not in collaborators | self.config.admins: msg = "Only collaborators can trigger Packit-as-a-Service" check_names = [ f"{PRCheckName.get_build_check(x)}" for x in self.job.metadata.get("targets") ] r.report("failure", msg, check_names=check_names) return HandlerResults(success=False, details={"msg": msg}) cbh = CoprBuildHandler(self.config, self.package_config, self.project, self.event) handler_results = cbh.run_copr_build() return handler_results
def run(self): build = CoprBuildDB().get_build(self.event.build_id) if not build: # TODO: how could this happen? msg = f"Copr build {self.event.build_id} not in CoprBuildDB" logger.warning(msg) return HandlerResults(success=False, details={"msg": msg}) r = BuildStatusReporter(self.event.get_project(), build["commit_sha"]) if self.event.chroot == "srpm-builds": # we don't want to set check for this msg = "SRPM build in copr has started" logger.debug(msg) return HandlerResults(success=True, details={"msg": msg}) r.report( state="pending", description="RPM build has started...", url=copr_url_from_event(self.event), check_names=PRCheckName.get_build_check(self.event.chroot), )
def run(self) -> HandlerResults: self.local_project = LocalProject( git_project=self.project, working_dir=self.config.command_handler_work_dir) logger.info("Running testing farm") r = BuildStatusReporter(self.project, self.event.commit_sha) chroots = self.job.metadata.get("targets") logger.debug(f"Testing farm chroots: {chroots}") for chroot in chroots: pipeline_id = str(uuid.uuid4()) logger.debug(f"Pipeline id: {pipeline_id}") payload: dict = { "pipeline": { "id": pipeline_id }, "api": { "token": self.config.testing_farm_secret }, } logger.debug(f"Payload: {payload}") stg = "-stg" if self.config.deployment == Deployment.stg else "" copr_repo_name = ( f"packit/{self.project.namespace}-{self.project.repo}-" f"{self.event.pr_id}{stg}") payload["artifact"] = { "repo-name": self.event.base_repo_name, "repo-namespace": self.event.base_repo_namespace, "copr-repo-name": copr_repo_name, "copr-chroot": chroot, "commit-sha": self.event.commit_sha, "git-url": self.event.project_url, "git-ref": self.base_ref, } logger.debug("Sending testing farm request...") logger.debug(payload) req = self.send_testing_farm_request(TESTING_FARM_TRIGGER_URL, "POST", {}, json.dumps(payload)) logger.debug(f"Request sent: {req}") if not req: msg = "Failed to post request to testing farm API." logger.debug("Failed to post request to testing farm API.") r.report( "failure", msg, None, "", check_names=PRCheckName.get_testing_farm_check(chroot), ) return HandlerResults(success=False, details={"msg": msg}) else: logger.debug( f"Submitted to testing farm with return code: {req.status_code}" ) """ Response: { "id": "9fa3cbd1-83f2-4326-a118-aad59f5", "success": true, "url": "https://console-testing-farm.apps.ci.centos.org/pipeline/<id>" } """ # success set check on pending if req.status_code != 200: # something went wrong msg = req.json()["message"] r.report( "failure", msg, None, check_names=PRCheckName.get_testing_farm_check(chroot), ) return HandlerResults(success=False, details={"msg": msg}) r.report( "pending", "Tests are running ...", None, req.json()["url"], check_names=PRCheckName.get_testing_farm_check(chroot), ) return HandlerResults(success=True, details={})
def run_copr_build(self) -> HandlerResults: # add suffix stg when using stg app stg = "-stg" if self.config.deployment == Deployment.stg else "" default_project_name = ( f"{self.project.namespace}-{self.project.repo}-{self.event.pr_id}{stg}" ) job = self.get_job_copr_build_metadata() if not job: msg = "No copr_build defined" # we can't report it to end-user at this stage return HandlerResults(success=False, details={"msg": msg}) self.job_project = job.metadata.get("project") or default_project_name self.job_owner = job.metadata.get("owner") or self.api.copr.config.get( "username") if not job.metadata.get("targets"): msg = "'targets' value is required in packit config for copr_build job" self.project.pr_comment(self.event.pr_id, msg) return HandlerResults(success=False, details={"msg": msg}) self.job_chroots = job.metadata.get("targets", []) for test_check_name in (f"{PRCheckName.get_testing_farm_check(x)}" for x in self.job_chroots): self.project.set_commit_status( self.event.commit_sha, "pending", "", "Waiting for a successful RPM build", test_check_name, trim=True, ) r = BuildStatusReporter(self.project, self.event.commit_sha, self.copr_build_model) build_check_names = [ f"{PRCheckName.get_build_check(x)}" for x in self.job_chroots ] msg_retrigger = ( f"You can re-trigger copr build by adding a comment (`/packit copr-build`) " f"into this pull request.") try: r.report( "pending", "SRPM build has just started...", check_names=PRCheckName.get_srpm_build_check(), ) r.report( "pending", "RPM build is waiting for succesfull SPRM build", check_names=build_check_names, ) build_id, _ = self.api.run_copr_build(project=self.job_project, chroots=self.job_chroots, owner=self.job_owner) r.report( "success", "SRPM was built successfully.", check_names=PRCheckName.get_srpm_build_check(), ) # provide common build url while waiting on response from copr url = ("https://copr.fedorainfracloud.org/coprs/" f"{self.job_owner}/{self.job_project}/build/{build_id}/") r.report( "pending", "RPM build has just started...", check_names=build_check_names, url=url, ) # Save copr build with commit information to be able to report status back # after fedmsg copr.build.end arrives copr_build_db = CoprBuildDB() copr_build_db.add_build( build_id, self.event.commit_sha, self.event.pr_id, self.event.base_repo_name, self.event.base_repo_namespace, self.event.base_ref, self.event.project_url, ) except SandcastleTimeoutReached: msg = f"You have reached 10-minute timeout while creating the SRPM. {msg_retrigger}" self.project.pr_comment(self.event.pr_id, msg) msg = "Timeout reached while creating a SRPM." r.report("failure", msg, check_names=build_check_names) return HandlerResults(success=False, details={"msg": msg}) except SandcastleCommandFailed as ex: max_log_size = 1024 * 16 # is 16KB enough? if len(ex.output) > max_log_size: output = "Earlier output was truncated\n\n" + ex.output[ -max_log_size:] else: output = ex.output msg = ( f"There was an error while creating a SRPM. {msg_retrigger}\n" "\nOutput:" "\n```\n" f"{output}" "\n```" f"\nReturn code: {ex.rc}") self.project.pr_comment(self.event.pr_id, msg) import sentry_sdk sentry_sdk.capture_exception(output) msg = "Failed to create a SRPM." r.report("failure", msg, check_names=PRCheckName.get_srpm_build_check()) return HandlerResults(success=False, details={"msg": msg}) except FailedCreateSRPM as ex: # so that we don't have to have sentry sdk installed locally import sentry_sdk sentry_sdk.capture_exception(ex) msg = f"Failed to create a SRPM: {ex}" r.report("failure", ex, check_names=PRCheckName.get_srpm_build_check()) return HandlerResults(success=False, details={"msg": msg}) except Exception as ex: # so that we don't have to have sentry sdk installed locally import sentry_sdk sentry_sdk.capture_exception(ex) msg = f"There was an error while running a copr build:\n```\n{ex}\n```\n" logger.error(msg) self.project.pr_comment(self.event.pr_id, f"{msg}\n{msg_retrigger}") r.report( "failure", "Build failed, check latest comment for details.", check_names=build_check_names, ) return HandlerResults(success=False, details={"msg": msg}) self.copr_build_model.build_id = build_id self.copr_build_model.save() return HandlerResults(success=True, details={})
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 status_reporter(self): if not self._status_reporter: self._status_reporter = BuildStatusReporter( self.project, self.event.commit_sha, self.copr_build_model ) return self._status_reporter
def run_copr_build(self) -> HandlerResults: check_name = PRCheckName.get_build_check() # add suffix stg when using stg app stg = "-stg" if self.config.deployment == Deployment.stg else "" default_project_name = ( f"{self.project.namespace}-{self.project.repo}-{self.event.pr_id}{stg}" ) job = self.get_job_copr_build_metadata() if not job.metadata.get("targets"): logger.error( "'targets' value is required in packit config for copr_build job" ) self.job_project = job.metadata.get("project") or default_project_name self.job_owner = job.metadata.get("owner") or self.api.copr.config.get( "username") self.job_chroots = job.metadata.get("targets") r = BuildStatusReporter(self.project, self.event.commit_sha, self.copr_build_model) msg_retrigger = ( f"You can re-trigger copr build by adding a comment (`/packit copr-build`) " f"into this pull request.") try: r.report("pending", "RPM build has just started...", check_name=check_name) build_id, repo_url = self.api.run_copr_build( project=self.job_project, chroots=self.job_chroots, owner=self.job_owner) except SandcastleTimeoutReached: msg = f"You have reached 10-minute timeout while creating the SRPM. {msg_retrigger}" self.project.pr_comment(self.event.pr_id, msg) msg = "Timeout reached while creating a SRPM." r.report("failure", msg, check_name=check_name) return HandlerResults(success=False, details={"msg": msg}) except SandcastleCommandFailed as ex: max_log_size = 1024 * 16 # is 16KB enough? if len(ex.output) > max_log_size: output = "Earlier output was truncated\n\n" + ex.output[ -max_log_size:] else: output = ex.output msg = ( f"There was an error while creating a SRPM. {msg_retrigger}\n" "\nOutput:" "\n```\n" f"{output}" "\n```" f"\nReturn code: {ex.rc}") self.project.pr_comment(self.event.pr_id, msg) msg = "Failed to create a SRPM." r.report("failure", msg, check_name=check_name) return HandlerResults(success=False, details={"msg": msg}) except FailedCreateSRPM: msg = "Failed to create a SRPM." r.report("failure", msg, check_name=check_name) return HandlerResults(success=False, details={"msg": msg}) except Exception as ex: msg = f"There was an error while running a copr build:\n```\n{ex}\n```\n" logger.error(msg) self.project.pr_comment(self.event.pr_id, f"{msg}\n{msg_retrigger}") r.report( "failure", "Build failed, check latest comment for details.", check_name=check_name, ) return HandlerResults(success=False, details={"msg": msg}) self.copr_build_model.build_id = build_id self.copr_build_model.save() timeout_config = job.metadata.get("timeout") timeout = int(timeout_config) if timeout_config else 60 * 60 * 2 build_state = self.api.watch_copr_build(build_id, timeout, report_func=r.report) if build_state == "succeeded": msg = ( f"Congratulations! The build [has finished]({repo_url})" " successfully. :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.job_owner}/{self.job_project}`\n" "* And now you can install the packages.\n" "\nPlease note that the RPMs should be used only in a testing environment." ) self.project.pr_comment(self.event.pr_id, msg) return HandlerResults(success=True, details={}) else: return HandlerResults( success=False, details={ "msg": (f"Copr build {build_id} failed {build_state}." f"Copr build URL is {repo_url}." f"Handler used by Copr build is {str(self.event.trigger)}" ) }, )
def run(self): build = CoprBuildDB().get_build(self.event.build_id) if not build: # TODO: how could this happen? msg = f"Copr build {self.event.build_id} not in CoprBuildDB" logger.warning(msg) return HandlerResults(success=False, details={"msg": msg}) r = BuildStatusReporter(self.event.get_project(), build["commit_sha"]) url = copr_url_from_event(self.event) build_url = get_copr_build_url(self.event) msg = "RPMs failed to be built." gh_state = "failure" # https://pagure.io/copr/copr/blob/master/f/common/copr_common/enums.py#_42 if self.event.status == 1: if self.event.chroot == "srpm-builds": # we don't want to set check for this msg = "SRPM build in copr has finished" logger.debug(msg) return HandlerResults(success=True, details={"msg": msg}) check_msg = "RPMs were built successfully." gh_state = "success" if not self.was_last_build_successful(): msg = ( f"Congratulations! The build [has finished]({build_url})" " successfully. :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.event.owner}/{self.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.pr_comment(pr_id=self.event.pr_id, body=msg) r.report( state=gh_state, description=check_msg, url=url, check_names=PRCheckName.get_build_check(self.event.chroot), ) test_job_config = self.get_tests_for_build() if test_job_config: testing_farm_handler = GithubTestingFarmHandler( self.config, test_job_config, self.event ) testing_farm_handler.run() else: logger.debug("Testing farm not in the job config.") return HandlerResults(success=True, details={}) r.report( gh_state, msg, url=url, check_names=PRCheckName.get_build_check(self.event.chroot), ) return HandlerResults(success=False, details={"msg": msg})