From 61c2e5d1fa50889119fa0e49439232efff727ffa Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 4 Jul 2025 08:06:44 -0600 Subject: [PATCH 1/4] test(gha): add unit test for GitHub Actions output of `commit_sha` ref: #717 --- .../cli/test_github_actions_output.py | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) 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 7d46f18ef..7b5cc2861 100644 --- a/tests/unit/semantic_release/cli/test_github_actions_output.py +++ b/tests/unit/semantic_release/cli/test_github_actions_output.py @@ -25,24 +25,27 @@ def test_version_github_actions_output_format( released: bool, version: str, is_prerelease: bool ): + commit_sha = "0" * 40 # 40 zeroes to simulate a SHA-1 hash expected_output = dedent( f"""\ released={'true' if released else 'false'} version={version} tag=v{version} is_prerelease={'true' if is_prerelease else 'false'} + commit_sha={commit_sha} """ ) output = VersionGitHubActionsOutput( released=released, version=Version.parse(version), + commit_sha=commit_sha, ) # Evaluate (expected -> actual) assert expected_output == output.to_output_text() -def test_version_github_actions_output_fails_if_missing_output(): +def test_version_github_actions_output_fails_if_missing_released_param(): output = VersionGitHubActionsOutput( version=Version.parse("1.2.3"), ) @@ -52,15 +55,28 @@ def test_version_github_actions_output_fails_if_missing_output(): output.to_output_text() +def test_version_github_actions_output_fails_if_missing_commit_sha_param(): + output = VersionGitHubActionsOutput( + released=True, + version=Version.parse("1.2.3"), + ) + + # Execute with expected failure + with pytest.raises(ValueError, match="required outputs were not set"): + output.to_output_text() + + def test_version_github_actions_output_writes_to_github_output_if_available( monkeypatch: pytest.MonkeyPatch, tmp_path: Path ): mock_output_file = tmp_path / "action.out" version_str = "1.2.3" + commit_sha = "0" * 40 # 40 zeroes to simulate a SHA-1 hash monkeypatch.setenv("GITHUB_OUTPUT", str(mock_output_file.resolve())) output = VersionGitHubActionsOutput( version=Version.parse(version_str), released=True, + commit_sha=commit_sha, ) output.write_if_possible() @@ -73,6 +89,8 @@ 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"v{version_str}" == action_outputs["tag"] + assert commit_sha == action_outputs["commit_sha"] def test_version_github_actions_output_no_error_if_not_in_gha( @@ -81,6 +99,7 @@ def test_version_github_actions_output_no_error_if_not_in_gha( output = VersionGitHubActionsOutput( version=Version.parse("1.2.3"), released=True, + commit_sha="0" * 40, # 40 zeroes to simulate a SHA-1 hash ) monkeypatch.delenv("GITHUB_OUTPUT", raising=False) From a65a72375f3bc228b008bc58edd903dcaebade37 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 4 Jul 2025 08:17:50 -0600 Subject: [PATCH 2/4] test(gha): update e2e test for GitHub Actions output to include `commit_sha` output check ref: #717 --- .../test_version_github_actions.py | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/tests/e2e/cmd_version/test_version_github_actions.py b/tests/e2e/cmd_version/test_version_github_actions.py index 53917e706..11f13b90f 100644 --- a/tests/e2e/cmd_version/test_version_github_actions.py +++ b/tests/e2e/cmd_version/test_version_github_actions.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING import pytest +from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from tests.const import MAIN_PROG_NAME, VERSION_SUBCMD from tests.fixtures.repos import ( @@ -13,16 +14,26 @@ if TYPE_CHECKING: from tests.conftest import RunCliFn from tests.fixtures.example_project import ExProjectDir + from tests.fixtures.git_repo import BuiltRepoResult -@pytest.mark.usefixtures( - repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__ +@pytest.mark.parametrize( + "repo_result", + [lazy_fixture(repo_w_git_flow_w_alpha_prereleases_n_conventional_commits.__name__)], ) def test_version_writes_github_actions_output( + repo_result: BuiltRepoResult, run_cli: RunCliFn, example_project_dir: ExProjectDir, ): mock_output_file = example_project_dir / "action.out" + expected_gha_output = { + "released": str(True).lower(), + "version": "1.2.1", + "tag": "v1.2.1", + "commit_sha": "0" * 40, + "is_prerelease": str(False).lower(), + } # Act cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--patch", "--no-push"] @@ -31,6 +42,9 @@ def test_version_writes_github_actions_output( ) assert_successful_exit_code(result, cli_cmd) + # Update the expected output with the commit SHA + expected_gha_output["commit_sha"] = repo_result["repo"].head.commit.hexsha + if not mock_output_file.exists(): pytest.fail( f"Expected output file {mock_output_file} to be created, but it does not exist." @@ -42,9 +56,14 @@ def test_version_writes_github_actions_output( ) # Evaluate - assert "released" in action_outputs - assert action_outputs["released"] == "true" - assert "version" in action_outputs - assert action_outputs["version"] == "1.2.1" - assert "tag" in action_outputs - assert action_outputs["tag"] == "v1.2.1" + expected_keys = set(expected_gha_output.keys()) + actual_keys = set(action_outputs.keys()) + key_difference = expected_keys.symmetric_difference(actual_keys) + + assert not key_difference, f"Unexpected keys found: {key_difference}" + + assert expected_gha_output["released"] == action_outputs["released"] + 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["commit_sha"] == action_outputs["commit_sha"] From 0bd81ace5bd49cc1b0b41e9ae49fa916003cb44e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 4 Jul 2025 08:35:44 -0600 Subject: [PATCH 3/4] feat(github-actions): add `commit_sha` as a GitHub Actions output value Implements: #717 --- action.yml | 4 ++++ src/semantic_release/cli/commands/version.py | 3 +++ .../cli/github_actions_output.py | 22 +++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/action.yml b/action.yml index 2cf7cf5ec..023aec9b7 100644 --- a/action.yml +++ b/action.yml @@ -130,6 +130,10 @@ outputs: description: | "true" if a release was made, "false" otherwise + commit_sha: + description: | + The commit SHA of the release if a release was made, otherwise an empty string + tag: description: | The Git tag corresponding to the version output diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index afa3f9b3d..45d9178b3 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -676,6 +676,9 @@ def version( # noqa: C901 noop=opts.noop, ) + with Repo(str(runtime.repo_dir)) as git_repo: + gha_output.commit_sha = git_repo.head.commit.hexsha + if push_changes: remote_url = runtime.hvcs_client.remote_url( use_token=not runtime.ignore_token_for_push diff --git a/src/semantic_release/cli/github_actions_output.py b/src/semantic_release/cli/github_actions_output.py index 7d7782922..9c4090d31 100644 --- a/src/semantic_release/cli/github_actions_output.py +++ b/src/semantic_release/cli/github_actions_output.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +from re import compile as regexp from semantic_release.globals import logger from semantic_release.version.version import Version @@ -13,9 +14,11 @@ def __init__( self, released: bool | None = None, version: Version | None = None, + commit_sha: str | None = None, ) -> None: self._released = released self._version = version + self._commit_sha = commit_sha @property def released(self) -> bool | None: @@ -45,12 +48,30 @@ def tag(self) -> str | None: def is_prerelease(self) -> bool | None: return self.version.is_prerelease if self.version is not None else None + @property + def commit_sha(self) -> str | None: + return self._commit_sha if self._commit_sha else None + + @commit_sha.setter + def commit_sha(self, value: str) -> None: + if not isinstance(value, str): + raise TypeError("output 'commit_sha' should be a string") + + if not regexp(r"^[0-9a-f]{40}$").match(value): + raise ValueError( + "output 'commit_sha' should be a valid 40-hex-character SHA" + ) + + self._commit_sha = value + def to_output_text(self) -> str: missing = set() if self.version is None: missing.add("version") if self.released is None: missing.add("released") + if self.released and self.commit_sha is None: + missing.add("commit_sha") if missing: raise ValueError( @@ -62,6 +83,7 @@ def to_output_text(self) -> str: "version": str(self.version), "tag": self.tag, "is_prerelease": str(self.is_prerelease).lower(), + "commit_sha": self.commit_sha if self.commit_sha else "", } return str.join("", [f"{key}={value!s}\n" for key, value in outputs.items()]) From efb9c17d1a5caac553af49ed92b381471a23b386 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 4 Jul 2025 08:39:07 -0600 Subject: [PATCH 4/4] docs(github-actions): add description of `commit_sha` GitHub Action output in docs --- .../automatic-releases/github-actions.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index d9a00f1f5..07d29bce4 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -535,6 +535,20 @@ A boolean value indicating whether a release was made. ---- +.. _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-version: ``version`` pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy