def report_successful_build(self): if (self.copr_build_helper.job_build and self.copr_build_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) url = get_copr_build_info_url(self.build.id) self.copr_build_helper.report_status_to_build_for_chroot( state=BaseCommitStatus.success, description="RPMs were built successfully.", url=url, chroot=self.copr_event.chroot, ) self.copr_build_helper.report_status_to_test_for_chroot( state=BaseCommitStatus.pending, description="RPMs were built successfully.", url=url, chroot=self.copr_event.chroot, )
def run(self): if not self.build: model = ("SRPMBuildDB" if self.copr_event.chroot == COPR_SRPM_CHROOT else "CoprBuildDB") msg = f"Copr build {self.copr_event.build_id} not in {model}." logger.warning(msg) return TaskResults(success=False, details={"msg": msg}) self.set_start_time() self.set_logs_url() if self.copr_event.chroot == COPR_SRPM_CHROOT: url = get_srpm_build_info_url(self.build.id) self.copr_build_helper.report_status_to_all( description="SRPM build is in progress...", state=BaseCommitStatus.running, url=url, ) msg = "SRPM build in Copr has started..." return TaskResults(success=True, details={"msg": msg}) self.pushgateway.copr_builds_started.inc() url = get_copr_build_info_url(self.build.id) self.build.set_status("pending") self.copr_build_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 handle_rpm_build_start( self, build_id: int, web_url: str, waiting_for_srpm: bool = False ): """ Create models for Copr build chroots and report start of RPM build if the SRPM is already built. """ unprocessed_chroots = [] for chroot in self.build_targets: if chroot not in self.available_chroots: self.report_status_to_all_for_chroot( state=BaseCommitStatus.error, description=f"Not supported target: {chroot}", url=get_srpm_build_info_url(self.srpm_model.id), chroot=chroot, ) self.monitor_not_submitted_copr_builds(1, "not_supported_target") unprocessed_chroots.append(chroot) continue copr_build = CoprBuildModel.create( build_id=str(build_id), commit_sha=self.metadata.commit_sha, project_name=self.job_project, owner=self.job_owner, web_url=web_url, target=chroot, status="waiting_for_srpm" if waiting_for_srpm else "pending", run_model=self.run_model, task_accepted_time=self.metadata.task_accepted_time, ) if not waiting_for_srpm: url = get_copr_build_info_url(id_=copr_build.id) self.report_status_to_all_for_chroot( state=BaseCommitStatus.running, description="Starting RPM build...", url=url, chroot=chroot, ) if unprocessed_chroots: unprocessed = "\n".join(sorted(unprocessed_chroots)) available = "\n".join(sorted(self.available_chroots)) self.status_reporter.comment( body="There are build targets that are not supported by COPR.\n" "<details>\n<summary>Unprocessed build targets</summary>\n\n" f"```\n{unprocessed}\n```\n</details>\n" "<details>\n<summary>Available build targets</summary>\n\n" f"```\n{available}\n```\n</details>", ) # release the hounds! celery_app.send_task( "task.babysit_copr_build", args=(build_id,), countdown=120, # do the first check in 120s )
def run_with_copr_builds(self, targets: List[str], failed: Dict): targets_without_builds = [] targets_with_builds = {} for target in targets: if self.build_id: copr_build = CoprBuildModel.get_by_id(self.build_id) else: copr_build = self.testing_farm_job_helper.get_latest_copr_build( target=target, commit_sha=self.data.commit_sha) if copr_build: targets_with_builds[target] = copr_build else: targets_without_builds.append(target) # Trigger copr build for targets missing build if targets_without_builds: logger.info( f"Missing Copr build for targets {targets_without_builds} in " f"{self.testing_farm_job_helper.job_owner}/" f"{self.testing_farm_job_helper.job_project}" f" and commit:{self.data.commit_sha}, running a new Copr build." ) for missing_target in targets_without_builds: self.testing_farm_job_helper.report_status_to_test_for_chroot( state=BaseCommitStatus.pending, description="Missing Copr build for this target, " "running a new Copr build.", url="", chroot=missing_target, ) event_data = self.data.get_dict() event_data["targets_override"] = targets_without_builds self.run_copr_build_handler(event_data, len(targets_without_builds)) for target, copr_build in targets_with_builds.items(): if copr_build.status != PG_COPR_BUILD_STATUS_SUCCESS: logger.info( "The latest build was not successful, not running tests for it." ) self.testing_farm_job_helper.report_status_to_test_for_chroot( state=BaseCommitStatus.failure, description="The latest build was not successful, " "not running tests for it.", chroot=target, url=get_copr_build_info_url(copr_build.id), ) continue logger.info(f"Running testing farm for {copr_build}:{target}.") self.run_for_target(target=target, build=copr_build, failed=failed)
def test_copr_build_not_comment_on_success(copr_build_end, pc_build_pr, copr_build_pr): flexmock(GithubProject).should_receive("is_private").and_return(False) flexmock(GithubProject).should_receive("get_pr").and_return( flexmock(source_project=flexmock()).should_receive("comment").never()) flexmock(AbstractCoprBuildEvent).should_receive( "get_package_config").and_return(pc_build_pr) flexmock(CoprBuildJobHelper).should_receive("get_build_check").and_return( EXPECTED_BUILD_CHECK_NAME) flexmock(CoprBuildEndHandler).should_receive( "was_last_packit_comment_with_congratulation").and_return(True) flexmock(CoprBuildModel).should_receive("get_by_build_id").and_return( copr_build_pr) copr_build_pr.should_call("set_status").with_args("success").once() copr_build_pr.should_receive("set_end_time").once() url = get_copr_build_info_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(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.success, description="RPMs were built successfully.", url=url, check_names=CoprBuildJobHelper.get_build_check( copr_build_end["chroot"]), markdown_content=None, ).once() # skip testing farm flexmock(CoprBuildJobHelper).should_receive( "get_built_packages").and_return([]) flexmock(CoprBuildJobHelper).should_receive("job_tests").and_return(None) flexmock(Signature).should_receive("apply_async").once() flexmock(Pushgateway).should_receive("push").once().and_return() # skip SRPM url since it touches multiple classes flexmock(CoprBuildEndHandler).should_receive("set_srpm_url").and_return( None) processing_results = SteveJobs().process_message(copr_build_end) event_dict, job, job_config, package_config = get_parameters_from_results( processing_results) assert json.dumps(event_dict) run_copr_build_end_handler( package_config=package_config, event=event_dict, job_config=job_config, )
def run(self): if not self.build: # TODO: how could this happen? model = ("SRPMBuildDB" if self.copr_event.chroot == COPR_SRPM_CHROOT else "CoprBuildDB") msg = f"Copr build {self.copr_event.build_id} not in {model}." logger.warning(msg) return TaskResults(success=False, details={"msg": msg}) if self.build.status in [ PG_BUILD_STATUS_FAILURE, PG_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.set_end_time() self.set_srpm_url() if self.copr_event.chroot == COPR_SRPM_CHROOT: return self.handle_srpm_end() 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) # 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." self.copr_build_helper.report_status_to_all_for_chroot( state=BaseCommitStatus.failure, description=failed_msg, url=get_copr_build_info_url(self.build.id), chroot=self.copr_event.chroot, ) self.build.set_status(PG_BUILD_STATUS_FAILURE) return TaskResults(success=False, details={"msg": failed_msg}) self.report_successful_build() self.build.set_status(PG_BUILD_STATUS_SUCCESS) built_packages = self.copr_build_helper.get_built_packages( int(self.build.build_id), self.build.target) self.build.set_built_packages(built_packages) self.handle_testing_farm() return TaskResults(success=True, details={})
def test_copr_build_just_tests_defined(copr_build_start, pc_tests, copr_build_pr): flexmock(GithubProject).should_receive("is_private").and_return(False) flexmock(GithubProject).should_receive("get_pr").and_return( flexmock(source_project=flexmock())) flexmock(AbstractCoprBuildEvent).should_receive( "get_package_config").and_return(pc_tests) flexmock(TestingFarmJobHelper).should_receive( "get_build_check").and_return(EXPECTED_BUILD_CHECK_NAME) flexmock(TestingFarmJobHelper).should_receive("get_test_check").and_return( EXPECTED_TESTING_FARM_CHECK_NAME) flexmock(CoprBuildModel).should_receive("get_by_build_id").and_return( copr_build_pr) url = get_copr_build_info_url(1) flexmock(requests).should_receive("get").and_return(requests.Response()) flexmock( requests.Response).should_receive("raise_for_status").and_return(None) copr_build_pr.should_receive("set_start_time").once() copr_build_pr.should_call("set_status").with_args("pending").once() copr_build_pr.should_receive("set_build_logs_url") # check if packit-service sets the correct PR status flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.running, description="RPM build is in progress...", url=url, check_names=EXPECTED_BUILD_CHECK_NAME, markdown_content=None, ).never() flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.running, description="RPM build is in progress...", url=url, check_names=TestingFarmJobHelper.get_test_check( copr_build_start["chroot"]), markdown_content=None, ).once() flexmock(Signature).should_receive("apply_async").once() flexmock(Pushgateway).should_receive("push").once().and_return() processing_results = SteveJobs().process_message(copr_build_start) event_dict, job, job_config, package_config = get_parameters_from_results( processing_results) assert json.dumps(event_dict) run_copr_build_start_handler( package_config=package_config, event=event_dict, job_config=job_config, )
def test_get_logs(client): chroot = "foo-1-x86_64" state = "success" build_id = 2 project_mock = flexmock() project_mock.namespace = "john-foo" project_mock.repo_name = "bar" pr_mock = flexmock() pr_mock.job_trigger_model_type = JobTriggerModelType.pull_request pr_mock.pr_id = 234 pr_mock.target_project = project_mock srpm_build_mock = flexmock() srpm_build_mock.id = 11 srpm_build_mock.url = "https://some.random.copr.subdomain.org/my_srpm.srpm" srpm_build_mock.build_submitted_time = datetime( year=2020, month=1, day=1, hour=0, minute=0, second=0, microsecond=0 ) copr_build_mock = flexmock() copr_build_mock.target = chroot copr_build_mock.build_id = str(build_id) copr_build_mock.status = state copr_build_mock.web_url = ( "https://copr.fedorainfracloud.org/coprs/john-foo-bar/john-foo-bar/build/2/" ) copr_build_mock.build_logs_url = "https://localhost:5000/build/2/foo-1-x86_64/logs" copr_build_mock.owner = "packit" copr_build_mock.build_submitted_time = datetime( year=2020, month=1, day=1, hour=0, minute=0, second=0, microsecond=0 ) copr_build_mock.project_name = "example_project" copr_build_mock.should_receive("get_trigger_object").and_return(pr_mock) copr_build_mock.should_receive("get_project").and_return(project_mock) copr_build_mock.should_receive("get_srpm_build").and_return(srpm_build_mock) flexmock(CoprBuildModel).should_receive("get_by_id").and_return(copr_build_mock) logs_url = get_copr_build_info_url(1) assert logs_url == "https://localhost/results/copr-builds/1"
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 test_copr_build_end_failed_testing_farm_no_json(copr_build_end, copr_build_pr): flexmock(GithubProject).should_receive("is_private").and_return(False) flexmock(GithubProject).should_receive("get_pr").and_return( flexmock( source_project=flexmock(get_web_url=lambda: "abc"), target_branch_head_commit="deadbeef", ).should_receive("comment").mock()) config = PackageConfig(jobs=[ JobConfig( type=JobType.copr_build, trigger=JobConfigTriggerType.pull_request, metadata=JobMetadataConfig( _targets=["fedora-rawhide"], owner="some-owner", project="some-project", ), ), JobConfig( type=JobType.tests, trigger=JobConfigTriggerType.pull_request, metadata=JobMetadataConfig(_targets=["fedora-rawhide"]), ), ]) flexmock(AbstractCoprBuildEvent).should_receive( "get_package_config").and_return(config) flexmock(PackageConfigGetter).should_receive( "get_package_config_from_repo").and_return(config) flexmock(CoprBuildEndHandler).should_receive( "was_last_packit_comment_with_congratulation").and_return(False) flexmock(LocalProject).should_receive("refresh_the_arguments").and_return( None) flexmock(CoprBuildModel).should_receive("get_by_build_id").and_return( copr_build_pr) flexmock(CoprBuildModel).should_receive("get_by_id").and_return( copr_build_pr) copr_build_pr.should_call("set_status").with_args("success").once() copr_build_pr.should_receive("set_end_time").once() url = get_copr_build_info_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(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.success, description="RPMs were built successfully.", url=url, check_names=EXPECTED_BUILD_CHECK_NAME, markdown_content=None, ).once() flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.pending, description="RPMs were built successfully.", url=url, check_names=EXPECTED_TESTING_FARM_CHECK_NAME, markdown_content=None, ).once() flexmock(TestingFarmJobHelper).should_receive( "is_fmf_configured").and_return(True) flexmock(TestingFarmJobHelper).should_receive( "send_testing_farm_request").and_return( RequestResponse( status_code=400, ok=False, content=b"some text error", reason="some text error", json=None, )) flexmock(CoprBuildModel).should_receive("set_status").with_args("failure") flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.running, description="Build succeeded. Submitting the tests ...", check_names=EXPECTED_TESTING_FARM_CHECK_NAME, url="", markdown_content=None, ).once() flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.failure, description="Failed to submit tests: some text error", check_names=EXPECTED_TESTING_FARM_CHECK_NAME, url="", markdown_content=None, ).once() flexmock(Signature).should_receive("apply_async").twice() flexmock(Pushgateway).should_receive("push").twice().and_return() # skip SRPM url since it touches multiple classes flexmock(CoprBuildEndHandler).should_receive("set_srpm_url").and_return( None) processing_results = SteveJobs().process_message(copr_build_end) event_dict, job, job_config, package_config = get_parameters_from_results( processing_results) assert json.dumps(event_dict) flexmock(CoprBuildJobHelper).should_receive( "get_built_packages").and_return([]) run_copr_build_end_handler( package_config=package_config, event=event_dict, job_config=job_config, ) flexmock(TestingFarmHandler).should_receive("db_trigger").and_return( copr_build_pr.get_trigger_object()) run_testing_farm_handler( package_config=package_config, event=event_dict, job_config=job_config, chroot="fedora-rawhide-x86_64", build_id=flexmock(), )
def test_copr_build_end_testing_farm(copr_build_end, copr_build_pr): tft_api_url = "https://api.dev.testing-farm.io/v0.1/" service_config = ServiceConfig(testing_farm_api_url=tft_api_url, testing_farm_secret="secret token") flexmock(ServiceConfig).should_receive("get_service_config").and_return( service_config) flexmock(GithubProject).should_receive("is_private").and_return(False) flexmock(GithubProject).should_receive("get_pr").and_return( flexmock( source_project=flexmock( get_web_url=lambda: "https://github.com/foo/bar"), target_branch_head_commit="deadbeef", ).should_receive("comment").mock()) urls.DASHBOARD_URL = "https://dashboard.localhost" config = PackageConfig(jobs=[ JobConfig( type=JobType.copr_build, trigger=JobConfigTriggerType.pull_request, metadata=JobMetadataConfig( _targets=["fedora-rawhide"], owner="some-owner", project="some-project", ), ), JobConfig( type=JobType.tests, trigger=JobConfigTriggerType.pull_request, metadata=JobMetadataConfig(_targets=["fedora-rawhide"]), ), ]) flexmock(AbstractCoprBuildEvent).should_receive( "get_package_config").and_return(config) flexmock(PackageConfigGetter).should_receive( "get_package_config_from_repo").and_return(config) flexmock(CoprBuildEndHandler).should_receive( "was_last_packit_comment_with_congratulation").and_return(False) flexmock(LocalProject).should_receive("refresh_the_arguments").and_return( None) flexmock(CoprBuildModel).should_receive("get_by_build_id").and_return( copr_build_pr) flexmock(CoprBuildModel).should_receive("get_by_id").and_return( copr_build_pr) copr_build_pr.should_call("set_status").with_args("success").once() copr_build_pr.should_receive("set_end_time").once() 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 url = get_copr_build_info_url(1) flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.success, description="RPMs were built successfully.", url=url, check_names=EXPECTED_BUILD_CHECK_NAME, markdown_content=None, ).once() flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.pending, description="RPMs were built successfully.", url=url, check_names=EXPECTED_TESTING_FARM_CHECK_NAME, markdown_content=None, ).once() payload = { "api_key": "secret token", "test": { "fmf": { "url": "https://github.com/foo/bar", "ref": "0011223344", }, }, "environments": [{ "arch": "x86_64", "os": { "compose": "Fedora-Rawhide" }, "tmt": { "context": { "distro": "fedora-rawhide", "arch": "x86_64", "trigger": "commit", } }, "artifacts": [ { "id": "1:fedora-rawhide-x86_64", "type": "fedora-copr-build", "packages": ["bar-0.1-1.noarch"], }, ], "variables": { "PACKIT_FULL_REPO_NAME": "foo/bar", "PACKIT_COMMIT_SHA": "0011223344", "PACKIT_PACKAGE_NVR": "bar-0.1-1", "PACKIT_BUILD_LOG_URL": "https://log-url", "PACKIT_TARGET_SHA": "deadbeef", }, }], "notification": { "webhook": { "url": "https://stg.packit.dev/api/testing-farm/results", "token": "secret token", } }, } flexmock(TestingFarmJobHelper).should_receive( "is_fmf_configured").and_return(True) flexmock(TestingFarmJobHelper).should_receive("distro2compose").with_args( "fedora-rawhide", "x86_64").and_return("Fedora-Rawhide") pipeline_id = "5e8079d8-f181-41cf-af96-28e99774eb68" flexmock(TestingFarmJobHelper).should_receive( "send_testing_farm_request").with_args( endpoint="requests", method="POST", data=payload).and_return( RequestResponse( status_code=200, ok=True, content=json.dumps({ "id": pipeline_id }).encode(), json={"id": pipeline_id}, )) flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.running, description="Build succeeded. Submitting the tests ...", check_names=EXPECTED_TESTING_FARM_CHECK_NAME, url="", markdown_content=None, ).once() flexmock(GithubProject).should_receive("get_web_url").and_return( "https://github.com/foo/bar") tft_test_run_model = flexmock(id=5) flexmock(TFTTestRunModel).should_receive("create").with_args( pipeline_id=pipeline_id, commit_sha="0011223344", status=TestingFarmResult.new, target="fedora-rawhide-x86_64", web_url=None, run_model=copr_build_pr.runs[0], data={ "base_project_url": "https://github.com/foo/bar" }, ).and_return(tft_test_run_model) flexmock(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.running, description="Tests have been submitted ...", url="https://dashboard.localhost/results/testing-farm/5", check_names=EXPECTED_TESTING_FARM_CHECK_NAME, markdown_content=None, ).once() flexmock(Signature).should_receive("apply_async").twice() # skip SRPM url since it touches multiple classes flexmock(CoprBuildEndHandler).should_receive("set_srpm_url").and_return( None) flexmock(Pushgateway).should_receive("push").twice().and_return() processing_results = SteveJobs().process_message(copr_build_end) event_dict, job, job_config, package_config = get_parameters_from_results( processing_results) assert json.dumps(event_dict) flexmock(CoprBuildJobHelper).should_receive( "get_built_packages").and_return([]) run_copr_build_end_handler( package_config=package_config, event=event_dict, job_config=job_config, ) flexmock(TestingFarmHandler).should_receive("db_trigger").and_return( copr_build_pr.get_trigger_object()) flexmock(CoprBuildModel).should_receive("get_all_by").and_return( [copr_build_pr]) run_testing_farm_handler( package_config=package_config, event=event_dict, job_config=job_config, chroot="fedora-rawhide-x86_64", build_id=1, )
def test_copr_build_end( copr_build_end, pc_build_pr, copr_build_pr, pc_comment_pr_succ, pr_comment_called, ): pr = flexmock(source_project=flexmock()) flexmock(GithubProject).should_receive("is_private").and_return(False) flexmock(GithubProject).should_receive("get_pr").and_return(pr) pc_build_pr.jobs[ 0].notifications.pull_request.successful_build = pc_comment_pr_succ flexmock(AbstractCoprBuildEvent).should_receive( "get_package_config").and_return(pc_build_pr) flexmock(CoprBuildEndHandler).should_receive( "was_last_packit_comment_with_congratulation").and_return(False) if pr_comment_called: pr.should_receive("comment") else: pr.should_receive("comment").never() flexmock(CoprBuildModel).should_receive("get_by_build_id").and_return( copr_build_pr) copr_build_pr.should_call("set_status").with_args("success").once() copr_build_pr.should_receive("set_end_time").once() url = get_copr_build_info_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(StatusReporter).should_receive("report").with_args( state=BaseCommitStatus.success, description="RPMs were built successfully.", url=url, check_names=CoprBuildJobHelper.get_build_check( copr_build_end["chroot"]), markdown_content=None, ).once() # no test job defined => testing farm should be skipped flexmock(TestingFarmJobHelper).should_receive("run_testing_farm").times(0) flexmock(Signature).should_receive("apply_async").once() # fix SRPM url since it touches multiple classes (flexmock(CoprBuildJobHelper).should_receive("get_build").with_args( 1044215).and_return( flexmock(source_package={"url": "https://my.host/my.srpm" })).at_least().once()) flexmock(copr_build_pr._srpm_build_for_mocking).should_receive( "set_url").with_args("https://my.host/my.srpm").mock() flexmock(Pushgateway).should_receive("push").once().and_return() processing_results = SteveJobs().process_message(copr_build_end) event_dict, job, job_config, package_config = get_parameters_from_results( processing_results) assert json.dumps(event_dict) flexmock(CoprBuildJobHelper).should_receive( "get_built_packages").and_return([]) run_copr_build_end_handler( package_config=package_config, event=event_dict, job_config=job_config, )
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={})
class CoprBuildJobHelper(BaseBuildJobHelper): job_type_build = JobType.copr_build job_type_test = JobType.tests status_name_build: str = "rpm-build" status_name_test: str = "testing-farm" def __init__( self, service_config: ServiceConfig, package_config: PackageConfig, project: GitProject, metadata: EventData, db_trigger: AbstractTriggerDbType, job_config: JobConfig, targets_override: Optional[Set[str]] = None, pushgateway: Optional[Pushgateway] = None, ): super().__init__( service_config=service_config, package_config=package_config, project=project, metadata=metadata, db_trigger=db_trigger, job_config=job_config, targets_override=targets_override, pushgateway=pushgateway, ) self.msg_retrigger: str = MSG_RETRIGGER.format( job="build", command="copr-build" if self.job_build else "build", place="pull request", ) @property def default_project_name(self) -> str: """ Project name for copr. * use hostname prefix for non-github service * replace slash in namespace with dash * add `-stg` suffix for the stg app """ service_hostname = parse_git_repo( self.project.service.instance_url).hostname service_prefix = ("" if isinstance(self.project, GithubProject) else f"{service_hostname}-") namespace = self.project.namespace.replace("/", "-") stg = "-stg" if self.service_config.deployment == Deployment.stg else "" # We want to share project between all releases. # More details: https://github.com/packit/packit-service/issues/1044 identifier = "releases" if self.metadata.tag_name else self.metadata.identifier return f"{service_prefix}{namespace}-{self.project.repo}-{identifier}{stg}" @property def job_project(self) -> Optional[str]: """ The job definition from the config file. """ if self.job_build and self.job_build.metadata.project: return self.job_build.metadata.project if self.job_tests and self.job_tests.metadata.project: return self.job_tests.metadata.project return self.default_project_name @property def job_owner(self) -> Optional[str]: """ Owner used for the copr build -- search the config or use the copr's config. """ if self.job_build and self.job_build.metadata.owner: return self.job_build.metadata.owner if self.job_tests and self.job_tests.metadata.owner: return self.job_tests.metadata.owner return self.api.copr_helper.copr_client.config.get("username") @property def preserve_project(self) -> Optional[bool]: """ If the project will be preserved or can be removed after 60 days. """ return self.job_build.metadata.preserve_project if self.job_build else None @property def list_on_homepage(self) -> Optional[bool]: """ If the project will be shown on the copr home page. """ return self.job_build.metadata.list_on_homepage if self.job_build else None @property def additional_repos(self) -> Optional[List[str]]: """ Additional repos that will be enable for copr build. """ return self.job_build.metadata.additional_repos if self.job_build else None @property def build_targets_all(self) -> Set[str]: """ Return all valid Copr build targets/chroots from config. """ return get_valid_build_targets(*self.configured_build_targets, default=None) @property def tests_targets_all(self) -> Set[str]: """ Return all valid test targets/chroots from config. """ return get_valid_build_targets(*self.configured_tests_targets, default=None) @property def available_chroots(self) -> Set[str]: """ Returns set of available COPR targets. """ return { *filter( lambda chroot: not chroot.startswith("_"), self.api.copr_helper.get_copr_client().mock_chroot_proxy. get_list().keys(), ) } def get_built_packages(self, build_id: int, chroot: str) -> List: return self.api.copr_helper.copr_client.build_chroot_proxy.get_built_packages( build_id, chroot).packages def get_build(self, build_id: int): return self.api.copr_helper.copr_client.build_proxy.get(build_id) def monitor_not_submitted_copr_builds(self, number_of_builds: int, reason: str): """ Measure the time it took to set the failed status in case of event (e.g. failed SRPM) that prevents Copr build to be submitted. """ time = measure_time(end=datetime.now(timezone.utc), begin=self.metadata.task_accepted_time) for _ in range(number_of_builds): self.pushgateway.copr_build_not_submitted_time.labels( reason=reason).observe(time) def run_copr_build(self) -> TaskResults: self.report_status_to_all( description="Building SRPM ...", state=BaseCommitStatus.running, # pagure requires "valid url" url="", ) if results := self.create_srpm_if_needed(): return results if not self.srpm_model.success: msg = "SRPM build failed, check the logs for details." self.report_status_to_all( state=BaseCommitStatus.failure, description=msg, url=get_srpm_build_info_url(self.srpm_model.id), ) self.monitor_not_submitted_copr_builds(len(self.build_targets), "srpm_failure") return TaskResults(success=False, details={"msg": msg}) try: build_id, web_url = self.run_build() except Exception as ex: sentry_integration.send_to_sentry(ex) # TODO: Where can we show more info about failure? # TODO: Retry self.report_status_to_all( state=BaseCommitStatus.error, description=f"Submit of the build failed: {ex}", ) self.monitor_not_submitted_copr_builds(len(self.build_targets), "submit_failure") return TaskResults( success=False, details={ "msg": "Submit of the Copr build failed.", "error": str(ex) }, ) unprocessed_chroots = [] for chroot in self.build_targets: if chroot not in self.available_chroots: self.report_status_to_all_for_chroot( state=BaseCommitStatus.error, description=f"Not supported target: {chroot}", url=get_srpm_build_info_url(self.srpm_model.id), chroot=chroot, ) self.monitor_not_submitted_copr_builds(1, "not_supported_target") unprocessed_chroots.append(chroot) continue copr_build = CoprBuildModel.create( build_id=str(build_id), commit_sha=self.metadata.commit_sha, project_name=self.job_project, owner=self.job_owner, web_url=web_url, target=chroot, status="pending", run_model=self.run_model, task_accepted_time=self.metadata.task_accepted_time, ) url = get_copr_build_info_url(id_=copr_build.id) self.report_status_to_all_for_chroot( state=BaseCommitStatus.running, description="Starting RPM build...", url=url, chroot=chroot, ) if unprocessed_chroots: unprocessed = "\n".join(sorted(unprocessed_chroots)) available = "\n".join(sorted(self.available_chroots)) self.status_reporter.comment( body="There are build targets that are not supported by COPR.\n" "<details>\n<summary>Unprocessed build targets</summary>\n\n" f"```\n{unprocessed}\n```\n</details>\n" "<details>\n<summary>Available build targets</summary>\n\n" f"```\n{available}\n```\n</details>", ) # release the hounds! celery_app.send_task( "task.babysit_copr_build", args=(build_id, ), countdown=120, # do the first check in 120s ) return TaskResults(success=True, details={})