Skip to content

Commit

Permalink
fix: create_or_update_release for Gitlab hvcs
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardcooke53 committed Jul 1, 2023
1 parent 31ae4d1 commit ba5c0d7
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 83 deletions.
6 changes: 3 additions & 3 deletions semantic_release/hvcs/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def upload_dists(self, tag: str, dist_glob: str) -> int:

def create_release(
self, tag: str, release_notes: str, prerelease: bool = False
) -> int:
) -> int | str:
"""
Create a release in a remote VCS, if supported
"""
Expand All @@ -104,7 +104,7 @@ def get_release_id_by_tag(self, tag: str) -> int | None:
_not_supported(self, "get_release_id_by_tag")
return None

def edit_release_notes(self, release_id: int, changelog: str) -> int:
def edit_release_notes(self, release_id: int, release_notes: str) -> int:
"""
Edit the changelog associated with a release, if supported
"""
Expand All @@ -113,7 +113,7 @@ def edit_release_notes(self, release_id: int, changelog: str) -> int:

def create_or_update_release(
self, tag: str, release_notes: str, prerelease: bool = False
) -> int:
) -> int | str:
"""
Create or update a release for the given tag in a remote VCS, attaching the
given changelog, if supported
Expand Down
2 changes: 1 addition & 1 deletion semantic_release/hvcs/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ def create_release(
:param tag: Tag to create release for
:param release_notes: The release notes for this version
:param prerelease: Whether or not this release should be created as a prerelease
:return: Whether the request succeeded
:return: the ID of the release
"""
log.info("Creating release for tag %s", tag)
resp = self.session.post(
Expand Down
49 changes: 36 additions & 13 deletions semantic_release/hvcs/gitlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,34 +87,57 @@ def _get_repository_owner_and_name(self) -> tuple[str, str]:
@logged_function(log)
def create_release(
self, tag: str, release_notes: str, prerelease: bool = False
) -> bool:
) -> str:
"""Post release changelog
:param tag: Tag to create release for
:param release_notes: The release notes for this version
:param prerelease: This parameter has no effect
:return: The status of the request
:return: The tag of the release
"""
client = gitlab.Gitlab(self.api_url, private_token=self.token)
client.auth()
log.info("Creating release for %s", tag)
# ref: https://docs.gitlab.com/ee/api/releases/index.html#create-a-release
client.projects.get(self.owner + "/" + self.repo_name).releases.create(
{
"name": "Release " + tag,
"tag_name": tag,
"description": release_notes,
}
)
log.info("Successfully created release for %s", tag)
return tag

@logged_function(log)
def edit_release_notes(self, release_id: str, release_notes: str) -> str: # type: ignore # TODO make str types accepted here
client = gitlab.Gitlab(self.api_url, private_token=self.token)
client.auth()
log.info("Updating release %s", release_id)

client.projects.get(self.owner + "/" + self.repo_name).releases.update(
release_id,
{
"description": release_notes,
},
)
return release_id

