예제 #1
0
파일: test_api.py 프로젝트: xsuchy/packit
class ProposeUpdate(PackitUnittestOgr):
    def setUp(self):
        super().setUp()
        self.api = PackitAPI(
            config=self.conf, package_config=self.pc, upstream_local_project=self.lp
        )
        self.api._up = self.upstream
        self.api._dg = self.dg
        # Do not upload package, because no credentials given in CI
        flexmock(self.api).should_receive("_handle_sources").and_return(None)
        self.set_git_user()

    @unittest.skip(
        "Issue in ogr causing that User is not stored in persistent yaml files for pagure"
    )
    def test_propose_update(self):
        # change specfile little bit to have there some change
        specfile_location = os.path.join(self.lp.working_dir, "python-ogr.spec")
        with open(specfile_location, "a") as myfile:
            myfile.write("# test text")
        check_output(
            f"cd {self.lp.working_dir}; git commit -m 'test change' python-ogr.spec",
            shell=True,
        )
        self.api.sync_release("master")
예제 #2
0
def test_basic_local_update_direct_push(upstream_distgit_remote,
                                        mock_remote_functionality_upstream):
    """ basic propose-update test: mock remote API, use local upstream and dist-git """
    upstream, distgit, remote_dir = upstream_distgit_remote

    with cwd(upstream):
        c = get_test_config()

        pc = get_local_package_config(str(upstream))
        pc.upstream_project_url = str(upstream)
        pc.dist_git_clone_path = str(distgit)
        up_lp = LocalProject(working_dir=str(upstream))
        api = PackitAPI(c, pc, up_lp)
        api.sync_release("master", "0.1.0", create_pr=False)

        remote_dir_clone = Path(f"{remote_dir}-clone")
        subprocess.check_call(
            ["git", "clone", remote_dir,
             str(remote_dir_clone)],
            cwd=str(remote_dir_clone.parent),
        )

        spec = get_specfile(str(remote_dir_clone / "beer.spec"))
        assert spec.get_version() == "0.1.0"
        assert (remote_dir_clone / "README.packit").is_file()
예제 #3
0
def test_update_on_cockpit_ostree(cockpit_ostree):
    def mocked_new_sources(sources=None):
        if not Path(sources).is_file():
            raise RuntimeError("archive does not exist")

    flexmock(FedPKG,
             init_ticket=lambda x=None: None,
             new_sources=mocked_new_sources)

    flexmock(
        DistGit,
        push_to_fork=lambda *args, **kwargs: None,
        is_archive_in_lookaside_cache=lambda archive_path: False,
    )
    flexmock(
        PackitAPI,
        push_and_create_pr=lambda pr_title, pr_description, dist_git_branch:
        None,
    )

    pc = get_local_package_config(str(cockpit_ostree))
    up_lp = LocalProject(working_dir=str(cockpit_ostree))
    c = get_test_config()

    api = PackitAPI(c, pc, up_lp)
    with cwd(cockpit_ostree):
        api.sync_release(
            "master",
            use_local_content=False,
            version="179",
            force_new_sources=False,
            create_pr=True,
        )

    assert api.dg.download_upstream_archive().is_file()
class ProposeDownstreamHandler(JobHandler):
    type = JobType.propose_downstream
    triggers = [TheJobTriggerType.release]
    task_name = TaskName.propose_downstream

    def run(self) -> TaskResults:
        """
        Sync the upstream release to dist-git as a pull request.
        """

        self.local_project = LocalProject(
            git_project=self.project,
            working_dir=self.service_config.command_handler_work_dir,
        )

        self.api = PackitAPI(self.service_config, self.job_config,
                             self.local_project)

        errors = {}
        for branch in get_branches(*self.job_config.metadata.dist_git_branches,
                                   default="master"):
            try:
                self.api.sync_release(dist_git_branch=branch,
                                      version=self.data.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.data.tag_name}",
                body=body_msg,
            )

            return TaskResults(
                success=False,
                details={
                    "msg": "Propose update failed.",
                    "errors": errors
                },
            )

        return TaskResults(success=True, details={})
