Repository: relekang/python-semantic-release
Pull request #364 (2021-08-03 10:26:32)

Prefer Github/Gitlab environment variables to get owner/project
related to #363 


ziouf: > 
> 
> Please fix the test by mocking the environment variable and add a test for using the environment variable.

Added some tests and updated upstream

Patch:
diff --git a/semantic_release/hvcs.py b/semantic_release/hvcs.py
index 22f1499..b1d35d2 100644
--- a/semantic_release/hvcs.py
+++ b/semantic_release/hvcs.py
@@ -4,6 +4,7 @@ import logging
 import mimetypes
 import os
 from typing import Optional, Union
+from urllib.parse import urlsplit
 
 from gitlab import exceptions, gitlab
 from requests import HTTPError, Session
@@ -352,7 +353,12 @@ class Gitlab(Base):
 
         :return: The Gitlab instance domain
         """
-        domain = config.get("hvcs_domain", os.environ.get("CI_SERVER_HOST"))
+        # Use Gitlab-CI environment vars if available
+        if 'CI_SERVER_URL' in os.environ:
+            url = urlsplit(os.environ['CI_SERVER_URL'])
+            return f"{url.netloc}{url.path}".rstrip('/')
+
+        domain = config.get("hvcs_domain", os.environ.get("CI_SERVER_HOST", None))
         return domain if domain else "gitlab.com"
 
     @staticmethod
@@ -361,6 +367,10 @@ class Gitlab(Base):
 
         :return: The Gitlab instance API url
         """
+        # Use Gitlab-CI environment vars if available
+        if 'CI_SERVER_URL' in os.environ:
+            return os.environ['CI_SERVER_URL']
+
         return f"https://{Gitlab.domain()}"
 
     @staticmethod
@@ -415,6 +425,7 @@ class Gitlab(Base):
         gl = gitlab.Gitlab(Gitlab.api_url(), private_token=Gitlab.token())
         gl.auth()
         try:
