示例#1
0
class BitBucket:
    def __init__(self, url, username, password):
        self.bitbucket = AtlassianBitBucketLib(
            url=url,
            username=username,
            password=password,
        )

    async def create_pull_request(
        self,
        source_project: str,
        source_repo: str,
        dest_project: str,
        dest_repo: str,
        branch_name: str,
        main_branch_name: str,
        commit_title: str,
        commit_message: str,
    ) -> str:
        pull_request = self.bitbucket.open_pull_request(
            source_project,
            source_repo,
            dest_project,
            dest_repo,
            branch_name,
            main_branch_name,
            commit_title,
            commit_message,
        )
        pull_request_url = pull_request["links"]["self"][0]["href"]
        return pull_request_url
示例#2
0
class BitbucketGitRepoApiAdapter(GitRepoApi):
    def __init__(
        self,
        git_provider_url: str,
        username: Optional[str],
        password: Optional[str],
        organisation: str,
        repository_name: str,
    ) -> None:
        self.__bitbucket = Bitbucket(git_provider_url, username, password)
        self.__git_provider_url = git_provider_url
        self.__organisation = organisation
        self.__repository_name = repository_name

    def get_username(self) -> Optional[str]:
        return str(self.__bitbucket.username)

    def get_password(self) -> Optional[str]:
        return str(self.__bitbucket.password)

    def get_clone_url(self) -> str:
        try:
            repo = self.__bitbucket.get_repo(self.__organisation, self.__repository_name)
        except requests.exceptions.ConnectionError as ex:
            raise GitOpsException(f"Error connecting to '{self.__git_provider_url}''") from ex
        if "errors" in repo:
            for error in repo["errors"]:
                exception = error["exceptionName"]
                if exception == "com.atlassian.bitbucket.auth.IncorrectPasswordAuthenticationException":
                    raise GitOpsException("Bad credentials")
                if exception == "com.atlassian.bitbucket.project.NoSuchProjectException":
                    raise GitOpsException(f"Organisation '{self.__organisation}' does not exist")
                if exception == "com.atlassian.bitbucket.repository.NoSuchRepositoryException":
                    raise GitOpsException(f"Repository '{self.__organisation}/{self.__repository_name}' does not exist")
                raise GitOpsException(error["message"])
        if "links" not in repo:
            raise GitOpsException(f"Repository '{self.__organisation}/{self.__repository_name}' does not exist")
        for clone_link in repo["links"]["clone"]:
            if clone_link["name"] == "http":
                repo_url = clone_link["href"]
        if not repo_url:
            raise GitOpsException("Couldn't determine repository URL.")
        return str(repo_url)

    def create_pull_request_to_default_branch(
        self, from_branch: str, title: str, description: str
    ) -> GitRepoApi.PullRequestIdAndUrl:
        to_branch = self.__get_default_branch()
        return self.create_pull_request(from_branch, to_branch, title, description)

    def create_pull_request(
        self, from_branch: str, to_branch: str, title: str, description: str
    ) -> GitRepoApi.PullRequestIdAndUrl:
        pull_request = self.__bitbucket.open_pull_request(
            self.__organisation,
            self.__repository_name,
            self.__organisation,
            self.__repository_name,
            from_branch,
            to_branch,
            title,
            description,
        )
        if "errors" in pull_request:
            raise GitOpsException(pull_request["errors"][0]["message"])
        return GitRepoApi.PullRequestIdAndUrl(pr_id=pull_request["id"], url=pull_request["links"]["self"][0]["href"])

    def merge_pull_request(self, pr_id: int, merge_method: Literal["squash", "rebase", "merge"] = "merge") -> None:
        pull_request = self.__bitbucket.get_pullrequest(self.__organisation, self.__repository_name, pr_id)
        self.__bitbucket.merge_pull_request(
            self.__organisation, self.__repository_name, pull_request["id"], pull_request["version"]
        )

    def add_pull_request_comment(self, pr_id: int, text: str, parent_id: Optional[int] = None) -> None:
        pull_request_comment = self.__bitbucket.add_pull_request_comment(
            self.__organisation, self.__repository_name, pr_id, text, parent_id
        )
        if "errors" in pull_request_comment:
            raise GitOpsException(pull_request_comment["errors"][0]["message"])

    def delete_branch(self, branch: str) -> None:
        branch_hash = self.get_branch_head_hash(branch)
        result = self.__bitbucket.delete_branch(self.__organisation, self.__repository_name, branch, branch_hash)
        if result and "errors" in result:
            raise GitOpsException(result["errors"][0]["message"])

    def get_branch_head_hash(self, branch: str) -> str:
        branches = self.__bitbucket.get_branches(self.__organisation, self.__repository_name, filter=branch, limit=1)
        if not branches:
            raise GitOpsException(f"Branch '{branch}' not found'")
        return str(branches[0]["latestCommit"])

    def get_pull_request_branch(self, pr_id: int) -> str:
        pull_request = self.__bitbucket.get_pullrequest(self.__organisation, self.__repository_name, pr_id)
        if "errors" in pull_request:
            raise GitOpsException(pull_request["errors"][0]["message"])
        return str(pull_request["fromRef"]["displayId"])

    def __get_default_branch(self) -> str:
        default_branch = self.__bitbucket.get_default_branch(self.__organisation, self.__repository_name)
        return str(default_branch["id"])