예제 #5
0
def update(config, dist_git_path, upstream_git_path, dist_git_branch, repo):
    """
    Release current upstream release into Fedora
    """
    package_config = get_local_package_config(directory=repo.working_dir)
    package_config.downstream_project_url = dist_git_path
    package_config.upstream_project_url = upstream_git_path
    api = PackitAPI(config=config, package_config=package_config)
    api.sync_release(dist_git_branch)
예제 #6
0
    def run(self):
        """
        Sync the upstream release to dist-git as a pull request.
        """
        version = self.event["release"]["tag_name"]

        local_project = LocalProject(git_project=self.project)

        api = PackitAPI(self.config, self.package_config, local_project)

        api.sync_release(
            dist_git_branch=self.job.metadata.get("dist-git-branch", "master"),
            version=version,
        )
예제 #7
0
class GithubReleaseHandler(AbstractGithubJobHandler):
    name = JobType.propose_downstream
    triggers = [JobTriggerType.release]
    event: ReleaseEvent

    def __init__(self, config: ServiceConfig, job: JobConfig,
                 release_event: ReleaseEvent):
        super().__init__(config=config, job=job, event=release_event)

        self.project: GitProject = release_event.get_project()
        self.package_config: PackageConfig = get_package_config_from_repo(
            self.project, release_event.tag_name)
        if not self.package_config:
            raise ValueError(
                f"No config file found in {self.project.full_repo_name}")
        self.package_config.upstream_project_url = release_event.project_url

    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={})
예제 #8
0
def test_basic_local_update(upstream_n_distgit, mock_upstream_remote_functionality):
    """ basic propose-update test: mock remote API, use local upstream and dist-git """
    u, d = upstream_n_distgit

    chdir(u)
    c = get_test_config()

    pc = get_local_package_config(str(u))
    pc.upstream_project_url = str(u)
    pc.downstream_project_url = str(d)
    api = PackitAPI(c, pc)
    api.sync_release("master", "0.1.0")

    assert (d / TARBALL_NAME).is_file()
    spec = SpecFile(str(d / "beer.spec"), None)
    assert spec.get_version() == "0.1.0"
예제 #9
0
파일: bot_api.py 프로젝트: eliskasl/packit
    def sync_upstream_release(
        self,
        package_config: PackageConfig,
        version: Optional[str],
        dist_git_branch: str,
    ):
        """
        Sync the upstream release to the distgit pull-request.

        :param package_config: PackageConfig
        :param version: not used now, str
        :param dist_git_branch: str
        """
        logger.info("syncing the upstream code to downstream")
        packit_api = PackitAPI(config=self.config, package_config=package_config)
        packit_api.sync_release(dist_git_branch=dist_git_branch, version=version)
예제 #10
0
def test_update_on_cockpit_ostree_pr_exists(cockpit_ostree):
    upstream_path, dist_git_path = cockpit_ostree

    def mocked_new_sources(sources=None):
        if not Path(sources).is_file():
            raise RuntimeError("archive does not exist")

    flexmock(PkgTool, new_sources=mocked_new_sources)
    flexmock(PackitAPI, init_kerberos_ticket=lambda: None)

    flexmock(
        DistGit,
        push_to_fork=lambda *args, **kwargs: None,
        is_archive_in_lookaside_cache=lambda archive_path: False,
        upload_to_lookaside_cache=lambda archive, pkg_tool: None,
        download_upstream_archive=lambda: "the-archive",
    )
    pr = flexmock(url="https://example.com/pull/1")
    flexmock(DistGit).should_receive("existing_pr").and_return(pr)

    pc = get_local_package_config(str(upstream_path))
    up_lp = LocalProject(working_dir=upstream_path)
    c = get_test_config()
    api = PackitAPI(c, pc, up_lp)
    api._dg = DistGit(c, pc)
    api._dg._local_project = LocalProject(working_dir=dist_git_path)

    with cwd(upstream_path):
        assert pr == api.sync_release(
            dist_git_branch="main",
            use_local_content=False,
            version="179",
            force_new_sources=False,
            create_pr=True,
        )
