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 test_check_names(self) -> List[str]: if not self._test_check_names: self._test_check_names = [ PRCheckName.get_testing_farm_check(chroot) for chroot in self.tests_chroots ] return self._test_check_names
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 build_check_names(self) -> List[str]: if not self._build_check_names: self._build_check_names = [ PRCheckName.get_build_check(chroot) for chroot in self.build_chroots ] return self._build_check_names
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 test_copr_build_just_tests_defined(copr_build_start, pc_tests, copr_build): steve = SteveJobs() flexmock(SteveJobs, _is_private=False) flexmock(CoprHelper).should_receive("get_copr_client").and_return( Client( config={ "copr_url": "https://copr.fedorainfracloud.org", "username": "******", })) flexmock(CoprBuildJobHelper).should_receive("copr_build_model").and_return( flexmock()) flexmock(CoprBuildEvent).should_receive("get_package_config").and_return( pc_tests) flexmock(PRCheckName).should_receive("get_build_check").and_return( PACKIT_STG_CHECK) flexmock(PRCheckName).should_receive("get_testing_farm_check").and_return( PACKIT_STG_TESTING_FARM_CHECK) flexmock(CoprBuild).should_receive("get_by_build_id").and_return( copr_build) flexmock(CoprBuildDB).should_receive("get_build").and_return({ "commit_sha": "XXXXX", "pr_id": 24, "repo_name": "hello-world", "repo_namespace": "packit-service", "ref": "XXXX", "https_url": "https://github.com/packit-service/hello-world", }) url = get_log_url(1) flexmock(requests).should_receive("get").and_return(requests.Response()) flexmock( requests.Response).should_receive("raise_for_status").and_return(None) flexmock(CoprBuild).should_receive("set_status").with_args("pending") flexmock(CoprBuild).should_receive("set_build_logs_url") # check if packit-service sets the correct PR status flexmock(BuildStatusReporter).should_receive("report").with_args( state="pending", description="RPM build has started...", url=url, check_names=PACKIT_STG_CHECK, ).never() flexmock(BuildStatusReporter).should_receive("report").with_args( state="pending", description="RPM build has started...", url=url, check_names=PRCheckName.get_testing_farm_check( copr_build_start["chroot"]), ).once() steve.process_message(copr_build_start)
def test_copr_build_not_comment_on_success(copr_build_end, pc_build, copr_build): steve = SteveJobs() flexmock(SteveJobs, _is_private=False) flexmock(CoprHelper).should_receive("get_copr_client").and_return( Client( config={ "copr_url": "https://copr.fedorainfracloud.org", "username": "******", })) flexmock(CoprBuildJobHelper).should_receive("copr_build_model").and_return( flexmock()) flexmock(CoprBuildEvent).should_receive("get_package_config").and_return( pc_build) flexmock(PRCheckName).should_receive("get_build_check").and_return( PACKIT_STG_CHECK) flexmock(CoprBuildEndHandler).should_receive( "was_last_build_successful").and_return(True) flexmock(GithubProject).should_receive("pr_comment").never() flexmock(CoprBuild).should_receive("get_by_build_id").and_return( copr_build) flexmock(CoprBuild).should_receive("set_status").with_args("success") flexmock(CoprBuildDB).should_receive("get_build").and_return({ "commit_sha": "XXXXX", "pr_id": 24, "repo_name": "hello-world", "repo_namespace": "packit-service", "ref": "XXXX", "https_url": "https://github.com/packit-service/hello-world", }) url = get_log_url(1) flexmock(requests).should_receive("get").and_return(requests.Response()) flexmock( requests.Response).should_receive("raise_for_status").and_return(None) # check if packit-service set correct PR status flexmock(BuildStatusReporter).should_receive("report").with_args( state="success", description="RPMs were built successfully.", url=url, check_names=PRCheckName.get_build_check(copr_build_end["chroot"]), ).once() # skip testing farm flexmock(CoprBuildJobHelper).should_receive("job_tests").and_return(None) steve.process_message(copr_build_end)
def report_status_to_test_for_chroot(self, description, state, url: str = "", chroot: str = ""): if self.job_tests and chroot in self.tests_chroots: self.status_reporter.report( description=description, state=state, url=url, check_names=PRCheckName.get_testing_farm_check(chroot), )
def _process_failed_srpm_build(self, ex): sentry_integration.send_to_sentry(ex) msg = f"Failed to create a SRPM: {ex}" self.status_reporter.report( state="failure", description=str(ex), check_names=PRCheckName.get_srpm_build_check(), ) self.status_reporter.report_tests_failed_because_of_the_build( test_check_names=self.test_check_names ) return HandlerResults(success=False, details={"msg": msg})
def report_status_to_build_for_chroot(self, description, state, url: str = "", chroot: str = ""): if self.job_copr_build and chroot in self.build_chroots: cs = PRCheckName.get_build_check(chroot) self.status_reporter.report( description=description, state=state, url=url, check_names=cs, )
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 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 _process_failed_command(self, 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) sentry_integration.send_to_sentry(output) msg = "Failed to create a SRPM." self.status_reporter.report( state="failure", description=msg, check_names=PRCheckName.get_srpm_build_check(), ) return HandlerResults(success=False, details={"msg": msg})
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 run_testing_farm(self, chroot: str) -> HandlerResults: if chroot not in self.tests_chroots: # Leaving here just to be sure that we will discover this situation if it occurs. # Currently not possible to trigger this situation. msg = f"Target '{chroot}' not defined for tests but triggered." logger.error(msg) send_to_sentry(PackitConfigException(msg)) return HandlerResults(success=False, details={"msg": msg},) if chroot not in self.build_chroots: self.report_missing_build_chroot(chroot) return HandlerResults( success=False, details={ "msg": f"Target '{chroot}' not defined for build. " f"Cannot run tests without build." }, ) check_name = PRCheckName.get_testing_farm_check(chroot) self.status_reporter.report( state="pending", description="Build succeeded. Submitting the tests ...", check_names=check_name, ) 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}, } 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.") self.status_reporter.report( state="failure", description=msg, check_names=check_name, ) 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 if req.json() and "message" in req.json(): msg = req.json()["message"] else: msg = f"Failed to submit tests: {req.reason}" logger.error(msg) self.status_reporter.report( state="failure", description=msg, check_names=check_name, ) return HandlerResults(success=False, details={"msg": msg}) self.status_reporter.report( state="pending", description="Tests are running ...", url=req.json()["url"], check_names=check_name, ) 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 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})
def run_copr_build(self) -> HandlerResults: if not (self.job_copr_build or self.job_tests): msg = "No copr_build or tests job defined." # we can't report it to end-user at this stage return HandlerResults(success=False, details={"msg": msg}) try: self.report_status_to_all(description="Building SRPM ...", state="pending") # we want to get packit logs from the SRPM creation process # so we stuff them into a StringIO buffer stream = StringIO() handler = logging.StreamHandler(stream) packit_logger = logging.getLogger("packit") packit_logger.setLevel(logging.DEBUG) packit_logger.addHandler(handler) formatter = PackitFormatter(None, "%H:%M:%S") handler.setFormatter(formatter) build_id, _ = self.api.run_copr_build( project=self.job_project, chroots=self.build_chroots, owner=self.job_owner, ) packit_logger.removeHandler(handler) stream.seek(0) logs = stream.read() web_url = get_copr_build_url_for_values(self.job_owner, self.job_project, build_id) srpm_build = SRPMBuild.create(logs) status = "pending" description = "Building RPM ..." for chroot in self.build_chroots: copr_build = CoprBuild.get_or_create( pr_id=self.event.pr_id, build_id=str(build_id), commit_sha=self.event.commit_sha, repo_name=self.event.base_repo_name, namespace=self.event.base_repo_namespace, web_url=web_url, target=chroot, status=status, srpm_build=srpm_build, ) url = get_log_url(id_=copr_build.id) self.status_reporter.report( state=status, description=description, url=url, check_names=PRCheckName.get_build_check(chroot), ) if chroot in self.tests_chroots: self.status_reporter.report( state=status, description=description, url=url, check_names=PRCheckName.get_testing_farm_check(chroot), ) except SandcastleTimeoutReached: return self._process_timeout() except SandcastleCommandFailed as ex: return self._process_failed_command(ex) except ApiException as ex: return self._process_openshift_error(ex) except PackitSRPMException as ex: return self._process_failed_srpm_build(ex) except PackitCoprProjectException as ex: return self._process_copr_submit_exception(ex) except PackitCoprException as ex: return self._process_general_exception(ex) except Exception as ex: return self._process_general_exception(ex) self.copr_build_model.build_id = build_id self.copr_build_model.save() return HandlerResults(success=True, details={})
def report_missing_build_chroot(self, chroot: str): self.status_reporter.report( state="error", description=f"No build defined for the target '{chroot}'.", check_names=PRCheckName.get_testing_farm_check(chroot), )
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)}" ) }, )