示例#3
0
class Server:
    """
    Class that represents the BitBucket server.

    Has methods to perform queries on the server.
    """
    def __init__(self, url: str, user: str, password: str):
        """
        C'tor of Server.

        :param str url: URL of the BitBucket server
        :param str user: Login username
        :param str password: Login password or token
        """
        self.server_url = url
        self.user = user
        self.password = password
        self.api = Bitbucket(self.server_url, self.user, self.password)

    def project_list(self) -> List[str]:
        """
        Get the list of projects from the server.

        :return: List of project names
        """
        query = self.api.project_list()
        return query

    def pr_approved(self, project: str, repo: str, pr: int) -> bool:
        """
        Return True if at least one reviewer approved the pull request, otherwise False.

        :param: project Project ID of the repository
        :param: repo Repository slug of the pull request
        :param: pr Pull request ID
        :returns: True if one reviewer approved the pull request, otherwise False
        """
        query = self.api.get_pull_request(project, repo, pr)
        for reviewer in query["reviewers"]:
            if reviewer["approved"]:
                return True
        return False

    def open_pr_in_repo(
        self,
        project: str,
        repo: str,
        src_branch: str,
        dst_branch: str,
        title: str,
        desc: str,
        reviewers: str = None,
    ):
        """
        Open a new pull request in a repository.

        :param project: Project name
        :param repo: Repository name
        :param src_branch: Source branch name
        :param dst_branch: Destination branch name
        :param title: Title of the pull request
        :param desc: Description text of the pull request
        :param reviewers: UUIDs of reviewers (default None)
        """
        self.open_pr(project, repo, src_branch, project, repo, dst_branch,
                     title, desc, reviewers)

    def open_pr(
        self,
        src_project: str,
        src_repo: str,
        src_branch: str,
        dst_project: str,
        dst_repo: str,
        dst_branch: str,
        title: str,
        desc: str,
        reviewers: str = None,
    ):
        """
        Open a new pull request.

        :param src_project: Source project name
        :param src_repo: Source repository name
        :param src_branch: Source branch name
        :param dst_project: Destination project name
        :param dst_repo: Destination repository name
        :param dst_branch: Destination branch name
        :param title: Title of the pull request
        :param desc: Description text of the pull request
        :param reviewers: UUIDs of reviewers (default None)
        """
        logging.info("Attempting to open a pull request:")
        logging.info(title)
        logging.info(desc)
        if self._confirm("Open pull request " + src_project + "/" + src_repo +
                         "/" + src_branch + "->" + dst_project + "/" +
                         dst_repo + "/" + dst_branch):
            self.api.open_pull_request(
                src_project,
                src_repo,
                dst_project,
                dst_repo,
                src_branch,
                dst_branch,
                title,
                desc,
                reviewers,
            )
            print("Success")
        else:
            print("Action aborted")

    @staticmethod
    def _confirm(question: str) -> bool:
        """Ask for user decision on question."""
        reply = str(input(question + " (y/n): ")).lower().strip()
        return reply[0] == "y"