예제 #11
0
def test_basic_local_update(upstream_n_distgit,
                            mock_remote_functionality_upstream):
    """ basic propose-update test: mock remote API, use local upstream and dist-git """
    u, d = upstream_n_distgit

    with cwd(u):
        c = get_test_config()

        pc = get_local_package_config(str(u))
        pc.upstream_project_url = str(u)
        pc.downstream_project_url = str(d)
        up_lp = LocalProject(path_or_url=str(u))
        api = PackitAPI(c, pc, up_lp)
        api.sync_release("master", "0.1.0")

        assert (d / TARBALL_NAME).is_file()
        spec = get_specfile(str(d / "beer.spec"))
        assert spec.get_version() == "0.1.0"
예제 #12
0
    def run(self) -> TaskResults:
        local_project = LocalProject(
            git_project=self.project,
            working_dir=self.service_config.command_handler_work_dir,
        )

        api = PackitAPI(
            config=self.service_config,
            # job_config and package_config are the same for PackitAPI
            # and we want to use job_config since people can override things in there
            package_config=self.job_config,
            upstream_local_project=local_project,
        )

        user_can_merge_pr = self.project.can_merge_pr(self.data.user_login)
        if not (user_can_merge_pr
                or self.data.user_login in self.service_config.admins):
            self.project.issue_comment(self.db_trigger.issue_id,
                                       PERMISSIONS_ERROR_WRITE_OR_ADMIN)
            return TaskResults(
                success=True,
                details={"msg": PERMISSIONS_ERROR_WRITE_OR_ADMIN})

        if not self.data.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.db_trigger.issue_id, msg)
            return TaskResults(success=False,
                               details={"msg": "Propose update failed"})

        sync_failed = False
        for branch in self.dist_git_branches_to_sync:
            msg = (
                f"for the Fedora package `{self.job_config.downstream_package_name}`"
                f"with the tag `{self.data.tag_name}` in the `{branch}` branch.\n"
            )
            try:
                new_pr = api.sync_release(dist_git_branch=branch,
                                          version=self.data.tag_name,
                                          create_pr=True)
                msg = f"Packit-as-a-Service proposed [a new update]({new_pr.url}) {msg}"
                self.project.issue_comment(self.db_trigger.issue_id, msg)
            except PackitException as ex:
                msg = f"There was an error while proposing a new update {msg} Traceback is: `{ex}`"
                self.project.issue_comment(self.db_trigger.issue_id, msg)
                logger.error(f"Error while running a build: {ex}")
                sync_failed = True
        if sync_failed:
            return TaskResults(success=False,
                               details={"msg": "Propose update failed"})

        # Close issue if propose-update was successful in all branches
        self.project.issue_close(self.db_trigger.issue_id)

        return TaskResults(success=True, details={})
예제 #13
0
class GithubReleaseHandler(JobHandler):
    name = JobType.propose_downstream
    triggers = [JobTriggerType.release]

    def run(self) -> HandlerResults:
        """
        Sync the upstream release to dist-git as a pull request.
        """
        version = self.event["release"]["tag_name"]

        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_release(
            dist_git_branch=self.job.metadata.get("dist-git-branch", "master"),
            version=version,
        )
        return HandlerResults(success=True, details={})
예제 #14
0
파일: test_api.py 프로젝트: Hojang2/packit
class ProposeUpdate(PackitUnittestOgr):
    def setUp(self):
        super().setUp()
        self.api = PackitAPI(config=self.conf,
                             package_config=self.pc,
                             upstream_local_project=self.lp)
        self.api._up = self.upstream
        self.api._dg = self.dg
        # Do not upload package, because no credentials given in CI
        flexmock(self.api).should_receive("_handle_sources").and_return(None)
        flexmock(self.api.dg).should_receive("push").and_return(None)
        flexmock(git.HEAD).should_receive("commit").and_return(
            "hash-of-some-commit")
        self.set_git_user()

    def test_propose_update(self):
        # change specfile little bit to have there some change
        specfile_location = os.path.join(self.lp.working_dir,
                                         "python-ogr.spec")
        with open(specfile_location, "r") as myfile:
            filedata = myfile.read()
        # Patch the specfile with new version
        version_increase = "0.0.0"
        for line in filedata.splitlines():
            if "Version:" in line:
                version = line.rsplit(" ", 1)[1]
                v1, v2, v3 = version.split(".")
                version_increase = ".".join([v1, str(int(v2) + 1), v3])
                filedata = filedata.replace(version, version_increase)
                break
        with open(specfile_location, "w") as myfile:
            myfile.write(filedata)
        check_output(
            f"cd {self.lp.working_dir};"
            f"git commit -m 'test change' python-ogr.spec;"
            f"git tag -a {version_increase} -m 'my version {version_increase}'",
            shell=True,
        )
        self.api.sync_release("master")
