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.project is dist-git, we need to get upstream dg = DistGit(self.config, self.package_config) self.package_config.upstream_project_url = ( dg.get_project_url_from_distgit_spec()) if not self.package_config.upstream_project_url: return HandlerResults( success=False, details={ "msg": "URL in specfile is not set. " "We don't know where the upstream project lives." }, ) n, r = get_namespace_and_repo_name( self.package_config.upstream_project_url) up = self.project.service.get_project(repo=r, namespace=n) self.local_project = LocalProject( git_project=up, working_dir=self.config.command_handler_work_dir) self.api = PackitAPI(self.config, self.package_config, self.local_project) self.api.sync_from_downstream( # rev is a commit # we use branch on purpose so we get the latest thing # TODO: check if rev is HEAD on {branch}, warn then? dist_git_branch=self.distgit_event.branch, upstream_branch="master", # TODO: this should be configurable ) return HandlerResults(success=True, details={})
def run(self) -> HandlerResults: """ Sync the upstream release to dist-git as a pull request. """ self.local_project = LocalProject( git_project=self.project, working_dir=self.config.command_handler_work_dir) self.api = PackitAPI(self.config, self.package_config, self.local_project) errors = [] for branch in get_branches( self.job.metadata.get("dist-git-branch", "master")): try: self.api.sync_release(dist_git_branch=branch, version=self.event.tag_name) except Exception as ex: sentry_integration.send_to_sentry(ex) errors.append( f"Propose update for branch {branch} failed: {ex}") if errors: return HandlerResults( success=False, details={ "msg": "Propose update failed.", "errors": errors }, ) return HandlerResults(success=True, details={})
def run(self) -> HandlerResults: """ Discover information about organization/user which wants to install packit on his repository Try to whitelist automatically if mapping from github username to FAS account can prove that user is a packager. :return: HandlerResults """ # try to add user to whitelist whitelist = Whitelist() Installation.create( installation_id=self.installation_event.installation_id, event=self.installation_event, ) if not whitelist.add_account(self.installation_event): # Create an issue in our repository, so we are notified when someone install the app self.project.create_issue( title= f"Account: {self.installation_event.account_login} needs to be approved.", body= (f"Hi @{self.installation_event.account_login}, we need to approve you in " "order to start using Packit-as-a-Service. Someone from our team will " "get back to you shortly."), ) msg = f"Account: {self.installation_event.account_login} needs to be approved manually!" logger.info(msg) return HandlerResults(success=True, details={"msg": msg}) return HandlerResults( success=True, details={ "msg": f"Account {self.installation_event.account_login} whitelisted!" }, )
def run(self) -> HandlerResults: """ Sync the upstream release to dist-git as a pull request. """ self.local_project = LocalProject( git_project=self.project, working_dir=self.config.command_handler_work_dir) self.api = PackitAPI(self.config, self.package_config, self.local_project) errors = {} for branch in get_branches( self.job.metadata.get("dist-git-branch", "master")): try: self.api.sync_release(dist_git_branch=branch, version=self.event.tag_name) except Exception as ex: sentry_integration.send_to_sentry(ex) errors[branch] = str(ex) if errors: branch_errors = "" for branch, err in sorted( errors.items(), key=lambda branch_error: branch_error[0]): err_without_new_lines = err.replace("\n", " ") branch_errors += f"| `{branch}` | `{err_without_new_lines}` |\n" body_msg = ( f"Packit failed on creating pull-requests in dist-git:\n\n" f"| dist-git branch | error |\n" f"| --------------- | ----- |\n" f"{branch_errors}\n\n" "You can re-trigger the update by adding `/packit propose-update`" " to the issue comment.\n") self.project.create_issue( title= f"[packit] Propose update failed for release {self.event.tag_name}", body=body_msg, ) return HandlerResults( success=False, details={ "msg": "Propose update failed.", "errors": errors }, ) return HandlerResults(success=True, details={})
def _process_openshift_error(self, ex: ApiException): sentry_integration.send_to_sentry(ex) error_message = f"({ex.status})\nReason: {ex.reason}\n" if ex.headers: error_message += f"HTTP response headers: {ex.headers}\n" if ex.body: try: json_content = json.loads(ex.body) formatted_json = json.dumps(json_content, indent=2) error_message += f"HTTP response body:\n{formatted_json}\n" except json.JSONDecodeError: error_message += f"HTTP response body: {ex.body}\n" msg = ( f"There was a problem in the environment the service is running in:\n" f"```\n" f"{error_message}\n" f"```\n") logger.error(msg) comment_msg = ( f"{msg}\n" f"{self.msg_retrigger}\n\n" "Please, contact " "[Packit team](https://github.com/orgs/packit-service/teams/the-packit-team) " "if the re-trigger did not help.") self.project.pr_comment(self.event.pr_id, comment_msg) self.report_status_to_all( state="error", description="Build failed, check the comments for details.", ) return HandlerResults(success=False, details={"msg": msg})
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 run_testing_farm_on_all(self): failed = {} for chroot in self.tests_chroots: result = self.run_testing_farm(chroot) if not result["success"]: failed[chroot] = result.get("details") if not failed: return HandlerResults(success=True, details={}) return HandlerResults( success=False, details={"msg": f"Failed testing farm targets: '{failed.keys()}'."}.update( failed ), )
def run(self) -> HandlerResults: """ Discover information about organization/user which wants to install packit on his repository Try to whitelist automatically if mapping from github username to FAS account can prove that user is a packager. :return: HandlerResults """ Installation.create( installation_id=self.installation_event.installation_id, event=self.installation_event, ) # try to add user to whitelist whitelist = Whitelist(fas_user=self.config.fas_user, fas_password=self.config.fas_password) account_login = self.installation_event.account_login account_type = self.installation_event.account_type if not whitelist.add_account(self.installation_event): # Create an issue in our repository, so we are notified when someone install the app self.project.create_issue( title=f"{account_type} {account_login} needs to be approved.", body= (f"Hi @{self.installation_event.sender_login}, we need to approve you in " "order to start using Packit-as-a-Service. Someone from our team will " "get back to you shortly.\n\n" "For more info, please check out the documentation: " "http://packit.dev/packit-as-a-service/"), ) msg = f"{account_type} {account_login} needs to be approved manually!" else: msg = f"{account_type} {account_login} whitelisted!" logger.info(msg) return HandlerResults(success=True, details={"msg": msg})
def run(self) -> HandlerResults: self.local_project = LocalProject( git_project=self.project, working_dir=self.config.command_handler_work_dir) self.api = PackitAPI(self.config, self.package_config, self.local_project) 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.issue_comment(self.event.issue_id, msg) return HandlerResults(success=True, details={"msg": msg}) if not self.event.tag_name: msg = ( "There was an error while proposing a new update for the Fedora package: " "no upstream release found.") self.project.issue_comment(self.event.issue_id, msg) return HandlerResults(success=False, details={"msg": "Propose update failed"}) sync_failed = False for branch in self.dist_git_branches_to_sync: msg = ( f"a new update for the Fedora package " f"`{self.package_config.downstream_package_name}`" f"with the tag `{self.event.tag_name}` in the `{branch}` branch.\n" ) try: self.api.sync_release(dist_git_branch=branch, version=self.event.tag_name) msg = f"Packit-as-a-Service proposed {msg}" self.project.issue_comment(self.event.issue_id, msg) except PackitException as ex: msg = f"There was an error while proposing {msg} Traceback is: `{ex}`" self.project.issue_comment(self.event.issue_id, msg) logger.error(f"error while running a build: {ex}") sync_failed = True if sync_failed: return HandlerResults(success=False, details={"msg": "Propose update failed"}) # Close issue if propose-update was successful in all branches self.project.issue_close(self.event.issue_id) return HandlerResults(success=True, details={})
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}) handler_results = HandlerResults(success=True, details={}) logger.debug(f"Test job config: {self.testing_farm_helper.job_tests}") if self.testing_farm_helper.job_tests: self.testing_farm_helper.run_testing_farm_on_all() else: logger.debug("Testing farm not in the job config.") return handler_results
def test_issue_comment_propose_update_handler( mock_issue_comment_functionality, issue_comment_propose_update_event ): flexmock(PackitAPI).should_receive("sync_release").and_return( HandlerResults(success=True, details={}) ) flexmock(SteveJobs, _is_private=False) results = SteveJobs().process_message(issue_comment_propose_update_event) assert results["jobs"]["pull_request_action"]["success"]
def _process_timeout(self): msg = f"You have reached 10-minute timeout while creating SRPM. {self.msg_retrigger}" self.project.pr_comment(self.event.pr_id, msg) msg = "Timeout reached while creating a SRPM." self.report_status_to_all( state="error", description=msg, ) return HandlerResults(success=False, details={"msg": msg})
def test_pr_comment_copr_build_handler( mock_pr_comment_functionality, pr_copr_build_comment_event ): flexmock(CoprBuildHandler).should_receive("run_copr_build").and_return( HandlerResults(success=True, details={}) ) flexmock(SteveJobs, _is_private=False) results = SteveJobs().process_message(pr_copr_build_comment_event) assert results["jobs"]["pull_request_action"]["success"]
def process_jobs(self, event: Event) -> Dict[str, HandlerResults]: """ Run a job handler (if trigger matches) for every job defined in config. """ handlers_results = {} package_config = event.get_package_config() if not package_config: # this happens when service receives events for repos which # don't have packit config, this is not an error msg = "Failed to obtain package config!" logger.info(msg) handlers_results[event.trigger.value] = HandlerResults( success=False, details={"msg": msg}) return handlers_results for job in package_config.jobs: if event.trigger == job.trigger: handler_kls: Type[JobHandler] = JOB_NAME_HANDLER_MAPPING.get( job.job, None) if not handler_kls: logger.warning(f"There is no handler for job {job}") continue handler = handler_kls(self.config, job, event) try: # check whitelist approval for every job to be able to track down which jobs # failed because of missing whitelist approval whitelist = Whitelist() if not whitelist.check_and_report(event, event.get_project()): handlers_results[job.job.value] = HandlerResults( success=False, details={"msg": "Account is not whitelisted!"}, ) return handlers_results logger.debug(f"Running handler: {str(handler_kls)}") handlers_results[job.job.value] = handler.run() # don't break here, other handlers may react to the same event finally: handler.clean() return handlers_results
def process_jobs(self, event: Event) -> Dict[str, HandlerResults]: """ Run a job handler (if trigger matches) for every job defined in config. """ handlers_results = {} package_config = event.get_package_config() if not package_config: # this happens when service receives events for repos which # don't have packit config, this is not an error # success=True - it's not an error that people don't have packit.yaml in their repo handlers_results[event.trigger.value] = HandlerResults( success=True, details={"msg": "No packit config in repo"}) return handlers_results for job in package_config.jobs: if event.trigger == job.trigger: handler_kls: Type[JobHandler] = JOB_NAME_HANDLER_MAPPING.get( job.job, None) if not handler_kls: logger.warning(f"There is no handler for job {job}") continue # check whitelist approval for every job to be able to track down which jobs # failed because of missing whitelist approval whitelist = Whitelist() github_login = getattr(event, "github_login", None) if github_login and github_login in self.config.admins: logger.info(f"{github_login} is admin, you shall pass") elif not whitelist.check_and_report(event, event.get_project()): handlers_results[job.job.value] = HandlerResults( success=False, details={"msg": "Account is not whitelisted!"}) return handlers_results logger.debug(f"Running handler: {str(handler_kls)}") handler = handler_kls(self.config, job, event) handlers_results[job.job.value] = handler.run_n_clean() # don't break here, other handlers may react to the same event return handlers_results
def run(self) -> HandlerResults: if self.event.trigger == JobTriggerType.pull_request: return self.handle_pull_request() # We do not support this workflow officially # elif self.triggered_by == JobTriggerType.release: # self.handle_release() else: return HandlerResults( success=False, details={"msg": f"No handler for {str(self.event.trigger)}"}, )
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}) handler_results = HandlerResults(success=True, details={}) test_job_config = self.get_tests_for_build() logger.debug(f"Test job config: {test_job_config}") if test_job_config: testing_farm_handler = GithubTestingFarmHandler( self.config, test_job_config, self.event) handler_results = testing_farm_handler.run() else: logger.debug("Testing farm not in the job config.") return handler_results
def _process_timeout(self): 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." self.status_reporter.report( state="failure", description=msg, check_names=self.build_check_names ) 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 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 _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 _process_failed_srpm_build(self, ex): sentry_integration.send_to_sentry(ex) msg = ( f"There was an error while creating SRPM. {self.msg_retrigger}\n" "\nOutput:" "\n```\n" f"{ex}" "\n```") self.project.pr_comment(self.event.pr_id, msg) short_msg = "Failed to create SRPM." self.report_status_to_all(description=short_msg, state="error") return HandlerResults(success=False, details={"msg": short_msg})
def process_comment_jobs( self, event: Union[PullRequestCommentEvent, IssueCommentEvent] ) -> HandlerResults: # packit_command can be `/packit propose-update` msg = f"PR comment '{event.comment[:35]}'" try: (packit_mark, *packit_command) = event.comment.split(maxsplit=3) except ValueError: return HandlerResults(success=False, details={"msg": f"{msg} is empty."}) if REQUESTED_PULL_REQUEST_COMMENT != packit_mark: return HandlerResults( success=False, details={"msg": f"{msg} is not handled by packit-service."}, ) if not packit_command: return HandlerResults( success=False, details={ "msg": f"{msg} does not contain a packit-service command." }, ) # packit has command `copr-build`. But PullRequestCommentAction has enum `copr_build`. try: packit_action = CommentAction[packit_command[0].replace("-", "_")] except KeyError: return HandlerResults( success=False, details={ "msg": f"{msg} does not contain a valid packit-service command." }, ) handler_kls: Type[ CommentActionHandler] = COMMENT_ACTION_HANDLER_MAPPING.get( packit_action, None) if not handler_kls: return HandlerResults( success=False, details={"msg": f"{msg} is not a packit-service command."}, ) handler = handler_kls(self.config, event) try: # check whitelist approval for every job to be able to track down which jobs # failed because of missing whitelist approval whitelist = Whitelist() if not whitelist.check_and_report(event, event.get_project()): handlers_results = HandlerResults( success=False, details={"msg": "Account is not whitelisted!"}) return handlers_results handlers_results = handler.run() finally: handler.clean() return handlers_results
def run(self) -> HandlerResults: self.local_project = LocalProject( git_project=self.project, working_dir=self.config.command_handler_work_dir ) self.api = PackitAPI(self.config, self.package_config, self.local_project) self.api.sync_pr( pr_id=self.pr_event.pr_id, dist_git_branch=self.job.metadata.get("dist-git-branch", "master"), # TODO: figure out top upstream commit for source-git here ) 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 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) -> HandlerResults: is_copr_build: Callable[ [JobConfig], bool] = lambda job: job.job == JobType.copr_build if self.job.job == JobType.tests and any( filter(is_copr_build, self.package_config.jobs)): return HandlerResults( success=False, details={ "msg": "Skipping build for testing. The COPR build is defined in the config." }, ) if self.event.trigger == JobTriggerType.pull_request: return self.handle_pull_request() # We do not support this workflow officially # elif self.triggered_by == JobTriggerType.release: # self.handle_release() else: return HandlerResults( success=False, details={"msg": f"No handler for {str(self.event.trigger)}"}, )
def _process_general_exception(self, ex): sentry_integration.send_to_sentry(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}") self.status_reporter.report( state="failure", description="Build failed, check latest comment for details.", check_names=self.build_check_names, ) 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 run(self) -> HandlerResults: collaborators = self.project.who_can_merge_pr() if self.event.github_login not in collaborators: msg = "Only collaborators can trigger Packit-as-a-Service" self.project.pr_comment(self.event.pr_id, msg) 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={}) return handler_results
def _process_general_exception(self, ex): sentry_integration.send_to_sentry(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{self.msg_retrigger}") self.report_status_to_build( state="failure", description="Build failed, check latest comment for details.", ) self.report_status_to_tests( state="error", description="Build failed, check latest comment for details.", ) return HandlerResults(success=False, details={"msg": msg})