@logged_function(log)
def create_or_update_release(
self, tag: str, release_notes: str, prerelease: bool = False
) -> str:
try:
log.info("Creating release for %s", tag)
# ref: https://docs.gitlab.com/ee/api/releases/index.html#create-a-release
client.projects.get(self.owner + "/" + self.repo_name).releases.create(
{
"name": "Release " + tag,
"tag_name": tag,
"description": release_notes,
}
return self.create_release(
tag=tag, release_notes=release_notes, prerelease=prerelease
)
return True
except gitlab.GitlabCreateError:
log.warning(
log.info(
"Release %s could not be created for project %s/%s",
tag,
self.owner,
self.repo_name,
)
return False
return self.edit_release_notes(release_id=tag, release_notes=release_notes)

def compare_url(self, from_rev: str, to_rev: str) -> str:
return f"https://{self.hvcs_domain}/{self.owner}/{self.repo_name}/-/compare/{from_rev}...{to_rev}"
Expand Down
2 changes: 2 additions & 0 deletions tests/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,3 +450,5 @@ def _read_long_description():
* Extra cookies to enhance your experience
* ~Removed~ simplified cookie opt-out handling logic
"""

RELEASE_NOTES = "# Release Notes"
42 changes: 17 additions & 25 deletions tests/unit/semantic_release/hvcs/test_gitea.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from semantic_release.hvcs.gitea import Gitea
from semantic_release.hvcs.token_auth import TokenAuth

from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER
from tests.const import EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, RELEASE_NOTES
from tests.util import netrc_file


Expand Down Expand Up @@ -199,7 +199,6 @@ def test_create_release_succeeds(
default_gitea_client, status_code, prerelease, mock_release_id
):
tag = "v1.0.0"
release_notes = "#TODO: Release Notes"
with requests_mock.Mocker(session=default_gitea_client.session) as m:
m.register_uri(
"POST",
Expand All @@ -208,7 +207,7 @@ def test_create_release_succeeds(
status_code=status_code,
)
assert (
default_gitea_client.create_release(tag, release_notes, prerelease)
default_gitea_client.create_release(tag, RELEASE_NOTES, prerelease)
== mock_release_id
)
assert m.called
Expand All @@ -225,7 +224,7 @@ def test_create_release_succeeds(
assert m.last_request.json() == {
"tag_name": tag,
"name": tag,
"body": release_notes,
"body": RELEASE_NOTES,
"draft": False,
"prerelease": prerelease,
}
Expand All @@ -238,7 +237,6 @@ def test_create_release_fails(
default_gitea_client, status_code, prerelease, mock_release_id
):
tag = "v1.0.0"
release_notes = "#TODO: Release Notes"
with requests_mock.Mocker(session=default_gitea_client.session) as m:
m.register_uri(
"POST",
Expand All @@ -248,7 +246,7 @@ def test_create_release_fails(
)

with pytest.raises(HTTPError):
default_gitea_client.create_release(tag, release_notes, prerelease)
default_gitea_client.create_release(tag, RELEASE_NOTES, prerelease)

assert m.called
assert len(m.request_history) == 1
Expand All @@ -264,7 +262,7 @@ def test_create_release_fails(
assert m.last_request.json() == {
"tag_name": tag,
"name": tag,
"body": release_notes,
"body": RELEASE_NOTES,
"draft": False,
"prerelease": prerelease,
}
Expand All @@ -275,15 +273,14 @@ def test_should_create_release_using_token_or_netrc(default_gitea_client, token)
default_gitea_client.token = token
default_gitea_client.session.auth = None if not token else TokenAuth(token)
tag = "v1.0.0"
release_notes = "#TODO: Release Notes"

# Note write netrc file with DEFAULT_DOMAIN not DEFAULT_API_DOMAIN as can't
# handle /api/v1 in file
with requests_mock.Mocker(session=default_gitea_client.session) as m, netrc_file(
machine=default_gitea_client.DEFAULT_DOMAIN
) as netrc, mock.patch.dict(os.environ, {"NETRC": netrc.name}, clear=True):
m.register_uri("POST", gitea_api_matcher, json={"id": 1}, status_code=201)
assert default_gitea_client.create_release(tag, release_notes) == 1
assert default_gitea_client.create_release(tag, RELEASE_NOTES) == 1
assert m.called
assert len(m.request_history) == 1
assert m.last_request.method == "POST"
Expand Down Expand Up @@ -311,7 +308,7 @@ def test_should_create_release_using_token_or_netrc(default_gitea_client, token)
assert m.last_request.json() == {
"tag_name": tag,
"name": tag,
"body": release_notes,
"body": RELEASE_NOTES,
"draft": False,
"prerelease": False,
}
Expand All @@ -323,7 +320,7 @@ def test_request_has_no_auth_header_if_no_token_or_netrc():

with requests_mock.Mocker(session=client.session) as m:
m.register_uri("POST", gitea_api_matcher, json={"id": 1}, status_code=201)
assert client.create_release("v1.0.0", "#TODO: Release Notes") == 1
assert client.create_release("v1.0.0", RELEASE_NOTES) == 1
assert m.called
assert len(m.request_history) == 1
assert m.last_request.method == "POST"
Expand Down Expand Up @@ -373,7 +370,6 @@ def test_get_release_id_by_tag(
def test_edit_release_notes_succeeds(
default_gitea_client, status_code, mock_release_id
):
release_notes = "#TODO: Release Notes"
with requests_mock.Mocker(session=default_gitea_client.session) as m:
m.register_uri(
"PATCH",
Expand All @@ -382,7 +378,7 @@ def test_edit_release_notes_succeeds(
status_code=status_code,
)
assert (
default_gitea_client.edit_release_notes(mock_release_id, release_notes)
default_gitea_client.edit_release_notes(mock_release_id, RELEASE_NOTES)
== mock_release_id
)
assert m.called
Expand All @@ -397,13 +393,12 @@ def test_edit_release_notes_succeeds(
release_id=mock_release_id,
)
)
assert m.last_request.json() == {"body": release_notes}
assert m.last_request.json() == {"body": RELEASE_NOTES}


@pytest.mark.parametrize("status_code", (400, 404, 429, 500, 503))
@pytest.mark.parametrize("mock_release_id", range(3))
def test_edit_release_notes_fails(default_gitea_client, status_code, mock_release_id):
release_notes = "#TODO: Release Notes"
with requests_mock.Mocker(session=default_gitea_client.session) as m:
m.register_uri(
"PATCH",
Expand All @@ -413,7 +408,7 @@ def test_edit_release_notes_fails(default_gitea_client, status_code, mock_releas
)

with pytest.raises(HTTPError):
default_gitea_client.edit_release_notes(mock_release_id, release_notes)
default_gitea_client.edit_release_notes(mock_release_id, RELEASE_NOTES)

assert m.called
assert len(m.request_history) == 1
Expand All @@ -427,7 +422,7 @@ def test_edit_release_notes_fails(default_gitea_client, status_code, mock_releas
release_id=mock_release_id,
)
)
assert m.last_request.json() == {"body": release_notes}
assert m.last_request.json() == {"body": RELEASE_NOTES}


# Note - mocking as the logic for the create/update of a release
Expand All @@ -442,7 +437,6 @@ def test_create_or_update_release_when_create_succeeds(
prerelease,
):
tag = "v1.0.0"
release_notes = "# TODO: Release Notes"
with mock.patch.object(
default_gitea_client, "create_release"
) as mock_create_release, mock.patch.object(
Expand All @@ -456,11 +450,11 @@ def test_create_or_update_release_when_create_succeeds(
# client = Github(remote_url="git@github.com:something/somewhere.git")
assert (
default_gitea_client.create_or_update_release(
tag, release_notes, prerelease
tag, RELEASE_NOTES, prerelease
)
== mock_release_id
)
mock_create_release.assert_called_once_with(tag, release_notes, prerelease)
mock_create_release.assert_called_once_with(tag, RELEASE_NOTES, prerelease)
mock_get_release_id_by_tag.assert_not_called()
mock_edit_release_notes.assert_not_called()

Expand All @@ -473,7 +467,6 @@ def test_create_or_update_release_when_create_fails_and_update_succeeds(
prerelease,
):
tag = "v1.0.0"
release_notes = "# TODO: Release Notes"
not_found = HTTPError("404 Not Found", response=Response())
not_found.response.status_code = 404
with mock.patch.object(
Expand All @@ -489,20 +482,19 @@ def test_create_or_update_release_when_create_fails_and_update_succeeds(
# client = Github(remote_url="git@github.com:something/somewhere.git")
assert (
default_gitea_client.create_or_update_release(
tag, release_notes, prerelease
tag, RELEASE_NOTES, prerelease
)
== mock_release_id
)
mock_get_release_id_by_tag.assert_called_once_with(tag)
mock_edit_release_notes.assert_called_once_with(mock_release_id, release_notes)
mock_edit_release_notes.assert_called_once_with(mock_release_id, RELEASE_NOTES)


@pytest.mark.parametrize("prerelease", (True, False))
def test_create_or_update_release_when_create_fails_and_no_release_for_tag(
default_gitea_client, prerelease
):
tag = "v1.0.0"
release_notes = "# TODO: Release Notes"
not_found = HTTPError("404 Not Found", response=Response())
not_found.response.status_code = 404
with mock.patch.object(
Expand All @@ -518,7 +510,7 @@ def test_create_or_update_release_when_create_fails_and_no_release_for_tag(

with pytest.raises(ValueError):
default_gitea_client.create_or_update_release(
tag, release_notes, prerelease
tag, RELEASE_NOTES, prerelease
)

mock_get_release_id_by_tag.assert_called_once_with(tag)
Expand Down

0 comments on commit ba5c0d7

Please sign in to comment.