예제 #15
0
def test_update_on_cockpit_ostree(cockpit_ostree):
    upstream_path, dist_git_path = cockpit_ostree

    def mocked_new_sources(sources=None):
        if not Path(sources).is_file():
            raise RuntimeError("archive does not exist")

    flexmock(FedPKG, new_sources=mocked_new_sources)
    flexmock(PackitAPI, init_kerberos_ticket=lambda: None)

    flexmock(
        DistGit,
        push_to_fork=lambda *args, **kwargs: None,
        is_archive_in_lookaside_cache=lambda archive_path: False,
        upload_to_lookaside_cache=lambda path: None,
        download_upstream_archive=lambda: "the-archive",
    )
    flexmock(
        PackitAPI,
        push_and_create_pr=lambda pr_title, pr_description, dist_git_branch:
        None,
    )

    pc = get_local_package_config(str(upstream_path))
    up_lp = LocalProject(working_dir=upstream_path)
    c = get_test_config()
    api = PackitAPI(c, pc, up_lp)
    api._dg = DistGit(c, pc)
    api._dg._local_project = LocalProject(working_dir=dist_git_path)

    with cwd(upstream_path):
        api.sync_release(
            dist_git_branch="main",
            use_local_content=False,
            version="179",
            force_new_sources=False,
            create_pr=True,
        )
예제 #16
0
class GithubReleaseHandler(AbstractGithubJobHandler):
    name = JobType.propose_downstream
    triggers = [JobTriggerType.release]
    event: ReleaseEvent

    def __init__(
        self, config: ServiceConfig, job: JobConfig, release_event: ReleaseEvent
    ):
        super().__init__(config=config, job=job, event=release_event)

        self.project: GitProject = release_event.get_project()
        self.package_config: PackageConfig = get_package_config_from_repo(
            self.project, release_event.tag_name
        )
        if not self.package_config:
            raise ValueError(f"No config file found in {self.project.full_repo_name}")
        self.package_config.upstream_project_url = release_event.project_url

    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)
        # create_pr is set to False.
        # Each upstream project decides
        # if creates PR or pushes directly into dist-git directly from packit.yaml file.
        self.api.sync_release(
            dist_git_branch=self.job.metadata.get("dist-git-branch", "master"),
            version=self.event.tag_name,
            create_pr=False,
        )

        return HandlerResults(success=True, details={})
예제 #17
0
def test_basic_local_update(upstream_n_distgit,
                            mock_remote_functionality_upstream):
    """ basic propose-update test: mock remote API, use local upstream and dist-git """
    u, d = upstream_n_distgit

    with cwd(u):
        c = get_test_config()

        pc = get_local_package_config(str(u))
        pc.upstream_project_url = str(u)
        pc.dist_git_clone_path = str(d)
        up_lp = LocalProject(working_dir=str(u))
        api = PackitAPI(c, pc, up_lp)
        api.sync_release("master", "0.1.0")

        assert (d / TARBALL_NAME).is_file()
        spec = get_specfile(str(d / "beer.spec"))
        assert spec.get_version() == "0.1.0"
        assert (d / "README.packit").is_file()
        # assert that we have changelog entries for both versions
        changelog = "\n".join(spec.spec_content.section("%changelog"))
        assert "0.0.0" in changelog
        assert "0.1.0" in changelog
