diff --git a/src/semantic_release/hvcs/remote_hvcs_base.py b/src/semantic_release/hvcs/remote_hvcs_base.py index e7cd93ab3..d26b01881 100644 --- a/src/semantic_release/hvcs/remote_hvcs_base.py +++ b/src/semantic_release/hvcs/remote_hvcs_base.py @@ -95,10 +95,15 @@ def create_server_url( query: str | None = None, fragment: str | None = None, ) -> str: - # Ensure any path prefix is transfered but not doubled up on the derived url + # Ensure any path prefix is transferred but not doubled up on the derived url + normalized_path = ( + f"{self.hvcs_domain.path}/{path}" + if self.hvcs_domain.path and not path.startswith(self.hvcs_domain.path) + else path + ) return self._derive_url( self.hvcs_domain, - path=f"{self.hvcs_domain.path or ''}/{path.lstrip(self.hvcs_domain.path)}", + path=normalized_path, auth=auth, query=query, fragment=fragment, @@ -123,10 +128,15 @@ def create_api_url( query: str | None = None, fragment: str | None = None, ) -> str: - # Ensure any api path prefix is transfered but not doubled up on the derived api url + # Ensure any api path prefix is transferred but not doubled up on the derived api url + normalized_endpoint = ( + f"{self.api_url.path}/{endpoint}" + if self.api_url.path and not endpoint.startswith(self.api_url.path) + else endpoint + ) return self._derive_url( self.api_url, - path=f"{self.api_url.path or ''}/{endpoint.lstrip(self.api_url.path)}", + path=normalized_endpoint, auth=auth, query=query, fragment=fragment, diff --git a/tests/unit/semantic_release/hvcs/test_bitbucket.py b/tests/unit/semantic_release/hvcs/test_bitbucket.py index 85f1d7e46..16d77fb87 100644 --- a/tests/unit/semantic_release/hvcs/test_bitbucket.py +++ b/tests/unit/semantic_release/hvcs/test_bitbucket.py @@ -301,6 +301,29 @@ def test_commit_hash_url(default_bitbucket_client: Bitbucket): assert expected_url == default_bitbucket_client.commit_hash_url(sha) +def test_commit_hash_url_w_custom_server(): + """ + Test the commit hash URL generation for a self-hosted Bitbucket server with prefix. + + ref: https://github.com/python-semantic-release/python-semantic-release/issues/1204 + """ + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + expected_url = "{server}/{owner}/{repo}/commits/{sha}".format( + server=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + owner="foo", + repo=EXAMPLE_REPO_NAME, + sha=sha, + ) + + with mock.patch.dict(os.environ, {}, clear=True): + actual_url = Bitbucket( + remote_url=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo/foo/{EXAMPLE_REPO_NAME}.git", + hvcs_domain=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + ).commit_hash_url(sha) + + assert expected_url == actual_url + + @pytest.mark.parametrize("pr_number", (666, "666", "#666")) def test_pull_request_url(default_bitbucket_client: Bitbucket, pr_number: int | str): expected_url = "{server}/{owner}/{repo}/pull-requests/{pr_number}".format( diff --git a/tests/unit/semantic_release/hvcs/test_gitea.py b/tests/unit/semantic_release/hvcs/test_gitea.py index 710b01b08..d98be8e96 100644 --- a/tests/unit/semantic_release/hvcs/test_gitea.py +++ b/tests/unit/semantic_release/hvcs/test_gitea.py @@ -203,6 +203,29 @@ def test_commit_hash_url(default_gitea_client: Gitea): assert expected_url == default_gitea_client.commit_hash_url(sha) +def test_commit_hash_url_w_custom_server(): + """ + Test the commit hash URL generation for a self-hosted Bitbucket server with prefix. + + ref: https://github.com/python-semantic-release/python-semantic-release/issues/1204 + """ + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + expected_url = "{server}/{owner}/{repo}/commit/{sha}".format( + server=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + owner="foo", + repo=EXAMPLE_REPO_NAME, + sha=sha, + ) + + with mock.patch.dict(os.environ, {}, clear=True): + actual_url = Gitea( + remote_url=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo/foo/{EXAMPLE_REPO_NAME}.git", + hvcs_domain=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + ).commit_hash_url(sha) + + assert expected_url == actual_url + + @pytest.mark.parametrize("issue_number", (666, "666", "#666")) def test_issue_url(default_gitea_client: Gitea, issue_number: int | str): expected_url = "{server}/{owner}/{repo}/issues/{issue_number}".format( diff --git a/tests/unit/semantic_release/hvcs/test_github.py b/tests/unit/semantic_release/hvcs/test_github.py index f52482ebf..e7f69a5ea 100644 --- a/tests/unit/semantic_release/hvcs/test_github.py +++ b/tests/unit/semantic_release/hvcs/test_github.py @@ -375,6 +375,29 @@ def test_commit_hash_url(default_gh_client: Github): assert expected_url == default_gh_client.commit_hash_url(sha) +def test_commit_hash_url_w_custom_server(): + """ + Test the commit hash URL generation for a self-hosted Bitbucket server with prefix. + + ref: https://github.com/python-semantic-release/python-semantic-release/issues/1204 + """ + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + expected_url = "{server}/{owner}/{repo}/commit/{sha}".format( + server=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + owner="foo", + repo=EXAMPLE_REPO_NAME, + sha=sha, + ) + + with mock.patch.dict(os.environ, {}, clear=True): + actual_url = Github( + remote_url=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo/foo/{EXAMPLE_REPO_NAME}.git", + hvcs_domain=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + ).commit_hash_url(sha) + + assert expected_url == actual_url + + @pytest.mark.parametrize("issue_number", (666, "666", "#666")) def test_issue_url(default_gh_client: Github, issue_number: str | int): expected_url = "{server}/{owner}/{repo}/issues/{issue_num}".format( diff --git a/tests/unit/semantic_release/hvcs/test_gitlab.py b/tests/unit/semantic_release/hvcs/test_gitlab.py index c4a0979fe..3011de8bb 100644 --- a/tests/unit/semantic_release/hvcs/test_gitlab.py +++ b/tests/unit/semantic_release/hvcs/test_gitlab.py @@ -260,6 +260,29 @@ def test_commit_hash_url(default_gl_client: Gitlab): assert expected_url == default_gl_client.commit_hash_url(REF) +def test_commit_hash_url_w_custom_server(): + """ + Test the commit hash URL generation for a self-hosted Bitbucket server with prefix. + + ref: https://github.com/python-semantic-release/python-semantic-release/issues/1204 + """ + sha = "244f7e11bcb1e1ce097db61594056bc2a32189a0" + expected_url = "{server}/{owner}/{repo}/-/commit/{sha}".format( + server=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + owner="foo", + repo=EXAMPLE_REPO_NAME, + sha=sha, + ) + + with mock.patch.dict(os.environ, {}, clear=True): + actual_url = Gitlab( + remote_url=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo/foo/{EXAMPLE_REPO_NAME}.git", + hvcs_domain=f"https://{EXAMPLE_HVCS_DOMAIN}/projects/demo-foo", + ).commit_hash_url(sha) + + assert expected_url == actual_url + + @pytest.mark.parametrize("issue_number", (666, "666", "#666")) def test_issue_url(default_gl_client: Gitlab, issue_number: int | str): expected_url = "{server}/{owner}/{repo}/-/issues/{issue_num}".format(