+            logger.debug(f"Before release create call")
             gl.projects.get(owner + "/" + repo).releases.create(
                 {
                     "name": "Release " + version,
diff --git a/semantic_release/vcs_helpers.py b/semantic_release/vcs_helpers.py
index a670d07..4e55766 100644
--- a/semantic_release/vcs_helpers.py
+++ b/semantic_release/vcs_helpers.py
@@ -9,7 +9,8 @@ from pathlib import Path, PurePath
 from typing import Optional, Tuple
 from urllib.parse import urlsplit
 
-from git import GitCommandError, InvalidGitRepositoryError, Repo, TagObject
+from git import GitCommandError, InvalidGitRepositoryError, Repo
+from git.objects.tag import TagObject
 from git.exc import BadName
 
 from .errors import GitError, HvcsRepoParseError
@@ -106,6 +107,16 @@ def get_repository_owner_and_name() -> Tuple[str, str]:
 
     :return: A tuple of the owner and name.
     """
+    # Gitlab-CI context
+    if 'CI_PROJECT_NAMESPACE' in os.environ and 'CI_PROJECT_NAME' in os.environ:
+        return os.environ['CI_PROJECT_NAMESPACE'], os.environ['CI_PROJECT_NAME']
+
+    # Github actions context
+    if 'GITHUB_REPOSITORY' in os.environ:
+        owner, name = os.environ['GITHUB_REPOSITORY'].rsplit('/', 1)
+        return owner, name
+
+    # Local context
     url = repo.remote("origin").url
     split_url = urlsplit(url)
     # Select the owner and name as regex groups
@@ -153,7 +164,7 @@ def commit_new_version(version: str):
     )
 
     for declaration in load_version_declarations():
-        git_path = PurePath(os.getcwd(), declaration.path).relative_to(repo.working_dir)
+        git_path = PurePath(os.getcwd(), declaration.path).relative_to(str(repo.working_dir))
         repo.git.add(str(git_path))
 
     return repo.git.commit(m=message, author=commit_author)
@@ -196,7 +207,7 @@ def update_changelog_file(version: str, content_to_add: str):
         ),
     )
     git_path.write_text(updated_content)
-    repo.git.add(str(git_path.relative_to(repo.working_dir)))
+    repo.git.add(str(git_path.relative_to(str(repo.working_dir))))
 
 
 @check_repo


Test patch:
diff --git a/tests/test_hvcs.py b/tests/test_hvcs.py
index 3f7ab25..21b5ffb 100644
--- a/tests/test_hvcs.py
+++ b/tests/test_hvcs.py
@@ -78,6 +78,13 @@ def test_check_token_should_return_false():
             "https://example.gitlab.com",
             None,
         ),
+        (
+            "gitlab",
+            "example.gitlab.com/path",
+            "example.gitlab.com/path",
+            "https://example.gitlab.com/path",
+            None,
+        ),
         (
             "gitlab",
             "example2.gitlab.com",
diff --git a/tests/test_vcs_helpers.py b/tests/test_vcs_helpers.py
index 969a0be..848d71e 100644
--- a/tests/test_vcs_helpers.py
+++ b/tests/test_vcs_helpers.py
@@ -1,5 +1,6 @@
 from datetime import date
 
+import os
 import git
 import pytest
 from git import GitCommandError, Repo, TagObject
@@ -132,6 +133,14 @@ def test_push_new_version_with_custom_branch(mock_git):
     )
 
 
+@mock.patch.dict(
+    os.environ,
+    {
+        k: v for k, v in os.environ.items()
+        if k not in ['GITHUB_REPOSITORY', 'CI_PROJECT_NAMESPACE', 'CI_PROJECT_NAME']
+    },
+    clear=True
+)
 @pytest.mark.parametrize(
     "origin_url,expected_result",
     [
@@ -197,6 +206,65 @@ def test_get_repository_owner_and_name(mocker, origin_url, expected_result):
             get_repository_owner_and_name()
 
 
+@mock.patch.dict(
+    os.environ,
+    {
+        **os.environ,
+        'GITHUB_REPOSITORY': 'group/subgroup/project',
+    },
+    clear=True
+)
+@pytest.mark.parametrize(
+    "origin_url,expected_result",
+    [
+        ("https://github.com/group/project.git", ("group/subgroup", "project")),
+        ("https://github.com/group/subgroup/project.git", ("group/subgroup", "project")),
+        ("https://github.com/group/sub.group/project.git", ("group/subgroup", "project")),
+        ("https://github.com/group/subgroup/pro.ject.git", ("group/subgroup", "project")),
+    ],
+)
+def test_get_repository_owner_and_name_github(mocker, origin_url, expected_result):
+    class FakeRemote:
+        url = origin_url
+
+    mocker.patch("git.repo.base.Repo.remote", return_value=FakeRemote())
+    if isinstance(expected_result, tuple):
+        assert get_repository_owner_and_name() == expected_result
+    else:
+        with pytest.raises(expected_result):
+            get_repository_owner_and_name()
+
+
+@mock.patch.dict(
+    os.environ,
+    {
+        **os.environ,
+        'CI_PROJECT_NAMESPACE': 'group/subgroup',
+        'CI_PROJECT_NAME': 'project',
+    },
+    clear=True
+)
+@pytest.mark.parametrize(
+    "origin_url,expected_result",
+    [
+        ("https://gitlab.example.com/group/subgroup/project.git", ("group/subgroup", "project")),
+        ("https://gitlab.example.com/group/subgroup/project",     ("group/subgroup", "project")),
+        ("https://gitlab.example.com/group/project",              ("group/subgroup", "project")),
+        ("https://gitlab-ci-token:MySuperToken@gitlab.example.com/group/subgroup/project.git", ("group/subgroup", "project")),
+    ],
+)
+def test_get_repository_owner_and_name_gitlab(mocker, origin_url, expected_result):
+    class FakeRemote:
+        url = origin_url
+
+    mocker.patch("git.repo.base.Repo.remote", return_value=FakeRemote())
+    if isinstance(expected_result, tuple):
+        assert get_repository_owner_and_name() == expected_result
+    else:
+        with pytest.raises(expected_result):
+            get_repository_owner_and_name()
+
+
 def test_get_current_head_hash(mocker):
     mocker.patch("git.objects.commit.Commit.name_rev", "commit-hash branch-name")
     assert get_current_head_hash() == "commit-hash"