예제 #18
0
class ProposeDownstreamHandler(JobHandler):
    type = JobType.propose_downstream
    triggers = [TheJobTriggerType.release]
    task_name = TaskName.propose_downstream

    def __init__(
        self,
        package_config: PackageConfig,
        job_config: JobConfig,
        data: EventData,
        task: Task,
    ):
        super().__init__(
            package_config=package_config,
            job_config=job_config,
            data=data,
        )
        self.task = task

    def run(self) -> TaskResults:
        """
        Sync the upstream release to dist-git as a pull request.
        """

        self.local_project = LocalProject(
            git_project=self.project,
            working_dir=self.service_config.command_handler_work_dir,
        )

        self.api = PackitAPI(self.service_config, self.job_config,
                             self.local_project)

        errors = {}
        for branch in get_branches(*self.job_config.metadata.dist_git_branches,
                                   default="master"):
            try:
                self.api.sync_release(dist_git_branch=branch,
                                      version=self.data.tag_name)
            except Exception as ex:
                # the archive has not been uploaded to PyPI yet
                if FILE_DOWNLOAD_FAILURE in str(ex):
                    # retry for the archive to become available
                    logger.info(
                        f"We were not able to download the archive: {ex}")
                    # when the task hits max_retries, it raises MaxRetriesExceededError
                    # and the error handling code would be never executed
                    retries = self.task.request.retries
                    if retries < RETRY_LIMIT:
                        logger.info(f"Retrying for the {retries + 1}. time...")
                        self.task.retry(exc=ex, countdown=15 * 2**retries)
                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.data.tag_name}",
                body=body_msg,
            )

            return TaskResults(
                success=False,
                details={
                    "msg": "Propose update failed.",
                    "errors": errors
                },
            )

        return TaskResults(success=True, details={})
예제 #19
0
class GitHubIssueCommentProposeUpdateHandler(CommentActionHandler):
    """ Handler for issue comment `/packit propose-update` """

    name = CommentAction.propose_update
    event: IssueCommentEvent

    def __init__(self, config: ServiceConfig, event: IssueCommentEvent):
        super().__init__(config=config, event=event)

        self.config = config
        self.event = event
        self.project = self.event.get_project()
        # Get the latest tag release
        self.event.tag_name = self.project.get_latest_release().tag_name
        self.package_config: PackageConfig = get_package_config_from_repo(
            self.project, self.event.tag_name)
        if not self.package_config:
            raise ValueError(
                f"No config file found in {self.project.full_repo_name}")
        self.package_config.upstream_project_url = event.project_url

    @property
    def dist_git_branches_to_sync(self) -> List[str]:
        """
        Get the dist-git branches to sync to with the aliases expansion.

        :return: list of dist-git branches
        """
        configured_branches = [
            job.metadata.get("dist-git-branch")
            for job in self.package_config.jobs
            if job.job == JobType.propose_downstream
        ]
        if configured_branches:
            return list(get_branches(*configured_branches))
        return []

    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})

        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={})

        # Close issue if propose-update was successful in all branches
        self.project.issue_close(self.event.issue_id)

        return HandlerResults(success=True, details={})
예제 #20
0
class GithubReleaseHandler(AbstractGithubJobHandler):
    name = JobType.propose_downstream
    triggers = [JobTriggerType.release]
    event: ReleaseEvent

    def __init__(self, config: ServiceConfig, job: JobConfig,
                 release_event: ReleaseEvent):
        super().__init__(config=config, job=job, event=release_event)

        self.project: GitProject = release_event.get_project()
        self.package_config: PackageConfig = self.get_package_config_from_repo(
            self.project, release_event.tag_name)
        self.package_config.upstream_project_url = release_event.project_url

    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={})