示例#4
0
class BitBucketGitUtil(AbstractGitUtil):
    def __init__(self, tmp_dir, git_provider_url, organisation,
                 repository_name, username, password, git_user, git_email):
        super().__init__(tmp_dir, username, password, git_user, git_email)
        self._git_provider_url = git_provider_url
        self._organisation = organisation
        self._repository_name = repository_name
        self._bitbucket = Bitbucket(self._git_provider_url, self._username,
                                    self._password)

    def get_clone_url(self):
        try:
            repo = self._bitbucket.get_repo(self._organisation,
                                            self._repository_name)
        except requests.exceptions.ConnectionError as ex:
            raise GitOpsException(
                f"Error connecting to '{self._git_provider_url}''") from ex
        if "errors" in repo:
            for error in repo["errors"]:
                exception = error["exceptionName"]
                if exception == "com.atlassian.bitbucket.auth.IncorrectPasswordAuthenticationException":
                    raise GitOpsException("Bad credentials")
                if exception == "com.atlassian.bitbucket.project.NoSuchProjectException":
                    raise GitOpsException(
                        f"Organisation '{self._organisation}' does not exist")
                if exception == "com.atlassian.bitbucket.repository.NoSuchRepositoryException":
                    raise GitOpsException(
                        f"Repository '{self._organisation}/{self._repository_name}' does not exist"
                    )
                raise GitOpsException(error["message"])
        if "links" not in repo:
            raise GitOpsException(
                f"Repository '{self._organisation}/{self._repository_name}' does not exist"
            )
        for clone_link in repo["links"]["clone"]:
            if clone_link["name"] == "http":
                repo_url = clone_link["href"]
        if not repo_url:
            raise GitOpsException("Couldn't determine repository URL.")
        return repo_url

    def create_pull_request(self, from_branch, to_branch, title, description):
        pull_request = self._bitbucket.open_pull_request(
            self._organisation,
            self._repository_name,
            self._organisation,
            self._repository_name,
            from_branch,
            to_branch,
            title,
            description,
        )
        if "errors" in pull_request:
            raise GitOpsException(pull_request["errors"][0]["message"])
        return pull_request

    def get_pull_request_url(self, pull_request):
        return pull_request["links"]["self"][0]["href"]

    def merge_pull_request(self, pull_request):
        self._bitbucket.merge_pull_request(self._organisation,
                                           self._repository_name,
                                           pull_request["id"],
                                           pull_request["version"])

    def add_pull_request_comment(self, pr_id, text, parent_id):
        pull_request_comment = self._bitbucket.add_pull_request_comment(
            self._organisation, self._repository_name, pr_id, text, parent_id)
        if "errors" in pull_request_comment:
            raise GitOpsException(pull_request_comment["errors"][0]["message"])
        return pull_request_comment

    def delete_branch(self, branch):
        branches = self._bitbucket.get_branches(self._organisation,
                                                self._repository_name,
                                                filter=branch,
                                                limit=1)
        if not branches:
            raise GitOpsException(f"Branch '{branch}' not found'")
        result = self._bitbucket.delete_branch(self._organisation,
                                               self._repository_name, branch,
                                               branches[0]["latestCommit"])
        if result and "errors" in result:
            raise GitOpsException(result["errors"][0]["message"])

    def get_pull_request_branch(self, pr_id):
        pull_request = self._bitbucket.get_pullrequest(self._organisation,
                                                       self._repository_name,
                                                       pr_id)
        if "errors" in pull_request:
            raise GitOpsException(pull_request["errors"][0]["message"])
        return pull_request["fromRef"]["displayId"]