From 5aad42f5c464cf8f75c18aeb8d75f9a6f7710311 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 26 Jul 2025 15:46:58 -0600 Subject: [PATCH 1/5] refactor(github-actions-output): add github client to object initialization --- src/semantic_release/cli/github_actions_output.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/semantic_release/cli/github_actions_output.py b/src/semantic_release/cli/github_actions_output.py index c10f927e1..77811d139 100644 --- a/src/semantic_release/cli/github_actions_output.py +++ b/src/semantic_release/cli/github_actions_output.py @@ -2,22 +2,29 @@ import os from re import compile as regexp -from typing import Any +from typing import TYPE_CHECKING from semantic_release.globals import logger from semantic_release.version.version import Version +if TYPE_CHECKING: + from typing import Any + + from semantic_release.hvcs.github import Github + class VersionGitHubActionsOutput: OUTPUT_ENV_VAR = "GITHUB_OUTPUT" def __init__( self, + gh_client: Github, released: bool | None = None, version: Version | None = None, commit_sha: str | None = None, release_notes: str | None = None, ) -> None: + self._gh_client = gh_client self._released = released self._version = version self._commit_sha = commit_sha From cf4933ead65be794ffaaf96745373b5a545d09db Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 26 Jul 2025 15:48:08 -0600 Subject: [PATCH 2/5] test(gha): update unit tests for GitHub Actions output to include `link` output check ref: #512 --- .../cli/test_github_actions_output.py | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/tests/unit/semantic_release/cli/test_github_actions_output.py b/tests/unit/semantic_release/cli/test_github_actions_output.py index 91b7e1605..d650a18e5 100644 --- a/tests/unit/semantic_release/cli/test_github_actions_output.py +++ b/tests/unit/semantic_release/cli/test_github_actions_output.py @@ -3,18 +3,24 @@ import os from textwrap import dedent from typing import TYPE_CHECKING +from unittest import mock import pytest from semantic_release.cli.github_actions_output import VersionGitHubActionsOutput +from semantic_release.hvcs.github import Github from semantic_release.version.version import Version +from tests.const import EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER from tests.util import actions_output_to_dict if TYPE_CHECKING: from pathlib import Path +BASE_VCS_URL = f"https://{EXAMPLE_HVCS_DOMAIN}/{EXAMPLE_REPO_OWNER}/{EXAMPLE_REPO_NAME}" + + @pytest.mark.parametrize( "version, is_prerelease", [ @@ -41,25 +47,29 @@ def test_version_github_actions_output_format( version={version} tag=v{version} is_prerelease={'true' if is_prerelease else 'false'} + link={BASE_VCS_URL}/releases/tag/v{version} commit_sha={commit_sha} """ ) + f"release_notes< actual) - assert expected_output == output.to_output_text() + assert expected_output == actual_output_text def test_version_github_actions_output_fails_if_missing_released_param(): output = VersionGitHubActionsOutput( + gh_client=Github(f"{BASE_VCS_URL}.git"), version=Version.parse("1.2.3"), ) @@ -70,6 +80,7 @@ def test_version_github_actions_output_fails_if_missing_released_param(): def test_version_github_actions_output_fails_if_missing_commit_sha_param(): output = VersionGitHubActionsOutput( + gh_client=Github(f"{BASE_VCS_URL}.git"), released=True, version=Version.parse("1.2.3"), ) @@ -81,6 +92,7 @@ def test_version_github_actions_output_fails_if_missing_commit_sha_param(): def test_version_github_actions_output_fails_if_missing_release_notes_param(): output = VersionGitHubActionsOutput( + gh_client=Github(f"{BASE_VCS_URL}.git"), released=True, version=Version.parse("1.2.3"), ) @@ -91,7 +103,7 @@ def test_version_github_actions_output_fails_if_missing_release_notes_param(): def test_version_github_actions_output_writes_to_github_output_if_available( - monkeypatch: pytest.MonkeyPatch, tmp_path: Path + tmp_path: Path, ): mock_output_file = tmp_path / "action.out" version_str = "1.2.3" @@ -103,15 +115,17 @@ def test_version_github_actions_output_writes_to_github_output_if_available( - Fixed bug """ ) - monkeypatch.setenv("GITHUB_OUTPUT", str(mock_output_file.resolve())) - output = VersionGitHubActionsOutput( - version=Version.parse(version_str), - released=True, - commit_sha=commit_sha, - release_notes=release_notes, - ) - output.write_if_possible() + patched_environ = {"GITHUB_OUTPUT": str(mock_output_file.resolve())} + + with mock.patch.dict(os.environ, patched_environ, clear=True): + VersionGitHubActionsOutput( + gh_client=Github(f"{BASE_VCS_URL}.git", hvcs_domain=EXAMPLE_HVCS_DOMAIN), + version=Version.parse(version_str), + released=True, + commit_sha=commit_sha, + release_notes=release_notes, + ).write_if_possible() with open(mock_output_file, encoding="utf-8", newline=os.linesep) as rfd: action_outputs = actions_output_to_dict(rfd.read()) @@ -120,6 +134,7 @@ def test_version_github_actions_output_writes_to_github_output_if_available( assert version_str == action_outputs["version"] assert str(True).lower() == action_outputs["released"] assert str(False).lower() == action_outputs["is_prerelease"] + assert f"{BASE_VCS_URL}/releases/tag/v{version_str}" == action_outputs["link"] assert f"v{version_str}" == action_outputs["tag"] assert commit_sha == action_outputs["commit_sha"] assert release_notes == action_outputs["release_notes"] @@ -129,6 +144,7 @@ def test_version_github_actions_output_no_error_if_not_in_gha( monkeypatch: pytest.MonkeyPatch, ): output = VersionGitHubActionsOutput( + gh_client=Github(f"{BASE_VCS_URL}.git"), version=Version.parse("1.2.3"), released=True, commit_sha="0" * 40, # 40 zeroes to simulate a SHA-1 hash From 22321d3a58b1104036eb1a9757d202c8d01cdc0e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 26 Jul 2025 15:48:52 -0600 Subject: [PATCH 3/5] test(gha): update e2e test for GitHub Actions output to include `link` output check ref: #512 --- tests/e2e/cmd_version/test_version_github_actions.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/e2e/cmd_version/test_version_github_actions.py b/tests/e2e/cmd_version/test_version_github_actions.py index a6c724a03..7c388e2da 100644 --- a/tests/e2e/cmd_version/test_version_github_actions.py +++ b/tests/e2e/cmd_version/test_version_github_actions.py @@ -17,6 +17,8 @@ from tests.util import actions_output_to_dict, assert_successful_exit_code if TYPE_CHECKING: + from semantic_release.hvcs.github import Github + from tests.conftest import GetStableDateNowFn, RunCliFn from tests.fixtures.example_project import ExProjectDir from tests.fixtures.git_repo import ( @@ -50,7 +52,7 @@ def test_version_writes_github_actions_output( all_versions = get_versions_from_repo_build_def(repo_def) latest_release_version = all_versions[-1] release_tag = tag_format_str.format(version=latest_release_version) - + hvcs_client = cast("Github", get_hvcs_client_from_repo_def(repo_def)) repo_actions_per_version = split_repo_actions_by_release_tags( repo_definition=repo_def, tag_format_str=tag_format_str, @@ -59,13 +61,14 @@ def test_version_writes_github_actions_output( "released": str(True).lower(), "version": latest_release_version, "tag": release_tag, + "link": hvcs_client.create_release_url(release_tag), "commit_sha": "0" * 40, "is_prerelease": str( Version.parse(latest_release_version).is_prerelease ).lower(), "release_notes": generate_default_release_notes_from_def( version_actions=repo_actions_per_version[release_tag], - hvcs=get_hvcs_client_from_repo_def(repo_def), + hvcs=hvcs_client, previous_version=( Version.parse(all_versions[-2]) if len(all_versions) > 1 else None ), @@ -112,6 +115,7 @@ def test_version_writes_github_actions_output( assert expected_gha_output["version"] == action_outputs["version"] assert expected_gha_output["tag"] == action_outputs["tag"] assert expected_gha_output["is_prerelease"] == action_outputs["is_prerelease"] + assert expected_gha_output["link"] == action_outputs["link"] assert expected_gha_output["commit_sha"] == action_outputs["commit_sha"] assert ( expected_gha_output["release_notes"].encode() From 20817a051ac2ee3bec47bedf7fd705b8b8249a13 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 26 Jul 2025 15:49:58 -0600 Subject: [PATCH 4/5] feat(github-actions): add release `link` as a GitHub Actions output value ref: #512 --- src/semantic_release/cli/commands/version.py | 8 +++++++- src/semantic_release/cli/github_actions_output.py | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index 77a52afb4..b144665b0 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -30,6 +30,7 @@ ) from semantic_release.gitproject import GitProject from semantic_release.globals import logger +from semantic_release.hvcs.github import Github from semantic_release.hvcs.remote_hvcs_base import RemoteHvcsBase from semantic_release.version.algorithm import ( next_version, @@ -466,7 +467,12 @@ def version( # noqa: C901 major_on_zero = runtime.major_on_zero no_verify = runtime.no_git_verify opts = runtime.global_cli_options - gha_output = VersionGitHubActionsOutput(released=False) + gha_output = VersionGitHubActionsOutput( + hvcs_client + if isinstance(hvcs_client, Github) + else Github(hvcs_client.remote_url(use_token=False)), + released=False, + ) forced_level_bump = None if not force_level else LevelBump.from_string(force_level) prerelease = is_forced_prerelease( diff --git a/src/semantic_release/cli/github_actions_output.py b/src/semantic_release/cli/github_actions_output.py index 77811d139..ffe34987f 100644 --- a/src/semantic_release/cli/github_actions_output.py +++ b/src/semantic_release/cli/github_actions_output.py @@ -105,6 +105,7 @@ def to_output_text(self) -> str: "version": str(self.version), "tag": self.tag, "is_prerelease": str(self.is_prerelease).lower(), + "link": self._gh_client.create_release_url(self.tag) if self.tag else "", "commit_sha": self.commit_sha if self.commit_sha else "", } From d5e41cf394e16b604c3af0cd4b9a86a3b6be2621 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 26 Jul 2025 15:50:31 -0600 Subject: [PATCH 5/5] docs(github-actions): add description of release `link` GitHub Action output --- action.yml | 13 ++++-- .../automatic-releases/github-actions.rst | 40 +++++++++++++------ 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/action.yml b/action.yml index b3ae0c213..97f5a9111 100644 --- a/action.yml +++ b/action.yml @@ -122,17 +122,22 @@ inputs: Build metadata to append to the new version outputs: + commit_sha: + description: | + The commit SHA of the release if a release was made, otherwise an empty string + is_prerelease: description: | "true" if the version is a prerelease, "false" otherwise - released: + link: description: | - "true" if a release was made, "false" otherwise + The link to the release in the remote VCS, if a release was made. If no release was made, + this will be an empty string. - commit_sha: + released: description: | - The commit SHA of the release if a release was made, otherwise an empty string + "true" if a release was made, "false" otherwise release_notes: description: | diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index 79aef1066..d21f2f351 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -513,6 +513,20 @@ and any actions that were taken. ---- +.. _gh_actions-psr-outputs-commit_sha: + +``commit_sha`` +"""""""""""""" + +**Type:** ``string`` + +The commit SHA of the release if a release was made, otherwise an empty string. + +Example upon release: ``d4c3b2a1e0f9c8b7a6e5d4c3b2a1e0f9c8b7a6e5`` +Example when no release was made: ``""`` + +---- + .. _gh_actions-psr-outputs-is_prerelease: ``is_prerelease`` @@ -524,28 +538,28 @@ A boolean value indicating whether the released version is a prerelease. ---- -.. _gh_actions-psr-outputs-released: +.. _gh_actions-psr-outputs-link: -``released`` -"""""""""""" +``link`` +"""""""" -**Type:** ``Literal["true", "false"]`` +**Type:** ``string`` -A boolean value indicating whether a release was made. +The URL link to the release if a release was made, otherwise an empty string. ----- +Example upon release: ``https://github.com/user/repo/releases/tag/v1.2.3`` +Example when no release was made: ``""`` -.. _gh_actions-psr-outputs-commit_sha: +---- -``commit_sha`` -""""""""""""""""" +.. _gh_actions-psr-outputs-released: -**Type:** ``string`` +``released`` +"""""""""""" -The commit SHA of the release if a release was made, otherwise an empty string. +**Type:** ``Literal["true", "false"]`` -Example upon release: ``d4c3b2a1e0f9c8b7a6e5d4c3b2a1e0f9c8b7a6e5`` -Example when no release was made: ``""`` +A boolean value indicating whether a release was made. ----