예제 #21
0
class ProposeUpdate(PackitUnittestOgr):
    @classmethod
    def _feature_id(cls):
        ogr_module = importlib.import_module("ogr")
        if "parent" in inspect.getsource(
                ogr_module.services.pagure.PagureProject.is_fork.fget):
            cls.variant = "ogr_old_fork"

    def setUp(self):
        self._feature_id()
        super().setUp()
        self.api = PackitAPI(config=self.conf,
                             package_config=self.pc,
                             upstream_local_project=self.lp)
        self.api._up = self.upstream
        self.api._dg = self.dg
        self.set_git_user()

    def check_version_increase(self):
        # change specfile little bit to have there some change
        specfile_location = os.path.join(self.lp.working_dir,
                                         "python-ogr.spec")
        with open(specfile_location, "r") as myfile:
            filedata = myfile.read()
        # Patch the specfile with new version
        version_increase = "0.0.0"
        for line in filedata.splitlines():
            if "Version:" in line:
                version = line.rsplit(" ", 1)[1]
                v1, v2, v3 = version.split(".")
                version_increase = ".".join([v1, str(int(v2) + 1), v3])
                filedata = filedata.replace(version, version_increase)
                break
        with open(specfile_location, "w") as myfile:
            myfile.write(filedata)
        check_output(
            f"cd {self.lp.working_dir};"
            f"git commit -m 'test change' python-ogr.spec;"
            f"git tag -a {version_increase} -m 'my version {version_increase}'",
            shell=True,
        )
        self.api.sync_release("master")

    @unittest.skip(
        reason="https://github.com/packit-service/packit/issues/562 and #561")
    def test_comment_in_spec(self):
        """
        change specfile little bit to have there some change, do not increase version
        """
        specfile_location = os.path.join(self.lp.working_dir,
                                         "python-ogr.spec")
        version_increase = "10.0.0"
        with open(specfile_location, "a") as myfile:
            myfile.write("\n# comment\n")
        check_output(
            f"cd {self.lp.working_dir};"
            f"git commit -m 'test change' python-ogr.spec;"
            f"git tag -a {version_increase} -m 'my version {version_increase}'",
            shell=True,
        )
        self.api.sync_release("master")

    # @unittest.skipIf(
    #     hasattr(rebasehelper, "VERSION")
    #     and int(rebasehelper.VERSION.split(".")[1]) >= 19,
    #     "Older version of rebasehelper raised exception",
    # )
    @unittest.skip(
        reason="https://github.com/packit-service/packit/issues/562 and #561")
    def test_version_change_exception(self):
        """
        check if it raises exception, because sources are not uploaded in distgit
        Downgrade rebasehelper to version < 0.19.0
        """
        self.assertRaises(RebaseHelperError, self.check_version_increase)

    # @unittest.skipUnless(
    #     hasattr(rebasehelper, "VERSION")
    #     and int(rebasehelper.VERSION.split(".")[1]) >= 19,
    #     "New version of rebasehelper works without raised exception",
    # )
    @unittest.skip(reason="https://github.com/packit-service/packit/issues/558"
                   )
    def test_version_change_new_rebaseheler(self):
        """
        check if it not raises exception, because sources are not uploaded in distgit
        """
        self.check_version_increase()

    @unittest.skip(
        reason="https://github.com/packit-service/packit/issues/562 and #561")
    def test_version_change_mocked(self):
        """
        version is not not uploaded, so skip in this test
        """
        flexmock(self.api).should_receive("_handle_sources").and_return(None)
        self.check_version_increase()
