From 35f767405b377f6b18cbc0cfe66f3f6a49bbb678 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 00:44:03 -0600 Subject: [PATCH 1/4] ci(deps): bump `docker/build-push-action@v7.3.0` to `v7.4.0` (#1495) Authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: codejedi365 --- .github/workflows/validate.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 278c12582..352b89585 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -449,7 +449,7 @@ jobs: - name: Build | Action Container id: container-builder - uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 with: context: ${{ env.ACTION_SRC_DIR }} load: true # add to `docker images` From 0f4d414062c688233130183b0cde60c29caae23a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:23:18 -0600 Subject: [PATCH 2/4] build(deps-dev): expand `filelock` compatibility range to include `v4.0+` (#1494) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Signed-off-by: codejedi365 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 218cf93d6..917566aba 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,7 +74,7 @@ docs = [ ] test = [ "coverage[toml] ~= 7.0", - "filelock ~= 3.15", + "filelock >= 3.15, < 5.0", "cj365-flatdict ~= 5.0", "freezegun ~= 1.5", "pyyaml ~= 6.0", From 3cecbebb9a87ff6adf46e9334f18bd9c84655608 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Tue, 22 Sep 2026 01:25:05 -0600 Subject: [PATCH 3/4] feat(cmd-version): add optional `git --signoff` to version commits (#1492) NOTICE: With this release, we added a boolean flag, `semantic_release.signoff_commit`, as an available configuration option to toggle the addition of the `Signed-off-by` git trailer/footer message to release commits made by Python Semantic Release. Default is currently set to `False` but if you want to try this option, set this configuration setting to `True`. In a future major release, this setting will be set to `True` by default. Implements: #1441 Co-authored-by: codejedi365 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * test(cmd-version): add e2e test to evaluate applying commit sign off to release commit Co-authored-by: codejedi365 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> * docs(configuration): added `signoff_commit` setting definition Co-authored-by: codejedi365 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- docs/configuration/configuration.rst | 20 ++++ src/semantic_release/cli/commands/version.py | 2 + src/semantic_release/cli/config.py | 3 + src/semantic_release/gitproject.py | 3 + tests/e2e/cmd_version/test_version.py | 109 +++++++++++++++++++ 5 files changed, 137 insertions(+) diff --git a/docs/configuration/configuration.rst b/docs/configuration/configuration.rst index 6d5a9ea86..17981ab94 100644 --- a/docs/configuration/configuration.rst +++ b/docs/configuration/configuration.rst @@ -1227,6 +1227,26 @@ raise an error and exit. ---- +.. _config-signoff_commit: + +``signoff_commit`` +""""""""""""""""""" + +**Type:** ``bool`` + +When true, Python Semantic Release will add a ``Signed-off-by`` trailer to the version +commit it creates during :ref:`cmd-version`, using the configured :ref:`config-commit_author` +name and email. This is equivalent to running ``git commit --signoff`` and is commonly used +to comply with a `Developer Certificate of Origin`_ (DCO) requirement. + +.. _Developer Certificate of Origin: https://developercertificate.org/ + +This option has no effect when the ``--no-commit`` option is passed. + +**Default:** ``false`` + +---- + .. _config-tag_format: ``tag_format`` diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index d83be9894..e9a0805a3 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -481,6 +481,7 @@ def version( # noqa: C901 commit_message = runtime.commit_message major_on_zero = runtime.major_on_zero no_verify = runtime.no_git_verify + signoff_commit = runtime.signoff_commit opts = runtime.global_cli_options add_partial_tags = config.add_partial_tags gha_output = VersionGitHubActionsOutput( @@ -713,6 +714,7 @@ def version( # noqa: C901 message=commit_message.format(version=new_version), date=int(commit_date.timestamp()), no_verify=no_verify, + signoff=signoff_commit, noop=opts.noop, ) except GitCommitEmptyIndexError: diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 995b44f7c..ac8f58759 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -382,6 +382,7 @@ class RawConfig(BaseModel): repo_dir: Path = Field(default=cast("Path", "."), validate_default=True) remote: RemoteConfig = RemoteConfig() no_git_verify: bool = False + signoff_commit: bool = False tag_format: str = "v{version}" add_partial_tags: bool = False publish: PublishConfig = PublishConfig() @@ -635,6 +636,7 @@ class RuntimeContext: allow_zero_version: bool prerelease: bool no_git_verify: bool + signoff_commit: bool assets: List[str] commit_author: Actor commit_message: str @@ -986,6 +988,7 @@ def from_raw_config( # noqa: C901 global_cli_options=global_cli_options, masker=masker, no_git_verify=raw.no_git_verify, + signoff_commit=raw.signoff_commit, ) # credential masker self.apply_log_masking(self.masker) diff --git a/src/semantic_release/gitproject.py b/src/semantic_release/gitproject.py index cc86d33b5..83a442dff 100644 --- a/src/semantic_release/gitproject.py +++ b/src/semantic_release/gitproject.py @@ -174,6 +174,7 @@ def git_commit( date: int | None = None, commit_all: bool = False, no_verify: bool = False, + signoff: bool = False, noop: bool = False, ) -> None: git_args = dict( @@ -184,6 +185,7 @@ def git_commit( "m": message, "date": date, "no_verify": no_verify, + "signoff": signoff, }.items(), ) ) @@ -209,6 +211,7 @@ def git_commit( command += f"git commit -m '{indented_commit_message}'" command += "--all" if commit_all else "" command += "--no-verify" if no_verify else "" + command += "--signoff" if signoff else "" noop_report( indented( diff --git a/tests/e2e/cmd_version/test_version.py b/tests/e2e/cmd_version/test_version.py index e5d5bf2da..32b585444 100644 --- a/tests/e2e/cmd_version/test_version.py +++ b/tests/e2e/cmd_version/test_version.py @@ -155,6 +155,115 @@ def test_version_no_git_verify( assert post_mocker.call_count == 1 # vcs release creation occurred +@pytest.mark.parametrize( + "repo_result", + [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], +) +def test_version_signoff_commit( + repo_result: BuiltRepoResult, + run_cli: RunCliFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + mocked_git_fetch: MagicMock, + mocked_git_push: MagicMock, + post_mocker: Mocker, +): + """ + Given signoff_commit is enabled, when a version commit is made, then the commit + message includes a Signed-off-by trailer matching the configured commit author. + """ + repo = repo_result["repo"] + + # setup: set configuration setting + update_pyproject_toml("tool.semantic_release.signoff_commit", True) + repo.git.commit( + m="chore: adjust project configuration for signoff release commits", a=True + ) + # Fake an automated push to remote by updating the remote tracking branch + repo.git.update_ref( + f"refs/remotes/origin/{repo.active_branch.name}", + repo.head.commit.hexsha, + ) + + # Take measurement beforehand + head_sha_before = repo.head.commit.hexsha + tags_before = {tag.name for tag in repo.tags} + + # Execute + cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--patch"] + result = run_cli(cli_cmd[1:]) + + # Take measurement after the command + head_after = repo.head.commit + tags_after = {tag.name for tag in repo.tags} + tags_set_difference = set.difference(tags_after, tags_before) + commit_author = head_after.author + expected_signed_off_footer = ( + f"Signed-off-by: {commit_author.name} <{commit_author.email}>" + ) + + # Evaluate (normal release actions should have occurred when forced patch bump) + assert_successful_exit_code(result, cli_cmd) + assert [head_sha_before] == [head.hexsha for head in head_after.parents] + assert len(tags_set_difference) == 1 # A tag has been created + assert expected_signed_off_footer in str(head_after.message) + assert mocked_git_fetch.call_count == 1 # fetch called to check for remote changes + assert mocked_git_push.call_count == 2 # 1 for commit, 1 for tag + assert post_mocker.call_count == 1 # vcs release creation occurred + + +@pytest.mark.parametrize( + "repo_result", + [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)], +) +def test_version_signoff_commit_disabled( + repo_result: BuiltRepoResult, + run_cli: RunCliFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + mocked_git_fetch: MagicMock, + mocked_git_push: MagicMock, + post_mocker: Mocker, +): + """ + Given signoff_commit is disabled (the default), when a version commit is made, + then the commit message does not include a Signed-off-by trailer. + """ + repo = repo_result["repo"] + + # setup: explicitly set configuration setting to False + update_pyproject_toml("tool.semantic_release.signoff_commit", False) + repo.git.commit( + m="chore: adjust project configuration for non-signoff release commits", + a=True, + ) + # Fake an automated push to remote by updating the remote tracking branch + repo.git.update_ref( + f"refs/remotes/origin/{repo.active_branch.name}", + repo.head.commit.hexsha, + ) + + # Take measurement beforehand + head_sha_before = repo.head.commit.hexsha + tags_before = {tag.name for tag in repo.tags} + + # Execute + cli_cmd = [MAIN_PROG_NAME, VERSION_SUBCMD, "--patch"] + result = run_cli(cli_cmd[1:]) + + # Take measurement after the command + head_after = repo.head.commit + tags_after = {tag.name for tag in repo.tags} + tags_set_difference = set.difference(tags_after, tags_before) + + # Evaluate (normal release actions should have occurred when forced patch bump) + assert_successful_exit_code(result, cli_cmd) + assert [head_sha_before] == [head.hexsha for head in head_after.parents] + assert len(tags_set_difference) == 1 # A tag has been created + assert "Signed-off-by:" not in str(head_after.message) + assert mocked_git_fetch.call_count == 1 # fetch called to check for remote changes + assert mocked_git_push.call_count == 2 # 1 for commit, 1 for tag + assert post_mocker.call_count == 1 # vcs release creation occurred + + @pytest.mark.parametrize( "repo_result", [lazy_fixture(repo_w_trunk_only_conventional_commits.__name__)] ) From b700dbeb1f431a0fcc30a79f3f0869407fe07278 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Tue, 22 Sep 2026 07:41:23 +0000 Subject: [PATCH 4/4] chore: release v10.7.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 50 +++++++++++++++++++ .../automatic-releases/github-actions.rst | 4 +- docs/configuration/configuration.rst | 6 +-- pyproject.toml | 2 +- src/gh_action/requirements.txt | 2 +- 5 files changed, 57 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index babc2cc5b..ce625ee73 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,56 @@ CHANGELOG ========= +.. _changelog-v10.7.0: + +v10.7.0 (2026-09-22) +==================== + +✨ Features +----------- + +* **cmd-stamp**: Expand version stamp mechanism to support typed python variables, closes `#1443`_ + (`PR#1485`_, `947c57b`_) + +* **cmd-version**: Add optional ``git --signoff`` to version commits, closes `#1441`_ (`PR#1492`_, + `3cecbeb`_) + +📖 Documentation +---------------- + +* **configuration**: Added ``signoff_commit`` setting definition (`PR#1492`_, `3cecbeb`_) + +* **configuration**: Document Python type annotation support for ``version_variables`` (`PR#1485`_, + `947c57b`_) + +⚙️ Build System +---------------- + +* **deps**: Correct ``click`` range specification for python3.10+ (`PR#1486`_, `e4b3bad`_) + +* **deps**: Expand ``click`` dependency range to include ``v8.5.*`` (`PR#1489`_, `fa87c95`_) + +💡 Additional Release Information +--------------------------------- + +* **cmd-version**: With this release, we added a boolean flag, ``semantic_release.signoff_commit``, + as an available configuration option to toggle the addition of the ``Signed-off-by`` git + trailer/footer message to release commits made by Python Semantic Release. Default is currently + set to ``False`` but if you want to try this option, set this configuration setting to ``True``. + In a future major release, this setting will be set to ``True`` by default. + +.. _#1441: https://github.com/python-semantic-release/python-semantic-release/issues/1441 +.. _#1443: https://github.com/python-semantic-release/python-semantic-release/issues/1443 +.. _3cecbeb: https://github.com/python-semantic-release/python-semantic-release/commit/3cecbebb9a87ff6adf46e9334f18bd9c84655608 +.. _947c57b: https://github.com/python-semantic-release/python-semantic-release/commit/947c57b48bee85753cf40296b98529da26bf4b58 +.. _e4b3bad: https://github.com/python-semantic-release/python-semantic-release/commit/e4b3badb127b751d4946ccd7efb3ec0e8c159895 +.. _fa87c95: https://github.com/python-semantic-release/python-semantic-release/commit/fa87c95ffbdaae3b7516dcc355e57bd5f4632c1f +.. _PR#1485: https://github.com/python-semantic-release/python-semantic-release/pull/1485 +.. _PR#1486: https://github.com/python-semantic-release/python-semantic-release/pull/1486 +.. _PR#1489: https://github.com/python-semantic-release/python-semantic-release/pull/1489 +.. _PR#1492: https://github.com/python-semantic-release/python-semantic-release/pull/1492 + + .. _changelog-v10.6.2: v10.6.2 (2026-08-28) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index 0a1d1deea..9fb75eed6 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -893,14 +893,14 @@ to the GitHub Release Assets as well. - name: Action | Semantic Version Release id: release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@COMMIT_HASH # v10.6.2 + uses: python-semantic-release/python-semantic-release@COMMIT_HASH # v10.7.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} git_committer_name: "github-actions" git_committer_email: "actions@users.noreply.github.com" - name: Publish | Upload to GitHub Release Assets - uses: python-semantic-release/publish-action@COMMIT_HASH # v10.6.2 + uses: python-semantic-release/publish-action@COMMIT_HASH # v10.7.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/docs/configuration/configuration.rst b/docs/configuration/configuration.rst index 17981ab94..b9e5e2367 100644 --- a/docs/configuration/configuration.rst +++ b/docs/configuration/configuration.rst @@ -1397,7 +1397,7 @@ version numbers. ] First, the ``__version__`` variable in ``src/semantic_release/__init__.py`` will be updated -with the next version using the `SemVer`_ number format. As of $NEW_RELEASE_TAG, this works +with the next version using the `SemVer`_ number format. As of v10.7.0, this works both with and without a Python type annotation on the variable: .. code-block:: diff @@ -1407,7 +1407,7 @@ both with and without a Python type annotation on the variable: - __version__ = "0.1.0" + __version__ = "0.2.0" - # or with a type annotation ($NEW_RELEASE_TAG or greater): + # or with a type annotation (v10.7.0 or greater): - __version__: str = "0.1.0" + __version__: str = "0.2.0" @@ -1475,7 +1475,7 @@ The regular expression generated from the ``version_variables`` definition will: the symbol. As of v10.0.0, a double-equals (``==``) operator is also supported as a valid operand symbol. As of v10.5.0, PSR can omit all operands as long as there is at least one whitespace character between the variable name and the version. - As of $NEW_RELEASE_TAG, an optional Python-style type annotation (e.g. ``: str``, + As of v10.7.0, an optional Python-style type annotation (e.g. ``: str``, ``: typing.Final[str]``) between the variable name and the assignment operator is also supported. diff --git a/pyproject.toml b/pyproject.toml index 917566aba..0887a0668 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "10.6.2" +version = "10.7.0" description = "Automatic Semantic Versioning for Python projects" requires-python = "~= 3.8" license = { text = "MIT" } diff --git a/src/gh_action/requirements.txt b/src/gh_action/requirements.txt index 629cc6bc5..bf57fb8da 100644 --- a/src/gh_action/requirements.txt +++ b/src/gh_action/requirements.txt @@ -1 +1 @@ -python-semantic-release == 10.6.2 +python-semantic-release == 10.7.0