예제 #22
0
class ProposeUpdate(PackitUnittestOgr):
    def setUp(self):
        if (hasattr(rebasehelper, "VERSION")
                and int(rebasehelper.VERSION.split(".")[1]) >= 19):
            DataMiner.key = "rebase-helper>=0.19"
        else:
            DataMiner.key = "rebase-helper<0.19"
        DataMiner.data_type = DataTypes.Dict

        super().setUp()
        self.api = PackitAPI(config=self.conf,
                             package_config=self.pc,
                             upstream_local_project=self.lp)
        self.api._up = self.upstream
        self.api._dg = self.dg
        self.set_git_user()

    def check_version_increase(self):
        # change specfile little bit to have there some change
        specfile_location = os.path.join(self.lp.working_dir,
                                         "python-ogr.spec")
        with open(specfile_location, "r") as myfile:
            filedata = myfile.read()
        # Patch the specfile with new version
        version_increase = "0.0.0"
        for line in filedata.splitlines():
            if "Version:" in line:
                version = line.rsplit(" ", 1)[1]
                v1, v2, v3 = version.split(".")
                version_increase = ".".join([v1, str(int(v2) + 1), v3])
                filedata = filedata.replace(version, version_increase)
                break
        with open(specfile_location, "w") as myfile:
            myfile.write(filedata)
        check_output(
            f"cd {self.lp.working_dir};"
            f"git commit -m 'test change' python-ogr.spec;"
            f"git tag -a {version_increase} -m 'my version {version_increase}'",
            shell=True,
        )
        self.api.sync_release("master")

    def test_comment_in_spec(self):
        """
        change specfile little bit to have there some change, do not increase version
        """
        specfile_location = os.path.join(self.lp.working_dir,
                                         "python-ogr.spec")
        version_increase = "10.0.0"
        with open(specfile_location, "a") as myfile:
            myfile.write("\n# comment\n")
        check_output(
            f"cd {self.lp.working_dir};"
            f"git commit -m 'test change' python-ogr.spec;"
            f"git tag -a {version_increase} -m 'my version {version_increase}'",
            shell=True,
        )
        self.api.sync_release("master")

    @unittest.skipIf(
        hasattr(rebasehelper, "VERSION")
        and int(rebasehelper.VERSION.split(".")[1]) >= 19,
        "Older version of rebasehelper raised exception",
    )
    def test_version_change_exception(self):
        """
        check if it raises exception, because sources are not uploaded in distgit
        Downgrade rebasehelper to version < 0.19.0
        """
        self.assertRaises(RebaseHelperError, self.check_version_increase)

    @unittest.skipUnless(
        hasattr(rebasehelper, "VERSION")
        and int(rebasehelper.VERSION.split(".")[1]) >= 19,
        "New version of rebasehelper works without raised exception",
    )
    def test_version_change_new_rebaseheler(self):
        """
        check if it not raises exception, because sources are not uploaded in distgit
        """
        self.check_version_increase()

    def test_version_change_mocked(self):
        """
        version is not not uploaded, so skip in this test
        """
        flexmock(self.api).should_receive("_handle_sources").and_return(None)
        self.check_version_increase()
예제 #23
0
class GitHubIssueCommentProposeUpdateHandler(CommentActionHandler,
                                             GithubPackageConfigGetter):
    """ Handler for issue comment `/packit propose-update` """

    type = CommentAction.propose_update
    triggers = [TheJobTriggerType.issue_comment]
    event: IssueCommentEvent

    def __init__(self, config: ServiceConfig, event: IssueCommentEvent):
        super().__init__(config=config, event=event)

        self.config = config
        self.event = event
        self.project = self.event.get_project()
        self.package_config: PackageConfig = self.get_package_config_from_repo(
            self.project, self.event.tag_name)
        if not self.package_config:
            raise ValueError(
                f"No config file found in {self.project.full_repo_name}")
        self.package_config.upstream_project_url = event.project_url

    @property
    def dist_git_branches_to_sync(self) -> Set[str]:
        """
        Get the dist-git branches to sync to with the aliases expansion.

        :return: list of dist-git branches
        """
        configured_branches = set()
        for job in self.package_config.jobs:
            if job.type == JobType.propose_downstream:
                configured_branches.update(job.metadata.dist_git_branches)

        if configured_branches:
            return get_branches(*configured_branches)
        return set()

    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:
            self.project.issue_comment(self.event.issue_id,
                                       PERMISSIONS_ERROR_WRITE_OR_ADMIN)
            return HandlerResults(
                success=True,
                details={"msg": PERMISSIONS_ERROR_WRITE_OR_ADMIN})

        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"for the Fedora package `{self.package_config.downstream_package_name}`"
                f"with the tag `{self.event.tag_name}` in the `{branch}` branch.\n"
            )
            try:
                new_pr = self.api.sync_release(dist_git_branch=branch,
                                               version=self.event.tag_name,
                                               create_pr=True)
                msg = f"Packit-as-a-Service proposed [a new update]({new_pr.url}) {msg}"
                self.project.issue_comment(self.event.issue_id, msg)
            except PackitException as ex:
                msg = f"There was an error while proposing a new update {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={})
예제 #24
0
class ProposeDownstreamHandler(JobHandler):
    task_name = TaskName.propose_downstream

    def __init__(
        self,
        package_config: PackageConfig,
        job_config: JobConfig,
        data: EventData,
        task: Task,
    ):
        super().__init__(
            package_config=package_config,
            job_config=job_config,
            data=data,
        )
        self.task = task

    def run(self) -> TaskResults:
        """
        Sync the upstream release to dist-git as a pull request.
        """

        self.local_project = LocalProject(
            git_project=self.project,
            working_dir=self.service_config.command_handler_work_dir,
        )

        self.api = PackitAPI(self.service_config, self.job_config,
                             self.local_project)

        errors = {}
        default_dg_branch = self.api.dg.local_project.git_project.default_branch
        for branch in get_branches(*self.job_config.metadata.dist_git_branches,
                                   default=default_dg_branch):
            try:
                self.api.sync_release(dist_git_branch=branch,
                                      tag=self.data.tag_name)
            except Exception as ex:
                # the archive has not been uploaded to PyPI yet
                if FILE_DOWNLOAD_FAILURE in str(ex):
                    # retry for the archive to become available
                    logger.info(
                        f"We were not able to download the archive: {ex}")
                    # when the task hits max_retries, it raises MaxRetriesExceededError
                    # and the error handling code would be never executed
                    retries = self.task.request.retries
                    if retries < int(
                            getenv("CELERY_RETRY_LIMIT", DEFAULT_RETRY_LIMIT)):
                        logger.info(f"Retrying for the {retries + 1}. time...")
                        # throw=False so that exception is not raised and task
                        # is not retried also automatically
                        self.task.retry(exc=ex,
                                        countdown=15 * 2**retries,
                                        throw=False)
                        return TaskResults(
                            success=False,
                            details={
                                "msg":
                                "Task was retried, we were not able to download the archive."
                            },
                        )
                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"

            msg_retrigger = MSG_RETRIGGER.format(job="update",
                                                 command="propose-downstream",
                                                 place="issue")
            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"
                f"{msg_retrigger}\n")

            self.project.create_issue(
                title=
                f"[packit] Propose downstream failed for release {self.data.tag_name}",
                body=body_msg,
            )

            return TaskResults(
                success=False,
                details={
                    "msg": "Propose downstream failed.",
                    "errors": errors
                },
            )

        return TaskResults(success=True, details={})
예제 #25
0
class GitHubIssueCommentProposeUpdateHandler(CommentActionHandler):
    """ Issue handler for comment `/packit propose-update` """

    name = CommentAction.propose_update
    event: IssueCommentEvent

    def __init__(self, config: Config, event: IssueCommentEvent):
        super().__init__(config=config, event=event)

        self.config = config
        self.event = event
        self.project: GitProject = self.github_service.get_project(
            repo=event.base_repo_name, namespace=event.base_repo_namespace)
        # Get the latest tag release
        self.event.tag_name = self.project.get_latest_release().tag_name
        self.package_config: PackageConfig = get_package_config_from_repo(
            self.project, self.event.tag_name)
        self.package_config.upstream_project_url = event.https_url

    def get_build_metadata_for_build(self) -> List[str]:
        """
        Check if there are propose-update defined
        :return: JobConfig or Empty list
        """
        return [
            job.metadata.get("dist-git-branch")
            for job in self.package_config.jobs
            if job.job == JobType.propose_downstream
        ]

    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:
            msg = "Only collaborators can trigger Packit-as-a-Service"
            self.project.issue_comment(self.event.issue_id, msg)
            return HandlerResults(success=False, details={"msg": msg})

        branches = self.get_build_metadata_for_build()
        sync_failed = False
        for brn in branches:
            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 `{brn}` branch.\n"
            )
            try:
                self.api.sync_release(dist_git_branch=brn,
                                      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={})

        # Close issue if propose-update was successful in all branches
        self.project.issue_close(self.event.issue_id)

        return HandlerResults(success=True, details={})