From e70325e2ad41fb16d33f0dfc179955a3c5d38604 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 03:46:51 -0600 Subject: [PATCH 01/37] chore(changelog): update changelog spelling, links, & shorten breaking change descriptions (#1254) * chore(docs): fix references within docs --- CHANGELOG.rst | 193 +++++------------- docs/api/commands.rst | 2 +- .../automatic-releases/travis.rst | 2 +- 3 files changed, 54 insertions(+), 143 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 61fa77784..ec7decbe1 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -89,7 +89,7 @@ v10.0.0 (2025-05-25) * **github-actions**: Update ``python-semantic-release/publish-action`` parameter notes (`c4d45ec`_) -* **github-actions**: Update PSR action parameter documenation (`a082896`_) +* **github-actions**: Update PSR action parameter documentation (`a082896`_) * **upgrading**: Re-locate version upgrade guides into ``Upgrading PSR`` (`a5f5e04`_) @@ -164,13 +164,14 @@ v10.0.0 (2025-05-25) configuration. * **github-action**: The ``root_options`` action input parameter has been removed because it created - a command injection vulernability for arbitrary code to execute within the container context of + a command injection vulnerability for arbitrary code to execute within the container context of the GitHub action if a command injection code was provided as part of the ``root_options`` parameter string. To eliminate the vulnerability, each relevant option that can be provided to ``semantic-release`` has been individually added as its own parameter and will be processed individually to prevent command injection. Please review our `Github Actions Configuration`__ page - on the Python Semantic Release Documentation website to review the newly available configuration - options that replace the ``root_options`` parameter. + to review the newly available configuration options that replace the ``root_options`` parameter. + + __ https://github.com/python-semantic-release/python-semantic-release/blob/v10.0.0/docs/configuration/automatic-releases/github-actions.rst * **parser-conventional**: Any breaking change footer messages that the conventional commit parser detects will now be removed from the ``commit.descriptions[]`` list but maintained in and only in @@ -178,146 +179,56 @@ v10.0.0 (2025-05-25) the commit message but that was redundant as the default changelog now handles breaking change footers in its own section. -* **parser-conventional**: Any issue resolution footers that the parser detects will now be removed - from the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the - commit message but now that the parser pulls out the issue numbers the numbers will be included in - the ``commit.linked_issues`` tuple for user extraction in any changelog generation. - -* **parser-conventional**: Any release notice footer messages that the commit parser detects will - now be removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-conventional**: Generally, a pull request or merge request number reference is included - in the subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks - for this reference and extracts it into the ``commit.linked_merge_request`` and the - ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out - individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list - (ie. the subject line) so that changelog macros do not have to replace the text but instead only - append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator - (`#` or ``!``). - -* **parser-conventional**: The configuration setting ``commit_parser_options.ignore_merge_commits`` - is now set to ``true`` by default. The feature to ignore squash commits was introduced in - ``v9.18.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking - update. The ignore merge commits feature prevents additional unnecessary processing on a commit - message that likely will not match a commit message syntax. Most merge commits are syntactically - pre-defined by Git or Remote Version Control System (ex. GitHub, etc.) and do not follow a commit - convention (nor should they). The larger issue with merge commits is that they ultimately are a - full copy of all the changes that were previously created and committed. The merge commit itself - ensures that the previous commit tree is maintained in history, therefore the commit message - always exists. If merge commits are parsed, it generally creates duplicate messages that will end - up in your changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-conventional**: The configuration setting ``commit_parser_options.parse_squash_commits`` - is now set to ``true`` by default. The feature to parse squash commits was introduced in - ``v9.17.0`` and was originally set to ``false`` to prevent unexpected results on a non-breaking - update. The parse squash commits feature attempts to find additional commits of the same commit - type within the body of a single commit message. When squash commits are found, Python Semantic - Release will separate out each commit into its own artificial commit object and parse them - individually. This potentially can change the resulting version bump if a larger bump was detected - within the squashed components. It also allows for the changelog and release notes to separately - order and display each commit as originally written. If this is not desired, you will need to - update your configuration to change the new setting to ``false``. - -* **parser-emoji**: Any issue resolution footers that the parser detects will now be removed from - the ``commit.descriptions[]`` list. Previously, the descriptions included all text from the commit - message but now that the parser pulls out the issue numbers the numbers will be included in the - ``commit.linked_issues`` tuple for user extraction in any changelog generation. - -* **parser-emoji**: Any release notice footer messages that the emoji commit parser detects will now - be removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-emoji**: Generally, a pull request or merge request number reference is included in the - subject line at the end within parentheses on some common VCS's (e.g. GitHub). PSR now looks for - these references and extract it into the ``commit.linked_merge_request`` field of a commit object. - Since this is now pulled out individually, it is cleaner to remove this from the first line of the - ``commit.descriptions`` list (ie. the subject line) so that changelog macros do not have to - replace the text but instead only append a PR/MR link to the end of the line. The reference will - maintain the PR/MR prefix indicator (e.g. ``#`` or ``!``). - -* **parser-emoji**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now - set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore - merge commits feature prevents additional unnecessary processing on a commit message that likely - will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or - Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should - they). The larger issue with merge commits is that they ultimately are a full copy of all the - changes that were previously created and committed. The merge commit itself ensures that the - previous commit tree is maintained in history, therefore the commit message always exists. If - merge commits are parsed, it generally creates duplicate messages that will end up in your - changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-emoji**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now - set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse - squash commits feature attempts to find additional commits of the same commit type within the body - of a single commit message. When squash commits are found, Python Semantic Release will separate - out each commit into its own artificial commit object and parse them individually. This - potentially can change the resulting version bump if a larger bump was detected within the - squashed components. It also allows for the changelog and release notes to separately order and - display each commit as originally written. If this is not desired, you will need to update your - configuration to change the new setting to ``false``. +* **parser-conventional, parser-emoji, parser-scipy**: Any issue resolution footers that the parser + detects will now be removed from the ``commit.descriptions[]`` list. Previously, the descriptions + included all text from the commit message but now that the parser pulls out the issue numbers the + numbers will be included in the ``commit.linked_issues`` tuple for user extraction in any + changelog generation. -* **parser-scipy**: Any issue resolution footers that the parser detects will now be removed from - the commit.descriptions[] list. Previously, the descriptions included all text from the commit - message but now that the parser pulls out the issue numbers the numbers will be included in the - commit.linked_issues tuple for user extraction in any changelog generation. - -* **parser-scipy**: Any release notice footer messages that the commit parser detects will now be - removed from the ``commit.descriptions[]`` list but maintained in and only in the - ``commit.notices[]`` list. Previously, the descriptions included all text from the commit message - but that was redundant as the default changelog now handles release notice footers in its own - section. - -* **parser-scipy**: Generally, a pull request or merge request number reference is included in the - subject line at the end within parentheses on some common VCS's like GitHub. PSR now looks for - this reference and extracts it into the ``commit.linked_merge_request`` and the - ``commit.linked_pull_request`` attributes of a commit object. Since this is now pulled out - individually, it is cleaner to remove this from the first line of the ``commit.descriptions`` list - (ie. the subject line) so that changelog macros do not have to replace the text but instead only - append a PR/MR link to the end of the line. The reference does maintain the PR/MR prefix indicator - (`#` or ``!``). - -* **parser-scipy**: The configuration setting ``commit_parser_options.ignore_merge_commits`` is now - set to ``true`` by default. The feature to ignore squash commits was introduced in ``v9.18.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The ignore - merge commits feature prevents additional unnecessary processing on a commit message that likely - will not match a commit message syntax. Most merge commits are syntactically pre-defined by Git or - Remote Version Control System (ex. GitHub, etc.) and do not follow a commit convention (nor should - they). The larger issue with merge commits is that they ultimately are a full copy of all the - changes that were previously created and committed. The merge commit itself ensures that the - previous commit tree is maintained in history, therefore the commit message always exists. If - merge commits are parsed, it generally creates duplicate messages that will end up in your - changelog, which is less than desired in most cases. If you have previously used the - ``changelog.exclude_commit_patterns`` functionality to ignore merge commit messages then you will - want this setting set to ``true`` to improve parsing speed. You can also now remove the merge - commit exclude pattern from the list as well to improve parsing speed. If this functionality is - not desired, you will need to update your configuration to change the new setting to ``false``. - -* **parser-scipy**: The configuration setting ``commit_parser_options.parse_squash_commits`` is now - set to ``true`` by default. The feature to parse squash commits was introduced in ``v9.17.0`` and - was originally set to ``false`` to prevent unexpected results on a non-breaking update. The parse - squash commits feature attempts to find additional commits of the same commit type within the body - of a single commit message. When squash commits are found, Python Semantic Release will separate - out each commit into its own artificial commit object and parse them individually. This - potentially can change the resulting version bump if a larger bump was detected within the - squashed components. It also allows for the changelog and release notes to separately order and - display each commit as originally written. If this is not desired, you will need to update your +* **parser-conventional, parser-emoji, parser-scipy**: Any release notice footer messages that the + commit parser detects will now be removed from the ``commit.descriptions[]`` list but maintained + in and only in the ``commit.notices[]`` list. Previously, the descriptions included all text from + the commit message but that was redundant as the default changelog now handles release notice + footers in its own section. + +* **parser-conventional, parser-emoji, parser-scipy**: Generally, a pull request or merge request + number reference is included in the subject line at the end within parentheses on some common + VCS's like GitHub. PSR now looks for this reference and extracts it into the + ``commit.linked_merge_request`` and the ``commit.linked_pull_request`` attributes of a commit + object. Since this is now pulled out individually, it is cleaner to remove this from the first + line of the ``commit.descriptions`` list (ie. the subject line) so that changelog macros do not + have to replace the text but instead only append a PR/MR link to the end of the line. The + reference does maintain the PR/MR prefix indicator (`#` or ``!``). + +* **parser-conventional, parser-emoji, parser-scipy**: The configuration setting + ``commit_parser_options.ignore_merge_commits`` is now set to ``true`` by default. The feature to + ignore squash commits was introduced in ``v9.18.0`` and was originally set to ``false`` to + prevent unexpected results on a non-breaking update. The ignore merge commits feature prevents + additional unnecessary processing on a commit message that likely will not match a commit message + syntax. Most merge commits are syntactically pre-defined by Git or Remote Version Control System + (ex. GitHub, etc.) and do not follow a commit convention (nor should they). The larger issue with + merge commits is that they ultimately are a full copy of all the changes that were previously + created and committed. The merge commit itself ensures that the previous commit tree is + maintained in history, therefore the commit message always exists. If merge commits are parsed, + it generally creates duplicate messages that will end up in your changelog, which is less than + desired in most cases. If you have previously used the ``changelog.exclude_commit_patterns`` + functionality to ignore merge commit messages then you will want this setting set to ``true`` to + improve parsing speed. You can also now remove the merge commit exclude pattern from the list as + well to improve parsing speed. If this functionality is not desired, you will need to update your configuration to change the new setting to ``false``. +* **parser-conventional, parser-emoji, parser-scipy**: The configuration setting + ``commit_parser_options.parse_squash_commits`` is now set to ``true`` by default. The feature to + parse squash commits was introduced in ``v9.17.0`` and was originally set to ``false`` to prevent + unexpected results on a non-breaking update. The parse squash commits feature attempts to find + additional commits of the same commit type within the body of a single commit message. When + squash commits are found, Python Semantic Release will separate out each commit into its own + artificial commit object and parse them individually. This potentially can change the resulting + version bump if a larger bump was detected within the squashed components. It also allows for the + changelog and release notes to separately order and display each commit as originally written. If + this is not desired, you will need to update your configuration to change the new setting to + ``false``. + .. _#733: https://github.com/python-semantic-release/python-semantic-release/issues/733 .. _080e4bc: https://github.com/python-semantic-release/python-semantic-release/commit/080e4bcb14048a2dd10445546a7ee3159b3ab85c .. _0bed906: https://github.com/python-semantic-release/python-semantic-release/commit/0bed9069df67ae806ad0a15f8434ac4efcc6ba31 diff --git a/docs/api/commands.rst b/docs/api/commands.rst index d99a40152..344c7f5f4 100644 --- a/docs/api/commands.rst +++ b/docs/api/commands.rst @@ -496,4 +496,4 @@ corresponding release is found in the remote VCS, then Python Semantic Release w attempt to create one. If using this option, the relevant authentication token *must* be supplied via the -relevant environment variable. For more information, see :ref:`index-creating-vcs-releases`. +relevant environment variable. diff --git a/docs/configuration/automatic-releases/travis.rst b/docs/configuration/automatic-releases/travis.rst index 5be380975..60ee68ce8 100644 --- a/docs/configuration/automatic-releases/travis.rst +++ b/docs/configuration/automatic-releases/travis.rst @@ -18,7 +18,7 @@ You will need to set up an environment variable in Travis. An easy way to do tha is to go to the settings page for your package and add it there. Make sure that the secret toggle is set correctly. -You need to set the :ref:`GH_TOKEN ` environment +You need to set the :ref:`GH_TOKEN ` environment variable with a personal access token for Github. It will need either ``repo`` or ``public_repo`` scope depending on whether the repository is private or public. From bba903ce21306059aebc5c0e8cd4e677a808ff7e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 03:34:06 -0600 Subject: [PATCH 02/37] ci(deps): bump `python-semantic-release@v9.21.0` action to `v10.0.0` --- .github/workflows/cicd.yml | 4 ++-- .github/workflows/validate.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 358dff3ab..2dc956b73 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -145,10 +145,10 @@ jobs: - name: Release | Python Semantic Release id: release - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@092ace20f4ebed6a656da54b499076f1a5b803c8 # v10.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} - root_options: "-v" + verbosity: 1 build: false - name: Release | Add distribution artifacts to GitHub Release Assets diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index a55e468fd..1e93ff0d6 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -112,10 +112,10 @@ jobs: - name: Build | Build next version artifacts id: version - uses: python-semantic-release/python-semantic-release@v9.21.1 + uses: python-semantic-release/python-semantic-release@092ace20f4ebed6a656da54b499076f1a5b803c8 # v10.0.0 with: github_token: "" - root_options: "-v" + verbosity: 1 build: true changelog: true commit: false From 96a9503d4e33bb23290760c79e341f60dd767761 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 09:17:03 +0000 Subject: [PATCH 03/37] ci(deps): bump `python-semantic-release/publish-action@v9.21.1` to `v10.0.0` --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 2dc956b73..8c8b45c45 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -152,7 +152,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@v9.21.1 + uses: python-semantic-release/publish-action@d62706ce15a7c98325c51a3e5cc789fdbe843e5a # v10.0.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} From 2803676cf26c52177fa98d9144934853744a22bb Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 03:53:27 -0600 Subject: [PATCH 04/37] fix(github-actions): bump the github-actions dependency to `v10.0.0` (#1255) --- src/gh_action/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gh_action/requirements.txt b/src/gh_action/requirements.txt index 1aae03384..2fabec60d 100644 --- a/src/gh_action/requirements.txt +++ b/src/gh_action/requirements.txt @@ -1 +1 @@ -python-semantic-release == 9.21.1 +python-semantic-release == 10.0.0 From 22fd6792e9de082f2769abf1069ac62250ff33a0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 17 May 2025 16:45:12 -0600 Subject: [PATCH 05/37] chore(changelog): update PSR template after v10 strips PR numbers from subject lines --- config/release-templates/.components/macros.md.j2 | 11 +++++------ config/release-templates/.components/macros.rst.j2 | 5 +---- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/config/release-templates/.components/macros.md.j2 b/config/release-templates/.components/macros.md.j2 index 7332fc741..89cd84cb2 100644 --- a/config/release-templates/.components/macros.md.j2 +++ b/config/release-templates/.components/macros.md.j2 @@ -58,13 +58,12 @@ ) %}{# #}{% if commit.linked_merge_request != "" -%}{% set pr_num = commit.linked_merge_request -%}{# # TODO: breaking change v10, remove summary line replacers as PSR will do it for us -#}{% set summary_line = summary_line | replace("(pull request ", "(") | replace("(" ~ pr_num ~ ")", "") | trim -%}{# - # # Add PR references with a link to the PR +%}{# # Add PR references with a link to the PR #}{% set _ = link_references.append( - format_link(pr_num | pull_request_url, "PR" ~ pr_num) + format_link( + commit.linked_merge_request | pull_request_url, + "PR" ~ commit.linked_merge_request + ) ) %}{% endif %}{# diff --git a/config/release-templates/.components/macros.rst.j2 b/config/release-templates/.components/macros.rst.j2 index a0621c252..779571c02 100644 --- a/config/release-templates/.components/macros.rst.j2 +++ b/config/release-templates/.components/macros.rst.j2 @@ -131,10 +131,7 @@ %}{% endif %}{# #}{% if commit.linked_merge_request != "" -%}{# # TODO: breaking change v10, remove summary line replacers as PSR will do it for us -#}{% set summary_line = summary_line | replace("(pull request ", "(") | replace("(" ~ commit.linked_merge_request ~ ")", "") | trim -%}{# - # # Add PR references with a link to the PR +%}{# # Add PR references with a link to the PR #}{% set _ = link_references.append("`PR%s`_" | format(commit.linked_merge_request)) %}{% endif %}{# From 74192b4ff4c7e7e5f0b577e482b9e564514d3be2 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 11:01:44 -0600 Subject: [PATCH 06/37] chore(changelog): update changelog with latest template change --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ec7decbe1..bdf5c855b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -420,7 +420,7 @@ v9.19.0 (2025-02-10) * Update references to Angular parser to Conventional Commit Parser (`PR#1177`_, `27ddf84`_) -💡 ADDITIONAL RELEASE INFORMATION +💡 Additional Release Information --------------------------------- * **parser-conventional**: The 'angular' commit parser has been renamed to 'conventional' to match From ceca7615915411ba843c14db135f5fac79fa8afc Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 11:31:05 -0600 Subject: [PATCH 07/37] chore(changelog): add reference to the v10 migration guide --- CHANGELOG.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index bdf5c855b..c2f11439b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -113,6 +113,12 @@ v10.0.0 (2025-05-25) 💥 Breaking Changes ------------------- +.. seealso:: + *For a summarized walkthrough, check out our* |v10 migration guide|_ *as well.* + +.. _v10 migration guide: ../upgrading/10-upgrade.html +.. |v10 migration guide| replace:: *v10 migration guide* + * **changelog-md**: The default Markdown changelog template and release notes template will no longer print out the entire commit message contents, instead, it will only print the commit subject line. This comes to meet the high demand of better formatted changelogs and requests for From 613640fd2cae3a90203537bcaed0077721d8dd76 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 11:52:33 -0600 Subject: [PATCH 08/37] chore(release-notes): update templates to support `first_release` creation --- .../.components/first_release.md.j2 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 config/release-templates/.components/first_release.md.j2 diff --git a/config/release-templates/.components/first_release.md.j2 b/config/release-templates/.components/first_release.md.j2 new file mode 100644 index 000000000..d0e44f7cc --- /dev/null +++ b/config/release-templates/.components/first_release.md.j2 @@ -0,0 +1,18 @@ +{# EXAMPLE: + +## vX.X.X (YYYY-MMM-DD) + +_This release is published under the MIT License._ # Release Notes Only + +- Initial Release + +#}{{ +"## %s (%s)\n" | format( + release.version.as_semver_tag(), + release.tagged_date.strftime("%Y-%m-%d") +) +}}{% if license_name is defined and license_name +%}{{ "\n_This release is published under the %s License._\n" | format(license_name) +}}{% endif +%} +- Initial Release From d099c20aad32201f1609a35765ca26f6562f4c6e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 14:15:50 -0600 Subject: [PATCH 09/37] test(fixtures): always run e2e tests in very verbose mode for test failure debugging (#1258) * test(e2e): update tests to ignore logging messages when validating stderr output --- tests/conftest.py | 3 +- tests/e2e/cmd_version/test_version.py | 7 +++-- tests/e2e/cmd_version/test_version_print.py | 31 +++++++++++++------- tests/e2e/cmd_version/test_version_strict.py | 7 +++-- tests/e2e/conftest.py | 21 ++++++++++++- tests/e2e/test_main.py | 17 ++++++++--- 6 files changed, 66 insertions(+), 20 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 16298e98b..2d081f62b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -207,10 +207,11 @@ def _run_cli( cli_runner = CliRunner(mix_stderr=False) env_vars = {**clean_os_environment, **(env or {})} + args = ["-vv", *(argv or [])] with mock.patch.dict(os.environ, env_vars, clear=True): # run the CLI with the provided arguments - return cli_runner.invoke(main, args=(argv or []), **(invoke_kwargs or {})) + return cli_runner.invoke(main, args=args, **(invoke_kwargs or {})) return _run_cli diff --git a/tests/e2e/cmd_version/test_version.py b/tests/e2e/cmd_version/test_version.py index 5cb9700cf..0af19f8a3 100644 --- a/tests/e2e/cmd_version/test_version.py +++ b/tests/e2e/cmd_version/test_version.py @@ -26,6 +26,7 @@ from requests_mock import Mocker from tests.conftest import RunCliFn + from tests.e2e.conftest import StripLoggingMessagesFn from tests.fixtures.example_project import GetWheelFileFn, UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult, GetVersionsFromRepoBuildDefFn @@ -151,6 +152,7 @@ def test_version_on_nonrelease_branch( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): """ Given repo is on a non-release branch, @@ -175,7 +177,7 @@ def test_version_on_nonrelease_branch( # Evaluate (expected -> actual) assert_successful_exit_code(result, cli_cmd) assert not result.stdout - assert expected_error_msg == result.stderr + assert expected_error_msg == strip_logging_messages(result.stderr) # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) tags_after = sorted([tag.name for tag in repo.tags]) @@ -196,6 +198,7 @@ def test_version_on_last_release( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): """ Given repo is on the last release version, @@ -229,7 +232,7 @@ def test_version_on_last_release( # Evaluate (expected -> actual) assert_successful_exit_code(result, cli_cmd) assert f"{latest_release_version}\n" == result.stdout - assert f"{expected_error_msg}\n" == result.stderr + assert f"{expected_error_msg}\n" == strip_logging_messages(result.stderr) # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) assert repo_status_before == repo_status_after diff --git a/tests/e2e/cmd_version/test_version_print.py b/tests/e2e/cmd_version/test_version_print.py index 4efd1e02f..18c0fc5f7 100644 --- a/tests/e2e/cmd_version/test_version_print.py +++ b/tests/e2e/cmd_version/test_version_print.py @@ -35,6 +35,7 @@ from requests_mock import Mocker from tests.conftest import RunCliFn + from tests.e2e.conftest import StripLoggingMessagesFn from tests.fixtures.git_repo import ( BuiltRepoResult, GetCfgValueFromDefFn, @@ -442,6 +443,7 @@ def test_version_print_last_released_prints_version( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] latest_release_version = get_versions_from_repo_build_def( @@ -465,7 +467,7 @@ def test_version_print_last_released_prints_version( # Evaluate assert_successful_exit_code(result, cli_cmd) - assert not result.stderr + assert not strip_logging_messages(result.stderr) assert f"{latest_release_version}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -493,6 +495,7 @@ def test_version_print_last_released_prints_released_if_commits( mocked_git_push: MagicMock, post_mocker: Mocker, file_in_repo: str, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] latest_release_version = get_versions_from_repo_build_def( @@ -520,7 +523,7 @@ def test_version_print_last_released_prints_released_if_commits( # Evaluate assert_successful_exit_code(result, cli_cmd) - assert not result.stderr + assert not strip_logging_messages(result.stderr) assert f"{latest_release_version}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -585,6 +588,7 @@ def test_version_print_last_released_on_detached_head( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] latest_release_version = get_versions_from_repo_build_def( @@ -611,7 +615,7 @@ def test_version_print_last_released_on_detached_head( # Evaluate (expected -> actual) assert_successful_exit_code(result, cli_cmd) - assert not result.stderr + assert not strip_logging_messages(result.stderr) assert f"{latest_release_version}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -632,6 +636,7 @@ def test_version_print_last_released_on_nonrelease_branch( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] latest_release_version = get_versions_from_repo_build_def( @@ -658,7 +663,7 @@ def test_version_print_last_released_on_nonrelease_branch( # Evaluate (expected -> actual) assert_successful_exit_code(result, cli_cmd) - assert not result.stderr + assert not strip_logging_messages(result.stderr) assert f"{latest_release_version}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -688,6 +693,7 @@ def test_version_print_last_released_tag_prints_correct_tag( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] repo_def = repo_result["definition"] @@ -712,7 +718,7 @@ def test_version_print_last_released_tag_prints_correct_tag( # Evaluate assert_successful_exit_code(result, cli_cmd) - assert not result.stderr + assert not strip_logging_messages(result.stderr) assert f"{latest_release_tag}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -748,6 +754,7 @@ def test_version_print_last_released_tag_prints_released_if_commits( mocked_git_push: MagicMock, post_mocker: Mocker, file_in_repo: str, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] repo_def = repo_result["definition"] @@ -776,7 +783,7 @@ def test_version_print_last_released_tag_prints_released_if_commits( # Evaluate assert_successful_exit_code(result, cli_cmd) - assert not result.stderr + assert not strip_logging_messages(result.stderr) assert f"{latest_release_tag}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -850,6 +857,7 @@ def test_version_print_last_released_tag_on_detached_head( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] repo_def = repo_result["definition"] @@ -877,7 +885,7 @@ def test_version_print_last_released_tag_on_detached_head( # Evaluate (expected -> actual) assert_successful_exit_code(result, cli_cmd) - assert not result.stderr + assert not strip_logging_messages(result.stderr) assert f"{latest_release_tag}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -907,6 +915,7 @@ def test_version_print_last_released_tag_on_nonrelease_branch( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] repo_def = repo_result["definition"] @@ -934,7 +943,7 @@ def test_version_print_last_released_tag_on_nonrelease_branch( # Evaluate (expected -> actual) assert_successful_exit_code(result, cli_cmd) - assert not result.stderr + assert not strip_logging_messages(result.stderr) assert f"{last_release_tag}\n" == result.stdout # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) @@ -961,6 +970,7 @@ def test_version_print_next_version_fails_on_detached_head( get_commit_def_fn: GetCommitDefFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] expected_error_msg = ( @@ -994,7 +1004,7 @@ def test_version_print_next_version_fails_on_detached_head( # Evaluate (expected -> actual) assert_exit_code(1, result, cli_cmd) assert not result.stdout - assert f"{expected_error_msg}\n" == result.stderr + assert f"{expected_error_msg}\n" == strip_logging_messages(result.stderr) # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) assert repo_status_before == repo_status_after @@ -1020,6 +1030,7 @@ def test_version_print_next_tag_fails_on_detached_head( get_commit_def_fn: GetCommitDefFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): repo = repo_result["repo"] expected_error_msg = ( @@ -1053,7 +1064,7 @@ def test_version_print_next_tag_fails_on_detached_head( # Evaluate (expected -> actual) assert_exit_code(1, result, cli_cmd) assert not result.stdout - assert f"{expected_error_msg}\n" == result.stderr + assert f"{expected_error_msg}\n" == strip_logging_messages(result.stderr) # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) assert repo_status_before == repo_status_after diff --git a/tests/e2e/cmd_version/test_version_strict.py b/tests/e2e/cmd_version/test_version_strict.py index 951a9966f..a41998ded 100644 --- a/tests/e2e/cmd_version/test_version_strict.py +++ b/tests/e2e/cmd_version/test_version_strict.py @@ -17,6 +17,7 @@ from requests_mock import Mocker from tests.conftest import RunCliFn + from tests.e2e.conftest import StripLoggingMessagesFn from tests.fixtures.git_repo import BuiltRepoResult, GetVersionsFromRepoBuildDefFn @@ -30,6 +31,7 @@ def test_version_already_released_when_strict( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): """ Given repo has no new changes since the last release, @@ -60,7 +62,7 @@ def test_version_already_released_when_strict( # Evaluate assert_exit_code(2, result, cli_cmd) assert f"{latest_release_version}\n" == result.stdout - assert f"{expected_error_msg}\n" == result.stderr + assert f"{expected_error_msg}\n" == strip_logging_messages(result.stderr) # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) assert repo_status_before == repo_status_after @@ -78,6 +80,7 @@ def test_version_on_nonrelease_branch_when_strict( run_cli: RunCliFn, mocked_git_push: MagicMock, post_mocker: Mocker, + strip_logging_messages: StripLoggingMessagesFn, ): """ Given repo is on a non-release branch, @@ -103,7 +106,7 @@ def test_version_on_nonrelease_branch_when_strict( # Evaluate assert_exit_code(2, result, cli_cmd) assert not result.stdout - assert expected_error_msg == result.stderr + assert expected_error_msg == strip_logging_messages(result.stderr) # assert nothing else happened (no code changes, no commit, no tag, no push, no vcs release) tags_after = sorted([tag.name for tag in repo.tags]) diff --git a/tests/e2e/conftest.py b/tests/e2e/conftest.py index b64d5aecf..209f3654e 100644 --- a/tests/e2e/conftest.py +++ b/tests/e2e/conftest.py @@ -2,7 +2,7 @@ import os from pathlib import Path -from re import IGNORECASE, compile as regexp +from re import IGNORECASE, MULTILINE, compile as regexp from typing import TYPE_CHECKING from unittest.mock import MagicMock @@ -47,6 +47,9 @@ class RetrieveRuntimeContextFn(Protocol): def __call__(self, repo: Repo) -> RuntimeContext: ... + class StripLoggingMessagesFn(Protocol): + def __call__(self, log: str) -> str: ... + @pytest.hookimpl(tryfirst=True) def pytest_collection_modifyitems(items: list[pytest.Item]) -> None: @@ -116,6 +119,22 @@ def _retrieve_runtime_context(repo: Repo) -> RuntimeContext: return _retrieve_runtime_context +@pytest.fixture(scope="session") +def strip_logging_messages() -> StripLoggingMessagesFn: + """Fixture to strip logging messages from the output.""" + # Log levels match SemanticReleaseLogLevel enum values + logger_msg_pattern = regexp( + r"^\s*(?:\[\d\d:\d\d:\d\d\])?\s*(FATAL|CRITICAL|ERROR|WARNING|INFO|DEBUG|SILLY).*?\n(?:\s+\S.*?\n)*(?!\n[ ]{11})", + MULTILINE, + ) + + def _strip_logging_messages(log: str) -> str: + # Make sure it ends with a newline + return logger_msg_pattern.sub("", log.rstrip("\n") + "\n") + + return _strip_logging_messages + + @pytest.fixture(scope="session") def long_hash_pattern() -> Pattern: return regexp(r"\b([0-9a-f]{40})\b", IGNORECASE) diff --git a/tests/e2e/test_main.py b/tests/e2e/test_main.py index ff290146e..8ce3c58a5 100644 --- a/tests/e2e/test_main.py +++ b/tests/e2e/test_main.py @@ -8,6 +8,7 @@ import git import pytest +from click.testing import CliRunner from pytest_lazy_fixtures.lazy_fixture import lf as lazy_fixture from semantic_release import __version__ @@ -20,6 +21,7 @@ from pathlib import Path from tests.conftest import RunCliFn + from tests.e2e.conftest import StripLoggingMessagesFn from tests.fixtures.example_project import ExProjectDir, UpdatePyprojectTomlFn from tests.fixtures.git_repo import BuiltRepoResult @@ -59,8 +61,13 @@ def test_main_prints_version_and_exits(run_cli: RunCliFn): assert result.output == f"semantic-release, version {__version__}\n" -def test_main_no_args_prints_help_text(run_cli: RunCliFn): - assert_successful_exit_code(run_cli(), [MAIN_PROG_NAME]) +def test_main_no_args_passes_w_help_text(): + from semantic_release.cli.commands.main import main + + cli_cmd = [MAIN_PROG_NAME] + result = CliRunner().invoke(main, prog_name=cli_cmd[0]) + assert_successful_exit_code(result, cli_cmd) + assert "Usage: " in result.output @pytest.mark.parametrize( @@ -210,7 +217,9 @@ def test_errors_when_config_file_does_not_exist_and_passed_explicitly( @pytest.mark.usefixtures(repo_w_no_tags_conventional_commits.__name__) def test_errors_when_config_file_invalid_configuration( - run_cli: RunCliFn, update_pyproject_toml: UpdatePyprojectTomlFn + run_cli: RunCliFn, + update_pyproject_toml: UpdatePyprojectTomlFn, + strip_logging_messages: StripLoggingMessagesFn, ): # Setup update_pyproject_toml("tool.semantic_release.remote.type", "invalidType") @@ -220,7 +229,7 @@ def test_errors_when_config_file_invalid_configuration( result = run_cli(cli_cmd[1:]) # preprocess results - stderr_lines = result.stderr.splitlines() + stderr_lines = strip_logging_messages(result.stderr).splitlines() # Evaluate assert_exit_code(1, result, cli_cmd) From b15f3cb1a0529a32eb149ce25d9a60ca4448b88e Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 25 May 2025 20:32:16 +0000 Subject: [PATCH 10/37] 10.0.1 Automatically generated by python-semantic-release --- CHANGELOG.rst | 15 +++++++++++++++ .../automatic-releases/github-actions.rst | 18 +++++++++--------- pyproject.toml | 2 +- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c2f11439b..6df94b9f6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,21 @@ CHANGELOG ========= +.. _changelog-v10.0.1: + +v10.0.1 (2025-05-25) +==================== + +🪲 Bug Fixes +------------ + +* **github-actions**: Bump the github-actions dependency to ``v10.0.0`` (#1255) (`PR#1255`_, + `2803676`_) + +.. _2803676: https://github.com/python-semantic-release/python-semantic-release/commit/2803676cf26c52177fa98d9144934853744a22bb +.. _PR#1255: https://github.com/python-semantic-release/python-semantic-release/pull/1255 + + .. _changelog-v10.0.0: v10.0.0 (2025-05-25) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index d8a8bd012..7c2750491 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -382,7 +382,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v10.0.0 + - uses: python-semantic-release/python-semantic-release@v10.0.1 with: root_options: "-vv --noop" @@ -699,7 +699,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v10.0.0 + - uses: python-semantic-release/publish-action@v10.0.1 with: root_options: "-vv --noop" @@ -873,14 +873,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@v10.0.0 + uses: python-semantic-release/python-semantic-release@v10.0.1 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@v10.0.0 + uses: python-semantic-release/publish-action@v10.0.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -979,7 +979,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v10.0.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -1038,14 +1038,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v10.0.1 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v10.0.1 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -1057,7 +1057,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.0 + uses: python-semantic-release/publish-action@v10.0.1 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -1065,7 +1065,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.0 + uses: python-semantic-release/publish-action@v10.0.1 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/pyproject.toml b/pyproject.toml index ec0bc1f8e..d14279384 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "10.0.0" +version = "10.0.1" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } From 3b7b821b6ebbdc1866c8f9852f1c6967e86cd898 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 14:37:17 -0600 Subject: [PATCH 11/37] Revert "10.0.1" This reverts commit b15f3cb1a0529a32eb149ce25d9a60ca4448b88e. Revert commit because release & deploy process failed in CI. Must revert. --- CHANGELOG.rst | 15 --------------- .../automatic-releases/github-actions.rst | 18 +++++++++--------- pyproject.toml | 2 +- 3 files changed, 10 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 6df94b9f6..c2f11439b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,21 +4,6 @@ CHANGELOG ========= -.. _changelog-v10.0.1: - -v10.0.1 (2025-05-25) -==================== - -🪲 Bug Fixes ------------- - -* **github-actions**: Bump the github-actions dependency to ``v10.0.0`` (#1255) (`PR#1255`_, - `2803676`_) - -.. _2803676: https://github.com/python-semantic-release/python-semantic-release/commit/2803676cf26c52177fa98d9144934853744a22bb -.. _PR#1255: https://github.com/python-semantic-release/python-semantic-release/pull/1255 - - .. _changelog-v10.0.0: v10.0.0 (2025-05-25) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index 7c2750491..d8a8bd012 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -382,7 +382,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v10.0.1 + - uses: python-semantic-release/python-semantic-release@v10.0.0 with: root_options: "-vv --noop" @@ -699,7 +699,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v10.0.1 + - uses: python-semantic-release/publish-action@v10.0.0 with: root_options: "-vv --noop" @@ -873,14 +873,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@v10.0.1 + uses: python-semantic-release/python-semantic-release@v10.0.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@v10.0.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -979,7 +979,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v10.0.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -1038,14 +1038,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v10.0.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v10.0.1 + uses: python-semantic-release/python-semantic-release@v10.0.0 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -1057,7 +1057,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -1065,7 +1065,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.1 + uses: python-semantic-release/publish-action@v10.0.0 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/pyproject.toml b/pyproject.toml index d14279384..ec0bc1f8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "10.0.1" +version = "10.0.0" description = "Automatic Semantic Versioning for Python projects" requires-python = ">=3.8" license = { text = "MIT" } From 74f72606b7a1fe9b67ff6f6d184b77222b2473e4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 15:31:41 -0600 Subject: [PATCH 12/37] ci(deps): force `python-semantic-release` action to have `v10.0.0` --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 8c8b45c45..6fa69a102 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -145,7 +145,7 @@ jobs: - name: Release | Python Semantic Release id: release - uses: python-semantic-release/python-semantic-release@092ace20f4ebed6a656da54b499076f1a5b803c8 # v10.0.0 + uses: python-semantic-release/python-semantic-release@2803676cf26c52177fa98d9144934853744a22bb with: github_token: ${{ secrets.GITHUB_TOKEN }} verbosity: 1 From f92899174544414d915973cd574418bf7268ca1b Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 15:45:28 -0600 Subject: [PATCH 13/37] ci(release): add filesystem check post psr action to detect error --- .github/workflows/cicd.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 6fa69a102..50286d5bc 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -165,6 +165,7 @@ jobs: GIT_COMMITTER_NAME: ${{ env.GITHUB_ACTIONS_AUTHOR_NAME }} GIT_COMMITTER_EMAIL: ${{ env.GITHUB_ACTIONS_AUTHOR_EMAIL }} run: | + ls -la .git/ MINOR_VERSION_TAG="$(echo "$FULL_VERSION_TAG" | cut -d. -f1,2)" git tag --force --annotate "$MINOR_VERSION_TAG" "${FULL_VERSION_TAG}^{}" -m "$MINOR_VERSION_TAG" git push -u origin "$MINOR_VERSION_TAG" --force From 917a2c730cb8f6c8cd3d00f23c876d724a4a844c Mon Sep 17 00:00:00 2001 From: semantic-release Date: Sun, 25 May 2025 22:02:48 +0000 Subject: [PATCH 14/37] 10.0.1 Automatically generated by python-semantic-release --- CHANGELOG.rst | 14 ++++++++++++++ .../automatic-releases/github-actions.rst | 18 +++++++++--------- pyproject.toml | 2 +- src/gh_action/requirements.txt | 2 +- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c2f11439b..cecf469dc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,20 @@ CHANGELOG ========= +.. _changelog-v10.0.1: + +v10.0.1 (2025-05-25) +==================== + +🪲 Bug Fixes +------------ + +* **github-actions**: Bump the github-actions dependency to ``v10.0.0`` (`PR#1255`_, `2803676`_) + +.. _2803676: https://github.com/python-semantic-release/python-semantic-release/commit/2803676cf26c52177fa98d9144934853744a22bb +.. _PR#1255: https://github.com/python-semantic-release/python-semantic-release/pull/1255 + + .. _changelog-v10.0.0: v10.0.0 (2025-05-25) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index d8a8bd012..7c2750491 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -382,7 +382,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v10.0.0 + - uses: python-semantic-release/python-semantic-release@v10.0.1 with: root_options: "-vv --noop" @@ -699,7 +699,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v10.0.0 + - uses: python-semantic-release/publish-action@v10.0.1 with: root_options: "-vv --noop" @@ -873,14 +873,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@v10.0.0 + uses: python-semantic-release/python-semantic-release@v10.0.1 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@v10.0.0 + uses: python-semantic-release/publish-action@v10.0.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -979,7 +979,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v10.0.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -1038,14 +1038,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v10.0.1 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v10.0.0 + uses: python-semantic-release/python-semantic-release@v10.0.1 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -1057,7 +1057,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.0 + uses: python-semantic-release/publish-action@v10.0.1 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -1065,7 +1065,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.0 + uses: python-semantic-release/publish-action@v10.0.1 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/pyproject.toml b/pyproject.toml index ec0bc1f8e..d14279384 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "10.0.0" +version = "10.0.1" 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 2fabec60d..574893c77 100644 --- a/src/gh_action/requirements.txt +++ b/src/gh_action/requirements.txt @@ -1 +1 @@ -python-semantic-release == 10.0.0 +python-semantic-release == 10.0.1 From ffb2976edd5d9a04e4c6c63b885484814b86879f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 16:24:59 -0600 Subject: [PATCH 15/37] ci(deps): bump `python-semantic-release@v10.0.0` action to `v10.0.1` --- .github/workflows/cicd.yml | 2 +- .github/workflows/validate.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 50286d5bc..c83674d22 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -145,7 +145,7 @@ jobs: - name: Release | Python Semantic Release id: release - uses: python-semantic-release/python-semantic-release@2803676cf26c52177fa98d9144934853744a22bb + uses: python-semantic-release/python-semantic-release@917a2c730cb8f6c8cd3d00f23c876d724a4a844c # v10.0.1 with: github_token: ${{ secrets.GITHUB_TOKEN }} verbosity: 1 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 1e93ff0d6..053448561 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -112,7 +112,7 @@ jobs: - name: Build | Build next version artifacts id: version - uses: python-semantic-release/python-semantic-release@092ace20f4ebed6a656da54b499076f1a5b803c8 # v10.0.0 + uses: python-semantic-release/python-semantic-release@917a2c730cb8f6c8cd3d00f23c876d724a4a844c # v10.0.1 with: github_token: "" verbosity: 1 From 109b8bdd2c83122e9812c88a8f8cc25109c1068c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 May 2025 22:20:14 +0000 Subject: [PATCH 16/37] ci(deps): bump `python-semantic-release/publish-action@v10.0.0` to `v10.0.1` --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index c83674d22..e4b35a055 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -152,7 +152,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@d62706ce15a7c98325c51a3e5cc789fdbe843e5a # v10.0.0 + uses: python-semantic-release/publish-action@d3a9934c4fff57f0d4df24450566d3dba7e7082a # v10.0.1 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} From 93e23c8993fe6f113095bfcd5089684f403cc6b9 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 20:20:40 -0600 Subject: [PATCH 17/37] fix(github-actions): add filesystem UID/GID fixer after action workspace modification (#1262) * ci(validate): fix job condition on github-action testing * ci(release): remove filesystem error check detection - debug flag * ci(deps): temporarily use the current HEAD github action to release psr --- .github/workflows/cicd.yml | 3 +-- .github/workflows/validate.yml | 2 +- src/gh_action/action.sh | 20 ++++++++++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index e4b35a055..f63564b7c 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -145,7 +145,7 @@ jobs: - name: Release | Python Semantic Release id: release - uses: python-semantic-release/python-semantic-release@917a2c730cb8f6c8cd3d00f23c876d724a4a844c # v10.0.1 + uses: ./ with: github_token: ${{ secrets.GITHUB_TOKEN }} verbosity: 1 @@ -165,7 +165,6 @@ jobs: GIT_COMMITTER_NAME: ${{ env.GITHUB_ACTIONS_AUTHOR_NAME }} GIT_COMMITTER_EMAIL: ${{ env.GITHUB_ACTIONS_AUTHOR_EMAIL }} run: | - ls -la .git/ MINOR_VERSION_TAG="$(echo "$FULL_VERSION_TAG" | cut -d. -f1,2)" git tag --force --annotate "$MINOR_VERSION_TAG" "${FULL_VERSION_TAG}^{}" -m "$MINOR_VERSION_TAG" git push -u origin "$MINOR_VERSION_TAG" --force diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 053448561..89535f255 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -393,7 +393,7 @@ jobs: test-gh-action: name: Validate Action Build & Execution runs-on: ubuntu-latest - if: inputs.gha-src-files-changed == 'true' || inputs.gha-test-files-changed == 'true' || inputs.ci-files-changed == 'true' + if: ${{ inputs.gha-src-files-changed == 'true' || inputs.gha-test-files-changed == 'true' || inputs.ci-files-changed == 'true' }} needs: - build diff --git a/src/gh_action/action.sh b/src/gh_action/action.sh index cd862d9a9..7ae58f68d 100644 --- a/src/gh_action/action.sh +++ b/src/gh_action/action.sh @@ -2,6 +2,8 @@ set -e +WORKSPACE_DIR="$(pwd)" + explicit_run_cmd() { local cmd="" cmd="$(printf '%s' "$*" | sed 's/^ *//g' | sed 's/ *$//g')" @@ -49,6 +51,20 @@ eval_string_input() { printf '%s' "${if_defined/\%s/$value}" } +# Capture UID and GID of the external filesystem +if [ ! -f "$WORKSPACE_DIR/.git/HEAD" ]; then + echo "::error:: .git/HEAD file not found. Ensure you are in a valid git repository." + exit 1 +fi + +EXT_HOST_UID="$(stat -c '%u' "$WORKSPACE_DIR/.git/HEAD")" +EXT_HOST_GID="$(stat -c '%g' "$WORKSPACE_DIR/.git/HEAD")" + +if [ -z "$EXT_HOST_UID" ] || [ -z "$EXT_HOST_GID" ]; then + echo "Error: Unable to determine external filesystem UID/GID from .git/HEAD" + exit 1 +fi + # Convert inputs to command line arguments ROOT_OPTIONS=() @@ -165,5 +181,9 @@ export GH_TOKEN="${INPUT_GITHUB_TOKEN}" # normalize extra spaces into single spaces as you combine the arguments CMD_ARGS="$(printf '%s' "${ROOT_OPTIONS[*]} version ${ARGS[*]}" | sed 's/ [ ]*/ /g' | sed 's/^ *//g')" +# Make sure the workspace directory is owned by the external filesystem UID/GID no matter what +# This is to ensure that after the action, and a commit was created, the files are owned by the external filesystem +trap "chown -R $EXT_HOST_UID:$EXT_HOST_GID '$WORKSPACE_DIR'" EXIT + # Run Semantic Release (explicitly use the GitHub action version) explicit_run_cmd "$PSR_VENV_BIN/semantic-release $CMD_ARGS" From 1a324000f2251a9e722e77b128bf72712653813f Mon Sep 17 00:00:00 2001 From: semantic-release Date: Mon, 26 May 2025 02:36:56 +0000 Subject: [PATCH 18/37] 10.0.2 Automatically generated by python-semantic-release --- CHANGELOG.rst | 15 +++++++++++++++ .../automatic-releases/github-actions.rst | 18 +++++++++--------- pyproject.toml | 2 +- src/gh_action/requirements.txt | 2 +- 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cecf469dc..09d90b45c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,21 @@ CHANGELOG ========= +.. _changelog-v10.0.2: + +v10.0.2 (2025-05-26) +==================== + +🪲 Bug Fixes +------------ + +* **github-actions**: Add filesystem UID/GID fixer after action workspace modification (`PR#1262`_, + `93e23c8`_) + +.. _93e23c8: https://github.com/python-semantic-release/python-semantic-release/commit/93e23c8993fe6f113095bfcd5089684f403cc6b9 +.. _PR#1262: https://github.com/python-semantic-release/python-semantic-release/pull/1262 + + .. _changelog-v10.0.1: v10.0.1 (2025-05-25) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index 7c2750491..4fc5022af 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -382,7 +382,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v10.0.1 + - uses: python-semantic-release/python-semantic-release@v10.0.2 with: root_options: "-vv --noop" @@ -699,7 +699,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v10.0.1 + - uses: python-semantic-release/publish-action@v10.0.2 with: root_options: "-vv --noop" @@ -873,14 +873,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@v10.0.1 + uses: python-semantic-release/python-semantic-release@v10.0.2 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@v10.0.1 + uses: python-semantic-release/publish-action@v10.0.2 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -979,7 +979,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v10.0.1 + uses: python-semantic-release/python-semantic-release@v10.0.2 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -1038,14 +1038,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v10.0.1 + uses: python-semantic-release/python-semantic-release@v10.0.2 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v10.0.1 + uses: python-semantic-release/python-semantic-release@v10.0.2 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -1057,7 +1057,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.1 + uses: python-semantic-release/publish-action@v10.0.2 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -1065,7 +1065,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.1 + uses: python-semantic-release/publish-action@v10.0.2 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/pyproject.toml b/pyproject.toml index d14279384..7a3aa918a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "10.0.1" +version = "10.0.2" 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 574893c77..835d01792 100644 --- a/src/gh_action/requirements.txt +++ b/src/gh_action/requirements.txt @@ -1 +1 @@ -python-semantic-release == 10.0.1 +python-semantic-release == 10.0.2 From 4a148cecf59d39a5e511e0c8d05f5dbb9ad0a09c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 25 May 2025 20:56:07 -0600 Subject: [PATCH 19/37] ci(deps): bump `python-semantic-release@v10.0.1` action to `v10.0.2` --- .github/workflows/cicd.yml | 2 +- .github/workflows/validate.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index f63564b7c..2209b8bbb 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -145,7 +145,7 @@ jobs: - name: Release | Python Semantic Release id: release - uses: ./ + uses: python-semantic-release/python-semantic-release@1a324000f2251a9e722e77b128bf72712653813f # v10.0.2 with: github_token: ${{ secrets.GITHUB_TOKEN }} verbosity: 1 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 89535f255..f20aa9700 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -112,7 +112,7 @@ jobs: - name: Build | Build next version artifacts id: version - uses: python-semantic-release/python-semantic-release@917a2c730cb8f6c8cd3d00f23c876d724a4a844c # v10.0.1 + uses: python-semantic-release/python-semantic-release@1a324000f2251a9e722e77b128bf72712653813f # v10.0.2 with: github_token: "" verbosity: 1 From 55486cd9551dafe57b715c06473fdc964f74e5e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 May 2025 02:54:34 +0000 Subject: [PATCH 20/37] ci(deps): bump `python-semantic-release/publish-action@v10.0.1` to `v10.0.2` --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 2209b8bbb..512454ab0 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -152,7 +152,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@d3a9934c4fff57f0d4df24450566d3dba7e7082a # v10.0.1 + uses: python-semantic-release/publish-action@e5e3010f6a207cd5d6f5d3dccedbea355484ca02 # v10.0.2 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} From b1151b1d3a3e8c3ff82435d4302f481d0c99b7a1 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 26 May 2025 13:39:52 -0600 Subject: [PATCH 21/37] refactor(changelog): simplify & consolidate jinja macro functionality (#1264) * refactor(changelog-md): simplify & consolidate jinja macro functionality * refactor(changelog-rst): simplify & consolidate jinja macro functionality --- .../conventional/md/.components/macros.md.j2 | 189 ++++++-------- .../rst/.components/macros.rst.j2 | 235 ++++++++---------- 2 files changed, 178 insertions(+), 246 deletions(-) diff --git a/src/semantic_release/data/templates/conventional/md/.components/macros.md.j2 b/src/semantic_release/data/templates/conventional/md/.components/macros.md.j2 index 8bb56ea79..13cc18fac 100644 --- a/src/semantic_release/data/templates/conventional/md/.components/macros.md.j2 +++ b/src/semantic_release/data/templates/conventional/md/.components/macros.md.j2 @@ -6,41 +6,50 @@ %} +{# + MACRO: Capitalize the first letter of a string only +#}{% macro capitalize_first_letter_only(sentence) +%}{{ (sentence[0] | upper) ~ sentence[1:] +}}{% endmacro +%} + + {# MACRO: commit message links or PR/MR links of commit #}{% macro commit_msg_links(commit) %}{% if commit.error is undefined -%}{% set commit_hash_link = format_link( - commit.hexsha | commit_hash_url, - "`%s`" | format(commit.short_hash) - ) %}{# -#}{% set summary_line = commit.descriptions[0] | safe -%}{% set summary_line = [ - summary_line.split(" ", maxsplit=1)[0] | capitalize, - summary_line.split(" ", maxsplit=1)[1] - ] | join(" ") + # # Initialize variables +#}{% set link_references = [] +%}{% set summary_line = capitalize_first_letter_only( + commit.descriptions[0] | safe + ) %}{# #}{% if commit.linked_merge_request != "" %}{# # Add PR references with a link to the PR -#}{% set pr_num = commit.linked_merge_request -%}{% set pr_link = format_link(pr_num | pull_request_url, pr_num) -%}{# - # TODO: breaking change v10, remove summary line replacers as PSR will do it for us -#}{% set summary_line = summary_line | replace("(pull request", "(") | replace("(" ~ pr_num ~ ")", "") | trim -%}{% set summary_line = "%s (%s, %s)" | format( - summary_line, - pr_link, - commit_hash_link, +#}{% set _ = link_references.append( + format_link( + commit.linked_merge_request | pull_request_url, + commit.linked_merge_request + ) ) +%}{% endif +%}{# + # # DEFAULT: Always include the commit hash as a link +#}{% set _ = link_references.append( + format_link( + commit.hexsha | commit_hash_url, + "`%s`" | format(commit.short_hash) + ) + ) %}{# - # DEFAULT: No PR identifier found, so just append commit hash as url to the commit summary_line -#}{% else -%}{% set summary_line = "%s (%s)" | format(summary_line, commit_hash_link) +#}{% set formatted_links = "" +%}{% if link_references | length > 0 +%}{% set formatted_links = " (%s)" | format(link_references | join(", ")) %}{% endif %}{# # Return the modified summary_line -#}{{ summary_line +#}{{ summary_line ~ formatted_links }}{% endif %}{% endmacro %} @@ -71,24 +80,21 @@ {# - MACRO: format the breaking changes description by: - - Capitalizing the description + MACRO: format a commit descriptions list by: + - Capitalizing the first line of the description - Adding an optional scope prefix -#}{% macro format_breaking_changes_description(commit) -%}{% set ns = namespace(full_description="") + - Joining the rest of the descriptions with a double newline +#}{% macro format_attr_paragraphs(commit, attribute) +%}{# NOTE: requires namespace because of the way Jinja2 handles variable scoping with loops +#}{% set ns = namespace(full_description="") %}{# #}{% if commit.error is undefined -%}{% for paragraph in commit.breaking_descriptions +%}{% for paragraph in commit | attr(attribute) %}{% if paragraph | trim | length > 0 %}{# -#}{% set paragraph_text = [ - paragraph.split(" ", maxsplit=1)[0] | capitalize, - paragraph.split(" ", maxsplit=1)[1] - ] | join(" ") | trim | safe -%}{# #}{% set ns.full_description = [ ns.full_description, - paragraph_text + capitalize_first_letter_only(paragraph) | trim | safe, ] | join("\n\n") %}{# #}{% endif @@ -108,65 +114,48 @@ %} +{# + MACRO: format the breaking changes description by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_breaking_changes_description(commit) +%}{{ format_attr_paragraphs(commit, 'breaking_descriptions') +}}{% endmacro +%} + + {# MACRO: format the release notice by: - Capitalizing the description - Adding an optional scope prefix #}{% macro format_release_notice(commit) -%}{% set ns = namespace(full_description="") -%}{# -#}{% if commit.error is undefined -%}{% for paragraph in commit.release_notices -%}{% if paragraph | trim | length > 0 -%}{# -#}{% set paragraph_text = [ - paragraph.split(" ", maxsplit=1)[0] | capitalize, - paragraph.split(" ", maxsplit=1)[1] - ] | join(" ") | trim | safe -%}{# -#}{% set ns.full_description = [ - ns.full_description, - paragraph_text - ] | join("\n\n") -%}{# -#}{% endif -%}{% endfor -%}{# -#}{% set ns.full_description = ns.full_description | trim -%}{# -#}{% if commit.scope -%}{% set ns.full_description = "**%s**: %s" | format( - commit.scope, ns.full_description - ) -%}{% endif -%}{% endif -%}{# -#}{{ ns.full_description +%}{{ format_attr_paragraphs(commit, "release_notices") }}{% endmacro %} {# - MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes - - Commits are sorted based on the commit type and the commit message - - Commits are grouped by the commit type - - parameter: ns (namespace) object with a commits list - - returns None but modifies the ns.commits list in place -#}{% macro apply_alphabetical_ordering_by_descriptions(ns) + MACRO: order commits alphabetically by scope and attribute + - Commits are sorted based on scope and then the attribute alphabetically + - Commits without scope are placed first and sorted alphabetically by the attribute + - parameter: ns (namespace) object with a commits list + - parameter: attr (string) attribute to sort by + - returns None but modifies the ns.commits list in place +#}{% macro order_commits_alphabetically_by_scope_and_attr(ns, attr) %}{% set ordered_commits = [] %}{# # # Eliminate any ParseError commits from input set #}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list %}{# - # # grab all commits with no scope and sort alphabetically by the first line of the commit message -#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute='descriptions.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor + # # grab all commits with no scope and sort alphabetically by attr +#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute=attr) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor %}{# - # # grab all commits with a scope and sort alphabetically by the scope and then the first line of the commit message -#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute='scope,descriptions.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor + # # grab all commits with a scope and sort alphabetically by the scope and then attr +#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute=(['scope', attr] | join(","))) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor %}{# # # Return the ordered commits #}{% set ns.commits = ordered_commits @@ -174,6 +163,18 @@ %} +{# + MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_descriptions(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'descriptions.0') +%}{% endmacro +%} + + {# MACRO: apply smart ordering of commits objects based on alphabetized breaking changes and then scopes - Commits are sorted based on the commit type and the commit message @@ -181,23 +182,7 @@ - parameter: ns (namespace) object with a commits list - returns None but modifies the ns.commits list in place #}{% macro apply_alphabetical_ordering_by_brk_descriptions(ns) -%}{% set ordered_commits = [] -%}{# - # # Eliminate any ParseError commits from input set -#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list -%}{# - # # grab all commits with no scope and sort alphabetically by the first line of the commit message -#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute='breaking_descriptions.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor -%}{# - # # grab all commits with a scope and sort alphabetically by the scope and then the first line of the commit message -#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute='scope,breaking_descriptions.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor -%}{# - # # Return the ordered commits -#}{% set ns.commits = ordered_commits +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') %}{% endmacro %} @@ -209,22 +194,6 @@ - parameter: ns (namespace) object with a commits list - returns None but modifies the ns.commits list in place #}{% macro apply_alphabetical_ordering_by_release_notices(ns) -%}{% set ordered_commits = [] -%}{# - # # Eliminate any ParseError commits from input set -#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list -%}{# - # # grab all commits with no scope and sort alphabetically by the first line of the commit message -#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute='release_notices.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor -%}{# - # # grab all commits with a scope and sort alphabetically by the scope and then the first line of the commit message -#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute='scope,release_notices.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor -%}{# - # # Return the ordered commits -#}{% set ns.commits = ordered_commits +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'release_notices.0') %}{% endmacro %} diff --git a/src/semantic_release/data/templates/conventional/rst/.components/macros.rst.j2 b/src/semantic_release/data/templates/conventional/rst/.components/macros.rst.j2 index f70043036..6fc7f90ff 100644 --- a/src/semantic_release/data/templates/conventional/rst/.components/macros.rst.j2 +++ b/src/semantic_release/data/templates/conventional/rst/.components/macros.rst.j2 @@ -1,3 +1,11 @@ +{# + MACRO: Capitalize the first letter of a string only +#}{% macro capitalize_first_letter_only(sentence) +%}{{ (sentence[0] | upper) ~ sentence[1:] +}}{% endmacro +%} + + {# MACRO: format a post-paragraph link reference in RST #}{% macro format_link_reference(link, label) @@ -6,6 +14,49 @@ %} +{# MACRO: generate a heading underline that matches the exact length of the header #} +{% macro generate_heading_underline(header, underline_char) +%}{% set header_underline = [] +%}{% for _ in header +%}{% set __ = header_underline.append(underline_char) +%}{% endfor +%}{# # Print out the header underline +#}{{ header_underline | join +}}{% endmacro +%} + + +{# + MACRO: formats a commit message for a non-inline RST link for a commit hash and/or PR/MR +#}{% macro commit_msg_links(commit) +%}{% if commit.error is undefined +%}{# + # # Initialize variables +#}{% set link_references = [] +%}{% set summary_line = capitalize_first_letter_only( + commit.descriptions[0] | safe + ) +%}{# +#}{% if commit.linked_merge_request != "" +%}{# # Add PR/MR references with a link to the PR/MR +#}{% set _ = link_references.append("`%s`_" | format(commit.linked_merge_request)) +%}{% endif +%}{# + # DEFAULT: Always include the commit hash as a link +#}{% set _ = link_references.append("`%s`_" | format(commit.short_hash)) +%}{# +#}{% set formatted_links = "" +%}{% if link_references | length > 0 +%}{% set formatted_links = " (%s)" | format(link_references | join(", ")) +%}{% endif +%}{# + # Return the modified summary_line +#}{{ summary_line ~ formatted_links +}}{% endif +%}{% endmacro +%} + + {# MACRO: format commit summary line #}{% macro format_commit_summary_line(commit) @@ -50,72 +101,21 @@ {# - MACRO: formats a commit message for a non-inline RST link for a commit hash and/or PR/MR -#}{% macro commit_msg_links(commit, hvcs_type) -%}{% if commit.error is undefined -%}{% set commit_hash_link = "`%s`_" | format(commit.short_hash) -%}{# -#}{% set summary_line = commit.descriptions[0] | safe -%}{% set summary_line = [ - summary_line.split(" ", maxsplit=1)[0] | capitalize, - summary_line.split(" ", maxsplit=1)[1] - ] | join(" ") -%}{# -#}{% if commit.linked_merge_request != "" -%}{# # Add PR references with a link to the PR -#}{% set pr_link = "`%s`_" | format(commit.linked_merge_request) -%}{# - # TODO: breaking change v10, remove summary line replacers as PSR will do it for us -#}{% set summary_line = summary_line | replace("(pull request ", "(") | replace("(" ~ commit.linked_merge_request ~ ")", "") | trim -%}{% set summary_line = "%s (%s, %s)" | format( - summary_line, - pr_link, - commit_hash_link, - ) -%}{# - # DEFAULT: No PR identifier found, so just append a commit hash as url to the commit summary_line -#}{% else -%}{% set summary_line = "%s (%s)" | format(summary_line, commit_hash_link) -%}{% endif -%}{# - # Return the modified summary_line -#}{{ summary_line -}}{% endif -%}{% endmacro -%} - - -{# MACRO: generate a heading underline that matches the exact length of the header #} -{% macro generate_heading_underline(header, underline_char) -%}{% set header_underline = [] -%}{% for _ in header -%}{{ header_underline.append(underline_char) | default("", true) -}}{% endfor -%}{# # Print out the header underline -#}{{ header_underline | join -}}{% endmacro -%} - - -{# - MACRO: format the breaking changes description by: - - Capitalizing the description + MACRO: format a commit descriptions list by: + - Capitalizing the first line of the description - Adding an optional scope prefix -#}{% macro format_breaking_changes_description(commit) -%}{% set ns = namespace(full_description="") + - Joining the rest of the descriptions with a double newline +#}{% macro format_attr_paragraphs(commit, attribute) +%}{# NOTE: requires namespace because of the way Jinja2 handles variable scoping with loops +#}{% set ns = namespace(full_description="") %}{# #}{% if commit.error is undefined -%}{% for paragraph in commit.breaking_descriptions +%}{% for paragraph in commit | attr(attribute) %}{% if paragraph | trim | length > 0 %}{# -#}{% set paragraph_text = [ - paragraph.split(" ", maxsplit=1)[0] | capitalize, - paragraph.split(" ", maxsplit=1)[1] - ] | join(" ") | trim | safe -%}{# #}{% set ns.full_description = [ ns.full_description, - paragraph_text + capitalize_first_letter_only(paragraph) | trim | safe, ] | join("\n\n") %}{# #}{% endif @@ -135,65 +135,48 @@ %} +{# + MACRO: format the breaking changes description by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_breaking_changes_description(commit) +%}{{ format_attr_paragraphs(commit, 'breaking_descriptions') +}}{% endmacro +%} + + {# MACRO: format the release notice by: - Capitalizing the description - Adding an optional scope prefix #}{% macro format_release_notice(commit) -%}{% set ns = namespace(full_description="") -%}{# -#}{% if commit.error is undefined -%}{% for paragraph in commit.release_notices -%}{% if paragraph | trim | length > 0 -%}{# -#}{% set paragraph_text = [ - paragraph.split(" ", maxsplit=1)[0] | capitalize, - paragraph.split(" ", maxsplit=1)[1] - ] | join(" ") | trim | safe -%}{# -#}{% set ns.full_description = [ - ns.full_description, - paragraph_text - ] | join("\n\n") -%}{# -#}{% endif -%}{% endfor -%}{# -#}{% set ns.full_description = ns.full_description | trim -%}{# -#}{% if commit.scope -%}{% set ns.full_description = "**%s**: %s" | format( - commit.scope, ns.full_description - ) -%}{% endif -%}{% endif -%}{# -#}{{ ns.full_description +%}{{ format_attr_paragraphs(commit, "release_notices") }}{% endmacro %} {# - MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes - - Commits are sorted based on the commit type and the commit message - - Commits are grouped by the commit type - - parameter: ns (namespace) object with a commits list - - returns None but modifies the ns.commits list in place -#}{% macro apply_alphabetical_ordering_by_descriptions(ns) + MACRO: order commits alphabetically by scope and attribute + - Commits are sorted based on scope and then the attribute alphabetically + - Commits without scope are placed first and sorted alphabetically by the attribute + - parameter: ns (namespace) object with a commits list + - parameter: attr (string) attribute to sort by + - returns None but modifies the ns.commits list in place +#}{% macro order_commits_alphabetically_by_scope_and_attr(ns, attr) %}{% set ordered_commits = [] %}{# # # Eliminate any ParseError commits from input set #}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list %}{# - # # grab all commits with no scope and sort alphabetically by the first line of the commit message -#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute='descriptions.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor + # # grab all commits with no scope and sort alphabetically by attr +#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute=attr) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor %}{# - # # grab all commits with a scope and sort alphabetically by the scope and then the first line of the commit message -#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute='scope,descriptions.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor + # # grab all commits with a scope and sort alphabetically by the scope and then attr +#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute=(['scope', attr] | join(","))) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor %}{# # # Return the ordered commits #}{% set ns.commits = ordered_commits @@ -201,6 +184,18 @@ %} +{# + MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_descriptions(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'descriptions.0') +%}{% endmacro +%} + + {# MACRO: apply smart ordering of commits objects based on alphabetized breaking changes and then scopes - Commits are sorted based on the commit type and the commit message @@ -208,23 +203,7 @@ - parameter: ns (namespace) object with a commits list - returns None but modifies the ns.commits list in place #}{% macro apply_alphabetical_ordering_by_brk_descriptions(ns) -%}{% set ordered_commits = [] -%}{# - # # Eliminate any ParseError commits from input set -#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list -%}{# - # # grab all commits with no scope and sort alphabetically by the first line of the commit message -#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute='breaking_descriptions.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor -%}{# - # # grab all commits with a scope and sort alphabetically by the scope and then the first line of the commit message -#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute='scope,breaking_descriptions.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor -%}{# - # # Return the ordered commits -#}{% set ns.commits = ordered_commits +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') %}{% endmacro %} @@ -236,22 +215,6 @@ - parameter: ns (namespace) object with a commits list - returns None but modifies the ns.commits list in place #}{% macro apply_alphabetical_ordering_by_release_notices(ns) -%}{% set ordered_commits = [] -%}{# - # # Eliminate any ParseError commits from input set -#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list -%}{# - # # grab all commits with no scope and sort alphabetically by the first line of the commit message -#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute='release_notices.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor -%}{# - # # grab all commits with a scope and sort alphabetically by the scope and then the first line of the commit message -#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute='scope,release_notices.0') -%}{{ ordered_commits.append(commit) | default("", true) -}}{% endfor -%}{# - # # Return the ordered commits -#}{% set ns.commits = ordered_commits +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'release_notices.0') %}{% endmacro %} From 10e1b52f4b3f5ccdfa322c3005dec3381b97fa81 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 26 May 2025 13:43:11 -0600 Subject: [PATCH 22/37] chore(psr-changelog): consolidate & simplify jinja macro implementation (#1265) --- .../.components/changes.md.j2 | 13 +- .../.components/changes.rst.j2 | 17 +- .../.components/macros.common.j2 | 160 ++++++++++ .../.components/macros.md.j2 | 174 +---------- .../.components/macros.rst.j2 | 275 ++++-------------- 5 files changed, 229 insertions(+), 410 deletions(-) create mode 100644 config/release-templates/.components/macros.common.j2 diff --git a/config/release-templates/.components/changes.md.j2 b/config/release-templates/.components/changes.md.j2 index d2a062d9a..6cdef2d17 100644 --- a/config/release-templates/.components/changes.md.j2 +++ b/config/release-templates/.components/changes.md.j2 @@ -1,9 +1,10 @@ -{% from 'macros.md.j2' import apply_alphabetical_ordering_by_brk_descriptions -%}{% from 'macros.md.j2' import apply_alphabetical_ordering_by_descriptions -%}{% from 'macros.md.j2' import apply_alphabetical_ordering_by_release_notices -%}{% from 'macros.md.j2' import emoji_map, format_breaking_changes_description -%}{% from 'macros.md.j2' import format_commit_summary_line, format_release_notice -%}{% from 'macros.md.j2' import section_heading_order, section_heading_translations +{% from 'macros.common.j2' import apply_alphabetical_ordering_by_brk_descriptions +%}{% from 'macros.common.j2' import apply_alphabetical_ordering_by_descriptions +%}{% from 'macros.common.j2' import apply_alphabetical_ordering_by_release_notices +%}{% from 'macros.common.j2' import emoji_map, format_breaking_changes_description +%}{% from 'macros.common.j2' import format_release_notice, section_heading_order +%}{% from 'macros.common.j2' import section_heading_translations +%}{% from 'macros.md.j2' import format_commit_summary_line %}{# EXAMPLE: diff --git a/config/release-templates/.components/changes.rst.j2 b/config/release-templates/.components/changes.rst.j2 index 90434bfdb..9751108c2 100644 --- a/config/release-templates/.components/changes.rst.j2 +++ b/config/release-templates/.components/changes.rst.j2 @@ -1,11 +1,12 @@ -{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_brk_descriptions -%}{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_descriptions -%}{% from 'macros.rst.j2' import apply_alphabetical_ordering_by_release_notices -%}{% from 'macros.rst.j2' import emoji_map, extract_issue_link_references, extract_pr_link_reference -%}{% from 'macros.rst.j2' import format_breaking_changes_description, format_commit_summary_line -%}{% from 'macros.rst.j2' import format_link_reference, format_release_notice -%}{% from 'macros.rst.j2' import generate_heading_underline, section_heading_order -%}{% from 'macros.rst.j2' import section_heading_translations +{% from 'macros.common.j2' import apply_alphabetical_ordering_by_brk_descriptions +%}{% from 'macros.common.j2' import apply_alphabetical_ordering_by_descriptions +%}{% from 'macros.common.j2' import apply_alphabetical_ordering_by_release_notices +%}{% from 'macros.common.j2' import emoji_map, format_breaking_changes_description +%}{% from 'macros.common.j2' import format_release_notice, section_heading_order +%}{% from 'macros.common.j2' import section_heading_translations +%}{% from 'macros.rst.j2' import extract_issue_link_references, extract_pr_link_reference +%}{% from 'macros.rst.j2' import format_commit_summary_line, format_link_reference +%}{% from 'macros.rst.j2' import generate_heading_underline %}{# ✨ Features diff --git a/config/release-templates/.components/macros.common.j2 b/config/release-templates/.components/macros.common.j2 new file mode 100644 index 000000000..5ec7ff6d0 --- /dev/null +++ b/config/release-templates/.components/macros.common.j2 @@ -0,0 +1,160 @@ +{# TODO: move to configuration for user to modify #} +{% set section_heading_translations = { + 'feat': 'features', + 'fix': 'bug fixes', + 'perf': 'performance improvements', + 'docs': 'documentation', + 'build': 'build system', + 'refactor': 'refactoring', + 'test': 'testing', + 'ci': 'continuous integration', + 'chore': 'chores', + 'style': 'code style', + } +%} + +{% set section_heading_order = section_heading_translations.values() %} + +{% set emoji_map = { + 'breaking': '💥', + 'features': '✨', + 'bug fixes': '🪲', + 'performance improvements': '⚡', + 'documentation': '📖', + 'build system': '⚙️', + 'refactoring': '♻️', + 'testing': '✅', + 'continuous integration': '🤖', + 'chores': '🧹', + 'code style': '🎨', + 'unknown': '❗', + 'release_note': '💡', +} %} + + +{# + MACRO: Capitalize the first letter of a string only +#}{% macro capitalize_first_letter_only(sentence) +%}{{ (sentence[0] | upper) ~ sentence[1:] +}}{% endmacro +%} + + +{# + MACRO: format a commit descriptions list by: + - Capitalizing the first line of the description + - Adding an optional scope prefix + - Joining the rest of the descriptions with a double newline +#}{% macro format_attr_paragraphs(commit, attribute) +%}{# NOTE: requires namespace because of the way Jinja2 handles variable scoping with loops +#}{% set ns = namespace(full_description="") +%}{# +#}{% if commit.error is undefined +%}{% for paragraph in commit | attr(attribute) +%}{% if paragraph | trim | length > 0 +%}{# +#}{% set ns.full_description = [ + ns.full_description, + capitalize_first_letter_only(paragraph) | trim | safe, + ] | join("\n\n") +%}{# +#}{% endif +%}{% endfor +%}{# +#}{% set ns.full_description = ns.full_description | trim +%}{# +#}{% if commit.scope +%}{% set ns.full_description = "**%s**: %s" | format( + commit.scope, ns.full_description + ) +%}{% endif +%}{% endif +%}{# +#}{{ ns.full_description +}}{% endmacro +%} + + +{# + MACRO: format the breaking changes description by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_breaking_changes_description(commit) +%}{{ format_attr_paragraphs(commit, 'breaking_descriptions') +}}{% endmacro +%} + + +{# + MACRO: format the release notice by: + - Capitalizing the description + - Adding an optional scope prefix +#}{% macro format_release_notice(commit) +%}{{ format_attr_paragraphs(commit, "release_notices") +}}{% endmacro +%} + + +{# + MACRO: order commits alphabetically by scope and attribute + - Commits are sorted based on scope and then the attribute alphabetically + - Commits without scope are placed first and sorted alphabetically by the attribute + - parameter: ns (namespace) object with a commits list + - parameter: attr (string) attribute to sort by + - returns None but modifies the ns.commits list in place +#}{% macro order_commits_alphabetically_by_scope_and_attr(ns, attr) +%}{% set ordered_commits = [] +%}{# + # # Eliminate any ParseError commits from input set +#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list +%}{# + # # grab all commits with no scope and sort alphabetically by attr +#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute=attr) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor +%}{# + # # grab all commits with a scope and sort alphabetically by the scope and then attr +#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute=(['scope', attr] | join(","))) +%}{% set _ = ordered_commits.append(commit) +%}{% endfor +%}{# + # # Return the ordered commits +#}{% set ns.commits = ordered_commits +%}{% endmacro +%} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_descriptions(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'descriptions.0') +%}{% endmacro +%} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized breaking changes and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_brk_descriptions(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') +%}{% endmacro +%} + + +{# + MACRO: apply smart ordering of commits objects based on alphabetized release notices and then scopes + - Commits are sorted based on the commit type and the commit message + - Commits are grouped by the commit type + - parameter: ns (namespace) object with a commits list + - returns None but modifies the ns.commits list in place +#}{% macro apply_alphabetical_ordering_by_release_notices(ns) +%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'release_notices.0') +%}{% endmacro +%} diff --git a/config/release-templates/.components/macros.md.j2 b/config/release-templates/.components/macros.md.j2 index 89cd84cb2..bbccd9c86 100644 --- a/config/release-templates/.components/macros.md.j2 +++ b/config/release-templates/.components/macros.md.j2 @@ -1,33 +1,4 @@ -{% set section_heading_translations = { - 'feat': 'features', - 'fix': 'bug fixes', - 'perf': 'performance improvements', - 'docs': 'documentation', - 'build': 'build system', - 'refactor': 'refactoring', - 'test': 'testing', - 'ci': 'continuous integration', - 'chore': 'chores', - 'style': 'code style', -} %} - -{% set section_heading_order = section_heading_translations.values() %} - -{% set emoji_map = { - 'breaking': '💥', - 'features': '✨', - 'bug fixes': '🪲', - 'performance improvements': '⚡', - 'documentation': '📖', - 'build system': '⚙️', - 'refactoring': '♻️', - 'testing': '✅', - 'continuous integration': '🤖', - 'chores': '🧹', - 'code style': '🎨', - 'unknown': '❗', - 'release_note': '💡', -} %} +{% from 'macros.common.j2' import capitalize_first_letter_only %} {# @@ -38,14 +9,6 @@ %} -{# - MACRO: Capitalize the first letter of a string only -#}{% macro capitalize_first_letter_only(sentence) -%}{{ (sentence[0] | upper) ~ sentence[1:] -}}{% endmacro -%} - - {# MACRO: commit message links or PR/MR links of commit #}{% macro commit_msg_links(commit) @@ -109,138 +72,3 @@ }}{% endif %}{% endmacro %} - - -{# - MACRO: format the breaking changes description by: - - Capitalizing the description - - Adding an optional scope prefix -#}{% macro format_breaking_changes_description(commit) -%}{% set ns = namespace(full_description="") -%}{# -#}{% if commit.error is undefined -%}{% for paragraph in commit.breaking_descriptions -%}{% if paragraph | trim | length > 0 -%}{# -#}{% set paragraph_text = capitalize_first_letter_only(paragraph) | trim | safe -%}{# -#}{% set ns.full_description = [ - ns.full_description, - paragraph_text - ] | join("\n\n") -%}{# -#}{% endif -%}{% endfor -%}{# -#}{% set ns.full_description = ns.full_description | trim -%}{# -#}{% if commit.scope -%}{% set ns.full_description = "**%s**: %s" | format( - commit.scope, ns.full_description - ) -%}{% endif -%}{% endif -%}{# -#}{{ ns.full_description -}}{% endmacro -%} - - -{# - MACRO: format the release notice by: - - Capitalizing the description - - Adding an optional scope prefix -#}{% macro format_release_notice(commit) -%}{% set ns = namespace(full_description="") -%}{# -#}{% if commit.error is undefined -%}{% for paragraph in commit.release_notices -%}{% if paragraph | trim | length > 0 -%}{# -#}{% set paragraph_text = capitalize_first_letter_only(paragraph) | trim | safe -%}{# -#}{% set ns.full_description = [ - ns.full_description, - paragraph_text - ] | join("\n\n") -%}{# -#}{% endif -%}{% endfor -%}{# -#}{% set ns.full_description = ns.full_description | trim -%}{# -#}{% if commit.scope -%}{% set ns.full_description = "**%s**: %s" | format( - commit.scope, ns.full_description - ) -%}{% endif -%}{% endif -%}{# -#}{{ ns.full_description -}}{% endmacro -%} - - -{# - MACRO: order commits alphabetically by scope and attribute - - Commits are sorted based on scope and then the attribute alphabetically - - Commits without scope are placed first and sorted alphabetically by the attribute - - parameter: ns (namespace) object with a commits list - - parameter: attr (string) attribute to sort by - - returns None but modifies the ns.commits list in place -#}{% macro order_commits_alphabetically_by_scope_and_attr(ns, attr) -%}{% set ordered_commits = [] -%}{# - # # Eliminate any ParseError commits from input set -#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list -%}{# - # # grab all commits with no scope and sort alphabetically by attr -#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute=attr) -%}{% set _ = ordered_commits.append(commit) -%}{% endfor -%}{# - # # grab all commits with a scope and sort alphabetically by the scope and then attr -#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute=(['scope', attr] | join(","))) -%}{% set _ = ordered_commits.append(commit) -%}{% endfor -%}{# - # # Return the ordered commits -#}{% set ns.commits = ordered_commits -%}{% endmacro -%} - - -{# - MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes - - Commits are sorted based on the commit type and the commit message - - Commits are grouped by the commit type - - parameter: ns (namespace) object with a commits list - - returns None but modifies the ns.commits list in place -#}{% macro apply_alphabetical_ordering_by_descriptions(ns) -%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'descriptions.0') -%}{% endmacro -%} - - -{# - MACRO: apply smart ordering of commits objects based on alphabetized breaking changes and then scopes - - Commits are sorted based on the commit type and the commit message - - Commits are grouped by the commit type - - parameter: ns (namespace) object with a commits list - - returns None but modifies the ns.commits list in place -#}{% macro apply_alphabetical_ordering_by_brk_descriptions(ns) -%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') -%}{% endmacro -%} - - -{# - MACRO: apply smart ordering of commits objects based on alphabetized release notices and then scopes - - Commits are sorted based on the commit type and the commit message - - Commits are grouped by the commit type - - parameter: ns (namespace) object with a commits list - - returns None but modifies the ns.commits list in place -#}{% macro apply_alphabetical_ordering_by_release_notices(ns) -%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'release_notices.0') -%}{% endmacro -%} diff --git a/config/release-templates/.components/macros.rst.j2 b/config/release-templates/.components/macros.rst.j2 index 779571c02..11c61d6de 100644 --- a/config/release-templates/.components/macros.rst.j2 +++ b/config/release-templates/.components/macros.rst.j2 @@ -1,35 +1,5 @@ -{# TODO: move to configuration for user to modify #} -{% set section_heading_translations = { - 'feat': 'features', - 'fix': 'bug fixes', - 'perf': 'performance improvements', - 'docs': 'documentation', - 'build': 'build system', - 'refactor': 'refactoring', - 'test': 'testing', - 'ci': 'continuous integration', - 'chore': 'chores', - 'style': 'code style', - } -%} - -{% set section_heading_order = section_heading_translations.values() %} +{% from 'macros.common.j2' import capitalize_first_letter_only %} -{% set emoji_map = { - 'breaking': '💥', - 'features': '✨', - 'bug fixes': '🪲', - 'performance improvements': '⚡', - 'documentation': '📖', - 'build system': '⚙️', - 'refactoring': '♻️', - 'testing': '✅', - 'continuous integration': '🤖', - 'chores': '🧹', - 'code style': '🎨', - 'unknown': '❗', - 'release_note': '💡', -} %} {# MACRO: format a post-paragraph link reference in RST @@ -39,79 +9,18 @@ %} -{# - MACRO: Capitalize the first letter of a string only -#}{% macro capitalize_first_letter_only(sentence) -%}{{ (sentence[0] | upper) ~ sentence[1:] +{# MACRO: generate a heading underline that matches the exact length of the header #} +{% macro generate_heading_underline(header, underline_char) +%}{% set header_underline = [] +%}{% for _ in header +%}{% set __ = header_underline.append(underline_char) +%}{% endfor +%}{# # Print out the header underline +#}{{ header_underline | join }}{% endmacro %} -{# - MACRO: format commit summary line -#}{% macro format_commit_summary_line(commit) -%}{# # Check for Parsing Error -#}{% if commit.error is undefined -%}{# - # # Add any message links to the commit summary line -#}{% set summary_line = commit_msg_links(commit) -%}{# -#}{% if commit.scope -%}{% set summary_line = "**%s**: %s" | format(commit.scope, summary_line) -%}{% endif -%}{# - # # Return the modified summary_line -#}{{ summary_line -}}{# -#}{% else -%}{# # Return the first line of the commit if there was a Parsing Error -#}{{ (commit.commit.message | string).split("\n", maxsplit=1)[0] -}}{% endif -%}{% endmacro -%} - - -{# - MACRO: Create & return an non-inline RST link from a commit message - - Returns empty string if no PR/MR identifier is found -#}{% macro extract_pr_link_reference(commit) -%}{% if commit.error is undefined -%}{% set summary_line = commit.descriptions[0] -%}{# -#}{% if commit.linked_merge_request != "" -%}{# # Create a PR/MR reference url -#}{{ format_link_reference( - commit.linked_merge_request | pull_request_url, - "PR" ~ commit.linked_merge_request, - ) -}}{% endif -%}{% endif -%}{% endmacro -%} - -{# - MACRO: Extract issue references from a parsed commit object - - Stores the issue urls in the namespace object -#}{% macro extract_issue_link_references(ns, commit) -%}{% set issue_urls = [] -%}{# -#}{% if commit.linked_issues is defined and commit.linked_issues | length > 0 -%}{% for issue_num in commit.linked_issues -%}{# # Create an issue reference url -#}{% set _ = issue_urls.append( - format_link_reference( - issue_num | issue_url, - issue_num, - ) - ) -%}{% endfor -%}{% endif -%}{# - # # Store the issue urls in the namespace object -#}{% set ns.urls = issue_urls -%}{% endmacro -%} - {# MACRO: formats a commit message for a non-inline RST link for a commit hash and/or PR/MR #}{% macro commit_msg_links(commit) @@ -150,148 +59,68 @@ %} -{# MACRO: generate a heading underline that matches the exact length of the header #} -{% macro generate_heading_underline(header, underline_char) -%}{% set header_underline = [] -%}{% for _ in header -%}{% set __ = header_underline.append(underline_char) -%}{% endfor -%}{# # Print out the header underline -#}{{ header_underline | join -}}{% endmacro -%} - - {# - MACRO: format the breaking changes description by: - - Capitalizing the description - - Adding an optional scope prefix -#}{% macro format_breaking_changes_description(commit) -%}{% set ns = namespace(full_description="") -%}{# + MACRO: format commit summary line +#}{% macro format_commit_summary_line(commit) +%}{# # Check for Parsing Error #}{% if commit.error is undefined -%}{% for paragraph in commit.breaking_descriptions -%}{% if paragraph | trim | length > 0 -%}{# -#}{% set paragraph_text = capitalize_first_letter_only(paragraph) | trim | safe %}{# -#}{% set ns.full_description = [ - ns.full_description, - paragraph_text - ] | join("\n\n") -%}{# -#}{% endif -%}{% endfor -%}{# -#}{% set ns.full_description = ns.full_description | trim + # # Add any message links to the commit summary line +#}{% set summary_line = commit_msg_links(commit) %}{# #}{% if commit.scope -%}{% set ns.full_description = "**%s**: %s" | format( - commit.scope, ns.full_description - ) +%}{% set summary_line = "**%s**: %s" | format(commit.scope, summary_line) %}{% endif -%}{% endif %}{# -#}{{ ns.full_description -}}{% endmacro + # # Return the modified summary_line +#}{{ summary_line +}}{# +#}{% else +%}{# # Return the first line of the commit if there was a Parsing Error +#}{{ (commit.commit.message | string).split("\n", maxsplit=1)[0] +}}{% endif +%}{% endmacro %} {# - MACRO: format the release notice by: - - Capitalizing the description - - Adding an optional scope prefix -#}{% macro format_release_notice(commit) -%}{% set ns = namespace(full_description="") -%}{# -#}{% if commit.error is undefined -%}{% for paragraph in commit.release_notices -%}{% if paragraph | trim | length > 0 -%}{# -#}{% set paragraph_text = capitalize_first_letter_only(paragraph) | trim | safe -%}{# -#}{% set ns.full_description = [ - ns.full_description, - paragraph_text - ] | join("\n\n") -%}{# -#}{% endif -%}{% endfor -%}{# -#}{% set ns.full_description = ns.full_description | trim + MACRO: Extract issue references from a parsed commit object + - Stores the issue urls in the namespace object +#}{% macro extract_issue_link_references(ns, commit) +%}{% set issue_urls = [] %}{# -#}{% if commit.scope -%}{% set ns.full_description = "**%s**: %s" | format( - commit.scope, ns.full_description +#}{% if commit.linked_issues is defined and commit.linked_issues | length > 0 +%}{% for issue_num in commit.linked_issues +%}{# # Create an issue reference url +#}{% set _ = issue_urls.append( + format_link_reference( + issue_num | issue_url, + issue_num, + ) ) -%}{% endif +%}{% endfor %}{% endif %}{# -#}{{ ns.full_description -}}{% endmacro -%} - - -{# - MACRO: order commits alphabetically by scope and attribute - - Commits are sorted based on scope and then the attribute alphabetically - - Commits without scope are placed first and sorted alphabetically by the attribute - - parameter: ns (namespace) object with a commits list - - parameter: attr (string) attribute to sort by - - returns None but modifies the ns.commits list in place -#}{% macro order_commits_alphabetically_by_scope_and_attr(ns, attr) -%}{% set ordered_commits = [] -%}{# - # # Eliminate any ParseError commits from input set -#}{% set filtered_commits = ns.commits | rejectattr("error", "defined") | list -%}{# - # # grab all commits with no scope and sort alphabetically by attr -#}{% for commit in filtered_commits | rejectattr("scope") | sort(attribute=attr) -%}{% set _ = ordered_commits.append(commit) -%}{% endfor -%}{# - # # grab all commits with a scope and sort alphabetically by the scope and then attr -#}{% for commit in filtered_commits | selectattr("scope") | sort(attribute=(['scope', attr] | join(","))) -%}{% set _ = ordered_commits.append(commit) -%}{% endfor -%}{# - # # Return the ordered commits -#}{% set ns.commits = ordered_commits -%}{% endmacro -%} - - -{# - MACRO: apply smart ordering of commits objects based on alphabetized summaries and then scopes - - Commits are sorted based on the commit type and the commit message - - Commits are grouped by the commit type - - parameter: ns (namespace) object with a commits list - - returns None but modifies the ns.commits list in place -#}{% macro apply_alphabetical_ordering_by_descriptions(ns) -%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'descriptions.0') -%}{% endmacro -%} - - -{# - MACRO: apply smart ordering of commits objects based on alphabetized breaking changes and then scopes - - Commits are sorted based on the commit type and the commit message - - Commits are grouped by the commit type - - parameter: ns (namespace) object with a commits list - - returns None but modifies the ns.commits list in place -#}{% macro apply_alphabetical_ordering_by_brk_descriptions(ns) -%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'breaking_descriptions.0') + # # Store the issue urls in the namespace object +#}{% set ns.urls = issue_urls %}{% endmacro %} {# - MACRO: apply smart ordering of commits objects based on alphabetized release notices and then scopes - - Commits are sorted based on the commit type and the commit message - - Commits are grouped by the commit type - - parameter: ns (namespace) object with a commits list - - returns None but modifies the ns.commits list in place -#}{% macro apply_alphabetical_ordering_by_release_notices(ns) -%}{% set _ = order_commits_alphabetically_by_scope_and_attr(ns, 'release_notices.0') + MACRO: Create & return an non-inline RST link from a commit message + - Returns empty string if no PR/MR identifier is found +#}{% macro extract_pr_link_reference(commit) +%}{% if commit.error is undefined +%}{% set summary_line = commit.descriptions[0] +%}{# +#}{% if commit.linked_merge_request != "" +%}{# # Create a PR/MR reference url +#}{{ format_link_reference( + commit.linked_merge_request | pull_request_url, + "PR" ~ commit.linked_merge_request, + ) +}}{% endif +%}{% endif %}{% endmacro %} From ce311adc300992642676cd27c58d39ce28513131 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 26 May 2025 14:01:11 -0600 Subject: [PATCH 23/37] chore: update in code todos for v10 (#1266) --- src/semantic_release/cli/changelog_writer.py | 2 +- src/semantic_release/cli/config.py | 10 +++++----- src/semantic_release/commit_parser/_base.py | 2 +- src/semantic_release/commit_parser/angular.py | 11 ----------- src/semantic_release/commit_parser/scipy.py | 4 ++-- src/semantic_release/commit_parser/token.py | 2 +- src/semantic_release/commit_parser/util.py | 2 +- .../commit_parser/test_conventional.py | 14 ++++++-------- .../semantic_release/commit_parser/test_emoji.py | 14 ++++++-------- .../semantic_release/commit_parser/test_scipy.py | 14 ++++++-------- 10 files changed, 29 insertions(+), 46 deletions(-) diff --git a/src/semantic_release/cli/changelog_writer.py b/src/semantic_release/cli/changelog_writer.py index 96020b73a..65e387896 100644 --- a/src/semantic_release/cli/changelog_writer.py +++ b/src/semantic_release/cli/changelog_writer.py @@ -269,7 +269,7 @@ def generate_release_notes( environment(autoescape=False, template_dir=tpl_dir) ) - # TODO: Remove in v10 + # TODO: Remove in v11 release_notes_env.globals["context"] = release_notes_env.globals["ctx"] = { "history": history, "mask_initial_release": mask_initial_release, diff --git a/src/semantic_release/cli/config.py b/src/semantic_release/cli/config.py index 59df69834..1d2057a48 100644 --- a/src/semantic_release/cli/config.py +++ b/src/semantic_release/cli/config.py @@ -148,7 +148,7 @@ def interpret_output_format(self) -> Self: class ChangelogConfig(BaseModel): - # TODO: BREAKING CHANGE v10, move to DefaultChangelogTemplatesConfig + # TODO: BREAKING CHANGE v11, move to DefaultChangelogTemplatesConfig changelog_file: str = "" """Deprecated! Moved to 'default_templates.changelog_file'""" @@ -191,7 +191,7 @@ def changelog_file_deprecation_warning(cls, val: str) -> str: @model_validator(mode="after") def move_changelog_file(self) -> Self: - # TODO: Remove this method in v10 + # TODO: Remove this method in v11 if not self.changelog_file: return self @@ -441,7 +441,7 @@ def set_default_opts(self) -> Self: parser_opts_type = None # If the commit parser is a known one, pull the default options object from it if self.commit_parser in _known_commit_parsers: - # TODO: BREAKING CHANGE v10 + # TODO: BREAKING CHANGE v11 # parser_opts_type = ( # _known_commit_parsers[self.commit_parser] # .get_default_options() @@ -454,7 +454,7 @@ def set_default_opts(self) -> Self: try: # if its a custom parser, try to import it and pull the default options object type custom_class = dynamic_import(self.commit_parser) - # TODO: BREAKING CHANGE v10 + # TODO: BREAKING CHANGE v11 # parser_opts_type = custom_class.get_default_options().__class__ if hasattr(custom_class, "parser_options"): parser_opts_type = custom_class.parser_options @@ -695,7 +695,7 @@ def from_raw_config( # noqa: C901 ) from err commit_parser_opts_class = commit_parser_cls.parser_options - # TODO: Breaking change v10 + # TODO: Breaking change v11 # commit_parser_opts_class = commit_parser_cls.get_default_options().__class__ try: commit_parser = commit_parser_cls( diff --git a/src/semantic_release/commit_parser/_base.py b/src/semantic_release/commit_parser/_base.py index 04d2f56bd..a144e0945 100644 --- a/src/semantic_release/commit_parser/_base.py +++ b/src/semantic_release/commit_parser/_base.py @@ -74,7 +74,7 @@ def __init__(self, options: _OPTS | None = None) -> None: options if options is not None else self.get_default_options() ) - # TODO: BREAKING CHANGE v10, add abstract method for all custom parsers + # TODO: BREAKING CHANGE v11, add abstract method for all custom parsers # @staticmethod # @abstractmethod def get_default_options(self) -> _OPTS: diff --git a/src/semantic_release/commit_parser/angular.py b/src/semantic_release/commit_parser/angular.py index eeef82796..411ac844b 100644 --- a/src/semantic_release/commit_parser/angular.py +++ b/src/semantic_release/commit_parser/angular.py @@ -94,11 +94,9 @@ class AngularParserOptions(ParserOptions): default_bump_level: LevelBump = LevelBump.NO_RELEASE """The minimum bump level to apply to valid commit message.""" - # TODO: breaking change v10, change default to True parse_squash_commits: bool = False """Toggle flag for whether or not to parse squash commits""" - # TODO: breaking change v10, change default to True ignore_merge_commits: bool = False """Toggle flag for whether or not to ignore merge commits""" @@ -236,15 +234,11 @@ def commit_body_components_separator( ) -> dict[str, list[str]]: if (match := breaking_re.match(text)) and (brk_desc := match.group(1)): accumulator["breaking_descriptions"].append(brk_desc) - # TODO: breaking change v10, removes breaking change footers from descriptions - # return accumulator elif (match := self.notice_selector.match(text)) and ( notice := match.group("notice") ): accumulator["notices"].append(notice) - # TODO: breaking change v10, removes notice footers from descriptions - # return accumulator elif match := self.issue_selector.search(text): # if match := self.issue_selector.search(text): @@ -265,8 +259,6 @@ def commit_body_components_separator( accumulator["linked_issues"] = sort_numerically( set(accumulator["linked_issues"]).union(new_issue_refs) ) - # TODO: breaking change v10, removes resolution footers from descriptions - # return accumulator # Prevent appending duplicate descriptions if text not in accumulator["descriptions"]: @@ -287,9 +279,6 @@ def parse_message(self, message: str) -> ParsedMessageResult | None: linked_merge_request = "" if mr_match := self.mr_selector.search(parsed_subject): linked_merge_request = mr_match.group("mr_number") - # TODO: breaking change v10, removes PR number from subject/descriptions - # expects changelog template to format the line accordingly - # parsed_subject = self.pr_selector.sub("", parsed_subject).strip() body_components: dict[str, list[str]] = reduce( self.commit_body_components_separator, diff --git a/src/semantic_release/commit_parser/scipy.py b/src/semantic_release/commit_parser/scipy.py index 7e0e6b246..e6988ea83 100644 --- a/src/semantic_release/commit_parser/scipy.py +++ b/src/semantic_release/commit_parser/scipy.py @@ -145,7 +145,7 @@ class ScipyParserOptions(ParserOptions): one of these prefixes, it will not be considered a valid commit message. """ - # TODO: breaking v10, make consistent with AngularParserOptions + # TODO: breaking v11, make consistent with AngularParserOptions default_level_bump: LevelBump = LevelBump.NO_RELEASE """The minimum bump level to apply to valid commit message.""" @@ -161,7 +161,7 @@ def tag_to_level(self) -> dict[str, LevelBump]: return self._tag_to_level def __post_init__(self) -> None: - # TODO: breaking v10, remove as the name is now consistent + # TODO: breaking v11, remove as the name is now consistent self.default_bump_level = self.default_level_bump self._tag_to_level: dict[str, LevelBump] = { str(tag): level diff --git a/src/semantic_release/commit_parser/token.py b/src/semantic_release/commit_parser/token.py index a9bb254de..332f4283b 100644 --- a/src/semantic_release/commit_parser/token.py +++ b/src/semantic_release/commit_parser/token.py @@ -150,7 +150,7 @@ def from_parsed_message_result( """A convience method to create a ParsedCommit object from a ParsedMessageResult object and a Commit object.""" return ParsedCommit( bump=parsed_message_result.bump, - # TODO: breaking v10, swap back to type rather than category + # TODO: breaking v11, swap back to type rather than category type=parsed_message_result.category, scope=parsed_message_result.scope, descriptions=list(parsed_message_result.descriptions), diff --git a/src/semantic_release/commit_parser/util.py b/src/semantic_release/commit_parser/util.py index 9c1322b41..258e8224b 100644 --- a/src/semantic_release/commit_parser/util.py +++ b/src/semantic_release/commit_parser/util.py @@ -6,7 +6,7 @@ from re import MULTILINE, compile as regexp from typing import TYPE_CHECKING -# TODO: remove in v10 +# TODO: remove in v11 from semantic_release.helpers import ( sort_numerically, # noqa: F401 # TODO: maintained for compatibility ) diff --git a/tests/unit/semantic_release/commit_parser/test_conventional.py b/tests/unit/semantic_release/commit_parser/test_conventional.py index 02cd4f5de..9298c1f24 100644 --- a/tests/unit/semantic_release/commit_parser/test_conventional.py +++ b/tests/unit/semantic_release/commit_parser/test_conventional.py @@ -205,7 +205,7 @@ def test_parser_squashed_commit_bitbucket_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues @@ -388,7 +388,7 @@ def test_parser_squashed_commit_git_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues @@ -549,7 +549,7 @@ def test_parser_squashed_commit_github_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues @@ -699,7 +699,6 @@ def test_parser_return_subject_from_commit_message( @pytest.mark.parametrize( "message, subject, merge_request_number", - # TODO: in v10, we will remove the merge request number from the subject line [ # GitHub, Gitea style ( @@ -1109,10 +1108,9 @@ def test_parser_return_release_notices_from_commit_message( assert isinstance(result, ParsedCommit) assert tuple(notices) == result.release_notices - # TODO: v10, remove this - # full_description = str.join("\n\n", result.descriptions) - # full_notice = str.join("\n\n", result.release_notices) - # assert full_notice not in full_description + full_description = str.join("\n\n", result.descriptions) + full_notice = str.join("\n\n", result.release_notices) + assert full_notice not in full_description ############################## diff --git a/tests/unit/semantic_release/commit_parser/test_emoji.py b/tests/unit/semantic_release/commit_parser/test_emoji.py index ac7708ebb..c477579ec 100644 --- a/tests/unit/semantic_release/commit_parser/test_emoji.py +++ b/tests/unit/semantic_release/commit_parser/test_emoji.py @@ -136,7 +136,6 @@ def test_parser_return_linked_merge_request_from_commit_message( @pytest.mark.parametrize( "message, linked_issues", - # TODO: in v10, we will remove the issue reference footers from the descriptions [ *[ # GitHub, Gitea, GitLab style @@ -510,10 +509,9 @@ def test_parser_return_release_notices_from_commit_message( assert isinstance(result, ParsedCommit) assert tuple(notices) == result.release_notices - # TODO: v10, remove this - # full_description = str.join("\n\n", result.descriptions) - # full_notice = str.join("\n\n", result.release_notices) - # assert full_notice not in full_description + full_description = str.join("\n\n", result.descriptions) + full_notice = str.join("\n\n", result.release_notices) + assert full_notice not in full_description @pytest.mark.parametrize( @@ -689,7 +687,7 @@ def test_parser_squashed_commit_bitbucket_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues @@ -878,7 +876,7 @@ def test_parser_squashed_commit_git_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues @@ -1042,7 +1040,7 @@ def test_parser_squashed_commit_github_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues diff --git a/tests/unit/semantic_release/commit_parser/test_scipy.py b/tests/unit/semantic_release/commit_parser/test_scipy.py index 46f70b211..ce714c0bc 100644 --- a/tests/unit/semantic_release/commit_parser/test_scipy.py +++ b/tests/unit/semantic_release/commit_parser/test_scipy.py @@ -615,7 +615,7 @@ def test_parser_squashed_commit_bitbucket_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues @@ -798,7 +798,7 @@ def test_parser_squashed_commit_git_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues @@ -959,7 +959,7 @@ def test_parser_squashed_commit_github_squash_style( assert expected["type"] == result.type # Optional assert expected.get("scope", "") == result.scope - # TODO: v10 change to tuples + # TODO: v11 change to tuples assert expected.get("descriptions", []) == result.descriptions assert expected.get("breaking_descriptions", []) == result.breaking_descriptions assert expected.get("linked_issues", ()) == result.linked_issues @@ -968,7 +968,6 @@ def test_parser_squashed_commit_github_squash_style( @pytest.mark.parametrize( "message, linked_issues", - # TODO: in v10, we will remove the issue reference footers from the descriptions [ *[ # GitHub, Gitea, GitLab style @@ -1331,10 +1330,9 @@ def test_parser_return_release_notices_from_commit_message( assert isinstance(result, ParsedCommit) assert tuple(notices) == result.release_notices - # TODO: v10, remove this - # full_description = str.join("\n\n", result.descriptions) - # full_notice = str.join("\n\n", result.release_notices) - # assert full_notice not in full_description + full_description = str.join("\n\n", result.descriptions) + full_notice = str.join("\n\n", result.release_notices) + assert full_notice not in full_description def test_parser_ignore_merge_commit( From bb8f2ee237ef1269cbf0e4f8137b3a099d8b9ea0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 7 Jun 2025 15:06:53 -0600 Subject: [PATCH 24/37] chore(config): add version stamp to github-actions examples in docs (#1272) * chore(scripts): drop code to bump versions of github-action examples in documentation --- pyproject.toml | 2 ++ scripts/bump_version_in_docs.py | 24 ------------------------ 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7a3aa918a..8dc8935a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -414,6 +414,8 @@ build_command = """ major_on_zero = true version_variables = [ "src/gh_action/requirements.txt:python-semantic-release:nf", + "docs/configuration/automatic-releases/github-actions.rst:python-semantic-release/python-semantic-release:tf", + "docs/configuration/automatic-releases/github-actions.rst:python-semantic-release/publish-action:tf", ] version_toml = ["pyproject.toml:project.version"] diff --git a/scripts/bump_version_in_docs.py b/scripts/bump_version_in_docs.py index 7c6104791..3155cd0c7 100644 --- a/scripts/bump_version_in_docs.py +++ b/scripts/bump_version_in_docs.py @@ -12,25 +12,6 @@ tag_replace_pattern = regexp(r"\$(NEW_RELEASE_TAG|{NEW_RELEASE_TAG})") -def update_github_actions_example(filepath: Path, release_tag: str) -> None: - psr_regex = regexp(r"(uses: python-semantic-release/python-semantic-release)@\S+$") - psr_publish_action_regex = regexp( - r"(uses: python-semantic-release/publish-action)@\S+$" - ) - file_content_lines: list[str] = filepath.read_text().splitlines() - - for regex in [psr_regex, psr_publish_action_regex]: - file_content_lines = list( - map( - lambda line, regex=regex: regex.sub(r"\1@" + release_tag, line), # type: ignore[misc] - file_content_lines, - ) - ) - - print(f"Bumping version in {filepath} to", release_tag) - filepath.write_text(str.join("\n", file_content_lines) + "\n") - - def envsubst(filepath: Path, version: str, release_tag: str) -> None: file_content = filepath.read_text() @@ -59,10 +40,5 @@ def envsubst(filepath: Path, version: str, release_tag: str) -> None: print("NEW_VERSION environment variable is not set") exit(1) - update_github_actions_example( - DOCS_DIR / "configuration" / "automatic-releases" / "github-actions.rst", - new_release_tag, - ) - for doc_file in DOCS_DIR.rglob("*.rst"): envsubst(filepath=doc_file, version=new_version, release_tag=new_release_tag) From fbb63ec76142ea903d8a0401369ec251abbec0fe Mon Sep 17 00:00:00 2001 From: Bart Dorlandt Date: Sat, 7 Jun 2025 23:12:09 +0200 Subject: [PATCH 25/37] docs(github-actions): clarify with examples of the `root_options` v10 migration change (#1271) --- .../automatic-releases/github-actions.rst | 16 +++---- docs/upgrading/10-upgrade.rst | 47 ++++++++++++++++--- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index 4fc5022af..93024e3dd 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -371,9 +371,9 @@ to the remote repository. This option is equivalent to adding either ``--push`` """""""""""""""" .. important:: - This option has been removed in v10.0.0 and newer because of a - command injection vulnerability. Please update as to v10.0.0 as soon - as possible. + This option has been removed in v10.0.0 and newer because of a command injection + vulnerability. Please update as to v10.0.0 as soon as possible. See + :ref:`Upgrading to v10 ` for more information. Additional options for the main ``semantic-release`` command, which will come before the :ref:`version ` subcommand. @@ -382,7 +382,7 @@ before the :ref:`version ` subcommand. .. code:: yaml - - uses: python-semantic-release/python-semantic-release@v10.0.2 + - uses: python-semantic-release/python-semantic-release@v9 with: root_options: "-vv --noop" @@ -688,9 +688,9 @@ This is useful for testing the action without actually publishing anything. """""""""""""""" .. important:: - This option has been removed in v10.0.0 and newer because of a - command injection vulnerability. Please update as to v10.0.0 as soon - as possible. + This option has been removed in v10.0.0 and newer because of a command injection + vulnerability. Please update as to v10.0.0 as soon as possible. See + :ref:`Upgrading to v10 ` for more information. Additional options for the main ``semantic-release`` command, which will come before the :ref:`publish ` subcommand. @@ -699,7 +699,7 @@ before the :ref:`publish ` subcommand. .. code:: yaml - - uses: python-semantic-release/publish-action@v10.0.2 + - uses: python-semantic-release/publish-action@v9 with: root_options: "-vv --noop" diff --git a/docs/upgrading/10-upgrade.rst b/docs/upgrading/10-upgrade.rst index ffd6b0276..7cb4e03be 100644 --- a/docs/upgrading/10-upgrade.rst +++ b/docs/upgrading/10-upgrade.rst @@ -42,18 +42,51 @@ This vulnerability existed in both the For the main :ref:`python-semantic-release/python-semantic-release ` action, the following inputs are now available (in place of the old ``root_options`` parameter): +:ref:`gh_actions-psr-inputs-config_file`, :ref:`gh_actions-psr-inputs-noop`, +:ref:`gh_actions-psr-inputs-strict`, and :ref:`gh_actions-psr-inputs-verbosity`. -- :ref:`gh_actions-psr-inputs-config_file` -- :ref:`gh_actions-psr-inputs-noop` -- :ref:`gh_actions-psr-inputs-strict` -- :ref:`gh_actions-psr-inputs-verbosity` + **Example migration** + + If you previously had the following in your GitHub Actions workflow file: + + .. code:: yaml + + - uses: python-semantic-release/python-semantic-release@v9 + with: + root_options: "-vv --strict" + + It would be updated to: + + .. code:: yaml + + - uses: python-semantic-release/python-semantic-release@v10 + with: + strict: true + verbosity: 2 For the :ref:`python-semantic-release/publish-action ` action, the following inputs are now available (in place of the old ``root_options`` parameter): +:ref:`gh_actions-publish-inputs-config_file`, :ref:`gh_actions-publish-inputs-noop`, +and :ref:`gh_actions-publish-inputs-verbosity`. + + **Example migration** + + If you previously had the following in your GitHub Actions workflow file: + + .. code:: yaml + + - uses: python-semantic-release/publish-action@v9 + with: + root_options: "-v -c /path/to/releaserc.yaml" + + It would be updated to: + + .. code:: yaml -- :ref:`gh_actions-publish-inputs-config_file` -- :ref:`gh_actions-publish-inputs-noop` -- :ref:`gh_actions-publish-inputs-verbosity` + - uses: python-semantic-release/publish-action@v10 + with: + config_file: /path/to/releaserc.yaml + verbosity: 1 .. _upgrade_v10-changelog_format-1_line_commit_subjects: From b31b5405d0f40f85af83e16ad7e6b78999319b6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 7 Jun 2025 14:35:18 -0700 Subject: [PATCH 26/37] build(deps-dev): bump mypy from 1.15.0 to 1.16.0 (#1270) * chore(config): update mypy version in `pre-commit` configuration --- .pre-commit-config.yaml | 4 ++-- pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f80f12a05..3f68bac6c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,14 +50,14 @@ repos: name: ruff (format) - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.15.0" + rev: "v1.16.0" hooks: - id: mypy additional_dependencies: - "pydantic>=2,<3" - "types-requests" log_file: "mypy.log" - files: "^src/.*" + files: "^(src|tests)/.*" pass_filenames: false - repo: https://github.com/pre-commit/pygrep-hooks diff --git a/pyproject.toml b/pyproject.toml index 8dc8935a4..df2551f80 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ dev = [ "ruff == 0.6.1" ] mypy = [ - "mypy == 1.15.0", + "mypy == 1.16.0", "types-Deprecated ~= 1.2", "types-requests ~= 2.32.0", "types-pyyaml ~= 6.0", From 8da19f113f4f00163ef6f30f14e9b430d4476fc0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 7 Jun 2025 15:26:04 -0600 Subject: [PATCH 27/37] ci(deps): bump `mikepenz/action-junit-report@v5.5.1` action to `v5.6.0` --- .github/workflows/validate.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index f20aa9700..bb8df05c1 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -195,7 +195,7 @@ jobs: --junit-xml=tests/reports/pytest-results.xml - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857 # v5.5.1 + uses: mikepenz/action-junit-report@65fe03598d8d251738592a497a9e8547a5c48eaa # v5.6.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -285,7 +285,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857 # v5.5.1 + uses: mikepenz/action-junit-report@65fe03598d8d251738592a497a9e8547a5c48eaa # v5.6.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -383,7 +383,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@cf701569b05ccdd861a76b8607a66d76f6fd4857 # v5.5.1 + uses: mikepenz/action-junit-report@65fe03598d8d251738592a497a9e8547a5c48eaa # v5.6.0 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml From f772121f4f07000e6cb8e720c50c5f2662fb67b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 18:32:34 +0000 Subject: [PATCH 28/37] ci(deps): bump `docker/build-push-action@v6.17.0` to `v6.18.0` --- .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 bb8df05c1..f8487cf39 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -428,7 +428,7 @@ jobs: - name: Build | Action Container id: container-builder - uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: ${{ env.ACTION_SRC_DIR }} load: true # add to `docker images` From 99fc9ccabbae9adf5646731591080366eacbe03c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Jun 2025 20:47:03 -0700 Subject: [PATCH 29/37] build(deps): expand `python-gitlab` dependency to include `v6.0.0` (#1273) --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index df2551f80..5d4f90314 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,7 @@ dependencies = [ "gitpython ~= 3.0", "requests ~= 2.25", "jinja2 ~= 3.1", - "python-gitlab >= 4.0.0, < 6.0.0", + "python-gitlab >= 4.0.0, < 7.0.0", "tomlkit ~= 0.11", "dotty-dict ~= 1.3", "importlib-resources ~= 6.0", From de623344cd18b3dbe05823eb90fdd010c5505c92 Mon Sep 17 00:00:00 2001 From: JonZeolla Date: Thu, 12 Jun 2025 00:02:46 -0400 Subject: [PATCH 30/37] feat(cmd-version): always stage version stamped files & changelog even with `--no-commit` (#1214) Resolves: #1211 * test(cmd-version): update version stamp test to verify changes are staged during execution * docs(configuration-guide): add how-to guide for `uv` integration * docs(cmd-version): improve command description & include common uses --------- Co-authored-by: codejedi365 --- docs/api/commands.rst | 77 ++++- .../configuration-guides/index.rst | 14 + .../configuration-guides/uv_integration.rst | 327 ++++++++++++++++++ docs/configuration/index.rst | 16 +- src/semantic_release/cli/commands/version.py | 17 +- tests/e2e/cmd_version/test_version_stamp.py | 11 +- 6 files changed, 426 insertions(+), 36 deletions(-) create mode 100644 docs/configuration/configuration-guides/index.rst create mode 100644 docs/configuration/configuration-guides/uv_integration.rst diff --git a/docs/api/commands.rst b/docs/api/commands.rst index 344c7f5f4..3dca77474 100644 --- a/docs/api/commands.rst +++ b/docs/api/commands.rst @@ -96,25 +96,73 @@ pipeline, while omitting this flag would allow the pipeline to continue to run. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Detect the semantically correct next version that should be applied to your -project. +project and release it. -By default: +By default (in order): - * Write this new version to the project metadata locations - specified in the configuration file - * Build the project using :ref:`config-build_command`, if specified - * Create a new commit with these locations and any other assets configured - to be included in a release - * Tag this commit according the configured format, with a tag that uniquely - identifies the version being released - * Push the new tag and commit to the remote for the repository - * Create a release (if supported) in the remote VCS for this tag + #. Write this new version to the project metadata locations + specified in the configuration file + + #. Update the changelog file with the new version and any changes + introduced since the last release, using the configured changelog template + + #. Build the project using :ref:`config-build_command`, if specified + + #. Create a new commit with these locations and any other assets configured + to be included in a release + + #. Tag this commit according the configured format, with a tag that uniquely + identifies the version being released + + #. Push the new tag and commit to the remote for the repository + + #. Create a release in the remote VCS for this tag (if supported) + +All of these steps can be toggled on or off using the command line options +described below. Some of the steps rely on others, so some options may implicitly +disable others. Changelog generation is done identically to the way it is done in :ref:`cmd-changelog`, but this command additionally ensures the updated changelog is included in the release commit that is made. + **Common Variations** + + .. code-block:: bash + + # Print the next version that will be applied + semantic-release version --print + + # Print the next version that will be applied, including the tag prefix + semantic-release version --print-tag + + # Print the last released version + semantic-release version --print-last-released + + # Print the last released version, including the tag prefix + semantic-release version --print-last-released-tag + + # Only stamp the next version in the project metadata locations + semantic-release version --no-changelog --skip-build --no-commit --no-tag + + # Stamp the version, update the changelog, and run the build command, then stop + semantic-release version --no-commit --no-tag + + # Make all local changes but do not publish them to the remote (changelog, build, commit & tag) + semantic-release version --no-push + + # Don't ever create a changelog (but do everything else) + semantic-release version --no-changelog + + # Don't create a release in the remote VCS (but do publish the commit and tag) + semantic-release version --no-vcs-release + + # Do everything + semantic-release version + + .. seealso:: + - :ref:`Ultraviolet (uv) integration ` - :ref:`cmd-changelog` - :ref:`changelog-templates` - :ref:`config-tag_format` @@ -122,6 +170,7 @@ commit that is made. - :ref:`config-version_toml` - :ref:`config-version_variables` + .. _cmd-version-options: Options: @@ -362,9 +411,9 @@ Whether or not to push new commits and/or tags to the remote repository. ``--vcs-release/--no-vcs-release`` ********************************** -Whether or not to create a "release" in the remote VCS service, if supported. Currently -releases in GitHub and Gitea remotes are supported. If releases aren't supported in a -remote VCS, this option will not cause a command failure, but will produce a warning. +Whether or not to create a "release" in the remote VCS service, if supported. If +releases aren't supported in a remote VCS, this option will not cause a command +failure, but will produce a warning. **Default:** ``--no-vcs-release`` if ``--no-push`` is supplied (including where this is implied by supplying only ``--no-commit``), otherwise ``--vcs-release`` diff --git a/docs/configuration/configuration-guides/index.rst b/docs/configuration/configuration-guides/index.rst new file mode 100644 index 000000000..70024dd11 --- /dev/null +++ b/docs/configuration/configuration-guides/index.rst @@ -0,0 +1,14 @@ +.. _config-guides: + +Configuration Guides +==================== + +This section provides detailed guides on how to configure PSR for various use cases and +integrations. It is recommended to complete the +:ref:`Getting Started Guide ` first before diving into these +more specific configurations. + +.. toctree:: + :maxdepth: 1 + + UV Project Setup diff --git a/docs/configuration/configuration-guides/uv_integration.rst b/docs/configuration/configuration-guides/uv_integration.rst new file mode 100644 index 000000000..cb31e79c1 --- /dev/null +++ b/docs/configuration/configuration-guides/uv_integration.rst @@ -0,0 +1,327 @@ +.. _config-guides-uv_integration: + +Ultraviolet (``uv``) Integration +================================ + +.. _uv: https://docs.astral.sh/uv/ + +`uv`_ is an extremely fast Python package and project manager that +provides a modern alternative to `pip `_ +and `venv `_. It provides a lot +of features that solve the common problems of Python package management but +it also introduces a few quirks that need to be taken into account when using +Python Semantic Release. + +.. important:: + + **Prerequisite:** Make sure you have run through the + :ref:`Getting Started Guide ` before proceeding with + this guide. + + +Updating the ``uv.lock`` +------------------------ + +One of the best features of ``uv`` is that it automatically generates a lock file +(``uv.lock``) that contains the exact versions of all the dependencies used in +your project. The lock file is generated when you run the ``uv install`` command, +and it is used to ensure that CI workflows are repeatable and development environments +are consistent. + +When creating a new release using Python Semantic Release, PSR will update the version +in the project's definition file (e.g., ``pyproject.toml``) to indicate the new version. +Unfortunately, this action will cause ``uv`` to fail on the next execution because the +lock file will be out of sync with the project's definition file. There are two ways to +resolve this issue depending on your preference: + +#. **Add a step to your build command**: Modify your + :ref:`semantic_release.build_command ` to include the command + to update the lock file and stage it for commit. This is commonly used with the + :ref:`GitHub Action ` and other CI/CD tools when you are building + the artifact at the time of release. + + .. code-block:: toml + + [tool.semantic_release] + build_command = """ + uv lock --offline + git add uv.lock + uv build + """ + + The ``--offline`` flag is used to ensure that the lock file is updated without + updating any dependency versions. The intent of this call is **ONLY** to update + the version of your project within the lock file after PSR has updated the version + in your project's definition file (e.g., ``pyproject.toml``). When you are running + PSR, you have already tested the project as is and you don't want to actually + update the dependencies if a new one just became available. + + If you are using the :ref:`PSR GitHub Action `, you will need to add an + installation command for ``uv`` to the :ref:`build_command ` + because the action runs in a Docker environment does not include ``uv`` by default. + The best way to ensure that the correct version of ``uv`` is installed is to define + the version of ``uv`` in an optional dependency list (e.g. ``build``). This will + also help with other automated tools like Dependabot or Renovate to keep the version + of ``uv`` up to date. + + .. code-block:: toml + + [project.optional-dependencies] + build = ["uv ~= 0.7.12"] + + [tool.semantic_release] + build_command = """ + python -m pip install -e .[build] + uv lock --offline + git add uv.lock + uv build + """ + +#. **Stamp the code first & then separately run release**: If you prefer to not modify the + build command, then you will need to run the ``uv lock --offline`` command prior to actually + creating the release. Essentially, you will run PSR twice: (1) once to update the version + in the project's definition file, and (2) a second time to generate the release. + + The intent of the ``uv lock --offline`` command is **ONLY** to update the version of your + project within the lock file after PSR has updated the version in your project's definition + file (e.g., ``pyproject.toml``). When you are running PSR, you have already tested the project + as is and you don't want to actually update the dependencies if a new one just became available. + + .. code-block:: bash + + # 1. PSR stamps version into files (nothing else) + # don't build the changelog (especially in update mode) + semantic-release -v version --skip-build --no-commit --no-tag --no-changelog + + # 2. run UV lock as pyproject.toml is updated with the next version + uv lock --offline + + # 3. stage the lock file to ensure it is included in the PSR commit + git add uv.lock + + # 4. run PSR fully to create release + semantic-release -v version + +**Advanced Example** + +Of course, you can mix and match these 2 approaches as needed. If PSR's pipeline was using +``uv``, we would have a mixture of the 2 approaches because we run the build in a separate +job from the release. In our case, PSR would also need to carry the lock file as a workflow +artifact along the pipeline for the release job to commit it. This advanced workflow would +look like this: + +.. code-block:: text + + # File: .tool-versions + uv 0.7.12 + +.. code-block:: text + + # File: .python-version + 3.11.11 + +.. code-block:: toml + + # File: pyproject.toml + [project.optional-dependencies] + build = ["python-semantic-release ~= 10.0"] + test = ["pytest ~= 8.0"] + + [tool.semantic_release] + build_command = """ + uv lock --offline + uv build + """ + +.. code-block:: yaml + + # File: .github/workflows/release.yml + on: + push: + branches: + - main + + jobs: + + build: + runs-on: ubuntu-latest + permissions: + contents: read + env: + dist_artifacts_name: dist + dist_artifacts_dir: dist + lock_file_artifact: uv.lock + steps: + - name: Setup | Checkout Repository at workflow sha + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.sha }} + fetch-depth: 0 + + - name: Setup | Force correct release branch on workflow sha + run: git checkout -B ${{ github.ref_name }} + + - name: Setup | Install uv + uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 # v4.0.2 + + - name: Setup | Install Python & Project dependencies + run: uv sync --extra build + + - name: Build | Build next version artifacts + id: version + env: + GH_TOKEN: "none" + run: uv run semantic-release -v version --no-commit --no-tag + + - name: Upload | Distribution Artifacts + if: ${{ steps.version.outputs.released == 'true' }} + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ env.dist_artifacts_name }} + path: ${{ format('{0}/**', env.dist_artifacts_dir) }} + if-no-files-found: error + retention-days: 2 + + - name: Upload | Lock File Artifact + if: ${{ steps.version.outputs.released == 'true' }} + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: ${{ env.lock_file_artifact }} + path: ${{ env.lock_file_artifact }} + if-no-files-found: error + retention-days: 2 + + outputs: + new-release-detected: ${{ steps.version.outputs.released }} + new-release-version: ${{ steps.version.outputs.version }} + new-release-tag: ${{ steps.version.outputs.tag }} + new-release-is-prerelease: ${{ steps.version.outputs.is_prerelease }} + distribution-artifacts: ${{ env.dist_artifacts_name }} + lock-file-artifact: ${{ env.lock_file_artifact }} + + + test-e2e: + needs: build + runs-on: ubuntu-latest + steps: + - name: Setup | Checkout Repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.sha }} + fetch-depth: 1 + + - name: Setup | Download Distribution Artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + if: ${{ needs.build.outputs.new-release-detected == 'true' }} + id: artifact-download + with: + name: ${{ needs.build.outputs.distribution-artifacts }} + path: ./dist + + - name: Setup | Install uv + uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 # v4.0.2 + + - name: Setup | Install Python & Project dependencies + run: uv sync --extra test + + - name: Setup | Install distribution artifact + if: ${{ steps.artifact-download.outcome == 'success' }} + run: | + uv pip uninstall my-package + uv pip install dist/python_semantic_release-*.whl + + - name: Test | Run pytest + run: uv run pytest -vv tests/e2e + + + release: + runs-on: ubuntu-latest + needs: + - build + - test-e2e + + if: ${{ needs.build.outputs.new-release-detected == 'true' }} + + concurrency: + group: ${{ github.workflow }}-release-${{ github.ref_name }} + cancel-in-progress: false + + permissions: + contents: write + + steps: + - name: Setup | Checkout Repository on Release Branch + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.ref_name }} + fetch-depth: 0 + + - name: Setup | Force release branch to be at workflow sha + run: git reset --hard ${{ github.sha }} + + - name: Setup | Install uv + uses: asdf-vm/actions/install@1902764435ca0dd2f3388eea723a4f92a4eb8302 # v4.0.2 + + - name: Setup | Install Python & Project dependencies + run: uv sync --extra build + + - name: Setup | Download Build Artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + id: artifact-download + with: + name: ${{ needs.build.outputs.distribution-artifacts }} + path: dist + + - name: Setup | Download Lock File Artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: ${{ needs.build.outputs.lock-file-artifact }} + + - name: Setup | Stage Lock File for Version Commit + run: git add uv.lock + + - name: Release | Create Release + id: release + shell: bash + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + bash .github/workflows/verify_upstream.sh + uv run semantic-release -v --strict version --skip-build + uv run semantic-release publish + + outputs: + released: ${{ steps.release.outputs.released }} + new-release-version: ${{ steps.release.outputs.version }} + new-release-tag: ${{ steps.release.outputs.tag }} + + + deploy: + name: Deploy + runs-on: ubuntu-latest + if: ${{ needs.release.outputs.released == 'true' && github.repository == 'python-semantic-release/my-package' }} + needs: + - build + - release + + environment: + name: pypi + url: https://pypi.org/project/my-package/ + + permissions: + id-token: write + + steps: + - name: Setup | Download Build Artifacts + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + id: artifact-download + with: + name: ${{ needs.build.outputs.distribution-artifacts }} + path: dist + + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc # v1.12.4 + with: + packages-dir: dist + print-hash: true + verbose: true diff --git a/docs/configuration/index.rst b/docs/configuration/index.rst index 3b5dade61..1044ae10d 100644 --- a/docs/configuration/index.rst +++ b/docs/configuration/index.rst @@ -3,16 +3,22 @@ Configuration ============= -Python Semantic Release is highly configurable, allowing you to tailor it to your project's needs. It supports -various runtime environments and can be integrated with different CI/CD services. +Python Semantic Release is highly configurable, allowing you to tailor it to your +project's needs. It supports various runtime environments and can be integrated with +different CI/CD services. -1. Check out the :ref:`Configuration Options ` to customize your release process. +#. Check out our set of :ref:`configuration guides ` to help walk + you through the set up of common project customizations. -2. Configure your :ref:`CI/CD services ` to use Python Semantic Release. +#. Dive in deep and explore the full set of possible :ref:`customization options `. + +#. Go Automatic and Configure your :ref:`CI/CD services ` to use Python + Semantic Release. .. toctree:: :maxdepth: 1 :hidden: - Configuration Options + Guides + Options automatic-releases/index diff --git a/src/semantic_release/cli/commands/version.py b/src/semantic_release/cli/commands/version.py index 8a8cf94a1..d3aed80f3 100644 --- a/src/semantic_release/cli/commands/version.py +++ b/src/semantic_release/cli/commands/version.py @@ -142,14 +142,10 @@ def apply_version_to_source_files( if not noop: logger.debug("Updating version %s in repository files...", version) - paths = list( - map( - lambda decl, new_version=version, noop=noop: ( # type: ignore[misc] - decl.update_file_w_version(new_version=new_version, noop=noop) - ), - version_declarations, - ) - ) + paths = [ + decl.update_file_w_version(new_version=version, noop=noop) + for decl in version_declarations + ] repo_filepaths = [ str(updated_file.relative_to(repo_dir)) @@ -650,10 +646,9 @@ def version( # noqa: C901 credential_masker=runtime.masker, ) - # Preparing for committing changes + # Preparing for committing changes; we always stage files even if we're not committing them in order to support a two-stage commit + project.git_add(paths=all_paths_to_add, noop=opts.noop) if commit_changes: - project.git_add(paths=all_paths_to_add, noop=opts.noop) - # NOTE: If we haven't modified any source code then we skip trying to make a commit # and any tag that we apply will be to the HEAD commit (made outside of # running PSR diff --git a/tests/e2e/cmd_version/test_version_stamp.py b/tests/e2e/cmd_version/test_version_stamp.py index a12059f37..182e9406b 100644 --- a/tests/e2e/cmd_version/test_version_stamp.py +++ b/tests/e2e/cmd_version/test_version_stamp.py @@ -3,7 +3,7 @@ import json from pathlib import Path from textwrap import dedent -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, cast import pytest import tomlkit @@ -61,8 +61,6 @@ def test_version_only_stamp_version( post_mocker: MagicMock, example_pyproject_toml: Path, example_project_dir: ExProjectDir, - example_changelog_md: Path, - example_changelog_rst: Path, ) -> None: repo = repo_result["repo"] version_file = example_project_dir.joinpath( @@ -97,10 +95,11 @@ def test_version_only_stamp_version( head_after = repo.head.commit tags_after = {tag.name for tag in repo.tags} tags_set_difference = set.difference(tags_after, tags_before) - differing_files = [ + actual_staged_files = [ # Make sure filepath uses os specific path separators str(Path(file)) - for file in str(repo.git.diff(name_only=True)).splitlines() + # Changed files should always be staged + for file in cast("str", repo.git.diff(staged=True, name_only=True)).splitlines() ] pyproject_toml_after = tomlkit.loads( example_pyproject_toml.read_text(encoding="utf-8") @@ -125,7 +124,7 @@ def test_version_only_stamp_version( assert post_mocker.call_count == 0 # no vcs release creation occurred # Files that should receive version change - assert expected_changed_files == differing_files + assert expected_changed_files == actual_staged_files # Compare pyproject.toml assert pyproject_toml_before == pyproject_toml_after From f9e152fb36cd2e590fe8c2bf85bbff08f7fc1c52 Mon Sep 17 00:00:00 2001 From: semantic-release Date: Thu, 12 Jun 2025 04:19:36 +0000 Subject: [PATCH 31/37] 10.1.0 Automatically generated by python-semantic-release --- CHANGELOG.rst | 35 +++++++++++++++++++ .../automatic-releases/github-actions.rst | 14 ++++---- pyproject.toml | 2 +- src/gh_action/requirements.txt | 2 +- 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 09d90b45c..f7255ca13 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,6 +4,41 @@ CHANGELOG ========= +.. _changelog-v10.1.0: + +v10.1.0 (2025-06-12) +==================== + +✨ Features +----------- + +* **cmd-version**: Always stage version stamped files & changelog even with ``--no-commit``, closes + `#1211`_ (`PR#1214`_, `de62334`_) + +📖 Documentation +---------------- + +* **cmd-version**: Improve command description & include common uses (`PR#1214`_, `de62334`_) + +* **configuration-guide**: Add how-to guide for ``uv`` integration (`PR#1214`_, `de62334`_) + +* **github-actions**: Clarify with examples of the ``root_options`` v10 migration change + (`PR#1271`_, `fbb63ec`_) + +⚙️ Build System +---------------- + +* **deps**: Expand ``python-gitlab`` dependency to include ``v6.0.0`` (`PR#1273`_, `99fc9cc`_) + +.. _#1211: https://github.com/python-semantic-release/python-semantic-release/issues/1211 +.. _99fc9cc: https://github.com/python-semantic-release/python-semantic-release/commit/99fc9ccabbae9adf5646731591080366eacbe03c +.. _de62334: https://github.com/python-semantic-release/python-semantic-release/commit/de623344cd18b3dbe05823eb90fdd010c5505c92 +.. _fbb63ec: https://github.com/python-semantic-release/python-semantic-release/commit/fbb63ec76142ea903d8a0401369ec251abbec0fe +.. _PR#1214: https://github.com/python-semantic-release/python-semantic-release/pull/1214 +.. _PR#1271: https://github.com/python-semantic-release/python-semantic-release/pull/1271 +.. _PR#1273: https://github.com/python-semantic-release/python-semantic-release/pull/1273 + + .. _changelog-v10.0.2: v10.0.2 (2025-05-26) diff --git a/docs/configuration/automatic-releases/github-actions.rst b/docs/configuration/automatic-releases/github-actions.rst index 93024e3dd..5d81ea7d0 100644 --- a/docs/configuration/automatic-releases/github-actions.rst +++ b/docs/configuration/automatic-releases/github-actions.rst @@ -873,14 +873,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@v10.0.2 + uses: python-semantic-release/python-semantic-release@v10.1.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@v10.0.2 + uses: python-semantic-release/publish-action@v10.1.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} @@ -979,7 +979,7 @@ The equivalent GitHub Action configuration would be: - name: Action | Semantic Version Release # Adjust tag with desired version if applicable. - uses: python-semantic-release/python-semantic-release@v10.0.2 + uses: python-semantic-release/python-semantic-release@v10.1.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} force: patch @@ -1038,14 +1038,14 @@ Publish Action. - name: Release submodule 1 id: release-submod-1 - uses: python-semantic-release/python-semantic-release@v10.0.2 + uses: python-semantic-release/python-semantic-release@v10.1.0 with: directory: ${{ env.SUBMODULE_1_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} - name: Release submodule 2 id: release-submod-2 - uses: python-semantic-release/python-semantic-release@v10.0.2 + uses: python-semantic-release/python-semantic-release@v10.1.0 with: directory: ${{ env.SUBMODULE_2_DIR }} github_token: ${{ secrets.GITHUB_TOKEN }} @@ -1057,7 +1057,7 @@ Publish Action. # ------------------------------------------------------------------- # - name: Publish | Upload package 1 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.2 + uses: python-semantic-release/publish-action@v10.1.0 if: steps.release-submod-1.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_1_DIR }} @@ -1065,7 +1065,7 @@ Publish Action. tag: ${{ steps.release-submod-1.outputs.tag }} - name: Publish | Upload package 2 to GitHub Release Assets - uses: python-semantic-release/publish-action@v10.0.2 + uses: python-semantic-release/publish-action@v10.1.0 if: steps.release-submod-2.outputs.released == 'true' with: directory: ${{ env.SUBMODULE_2_DIR }} diff --git a/pyproject.toml b/pyproject.toml index 5d4f90314..541520dc3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta" [project] name = "python-semantic-release" -version = "10.0.2" +version = "10.1.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 835d01792..44b3d8407 100644 --- a/src/gh_action/requirements.txt +++ b/src/gh_action/requirements.txt @@ -1 +1 @@ -python-semantic-release == 10.0.2 +python-semantic-release == 10.1.0 From f0da1697914112ed38e5dcc4cbc6d0560351f040 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Thu, 12 Jun 2025 00:02:22 -0600 Subject: [PATCH 32/37] chore(dependabot): update config for action's new Dockerfile file location (#1276) --- .github/dependabot.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml index 85652f7c5..b1115d0c4 100644 --- a/.github/dependabot.yaml +++ b/.github/dependabot.yaml @@ -20,7 +20,7 @@ updates: # Maintain dependencies for Docker (ie our GitHub Action) - package-ecosystem: "docker" - directory: "/" + directory: "src/gh_action" schedule: interval: "monthly" labels: From 11d43e752e44dcb7455e2ffd85b8ea1a9c2947c1 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Wed, 11 Jun 2025 23:56:46 -0600 Subject: [PATCH 33/37] ci(deps): bump `python-semantic-release@v10.0.2` action to `v10.1.0` --- .github/workflows/cicd.yml | 2 +- .github/workflows/validate.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index 512454ab0..e3d962ff5 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -145,7 +145,7 @@ jobs: - name: Release | Python Semantic Release id: release - uses: python-semantic-release/python-semantic-release@1a324000f2251a9e722e77b128bf72712653813f # v10.0.2 + uses: python-semantic-release/python-semantic-release@f9e152fb36cd2e590fe8c2bf85bbff08f7fc1c52 # v10.1.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} verbosity: 1 diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index f8487cf39..0cb01af4b 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -112,7 +112,7 @@ jobs: - name: Build | Build next version artifacts id: version - uses: python-semantic-release/python-semantic-release@1a324000f2251a9e722e77b128bf72712653813f # v10.0.2 + uses: python-semantic-release/python-semantic-release@f9e152fb36cd2e590fe8c2bf85bbff08f7fc1c52 # v10.1.0 with: github_token: "" verbosity: 1 From 64b713872ccef3456bd7eb9f2ce247171adad045 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Jun 2025 05:54:26 +0000 Subject: [PATCH 34/37] ci(deps): bump `python-semantic-release/publish-action@v10.0.2` to `v10.1.0` --- .github/workflows/cicd.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml index e3d962ff5..84d656b96 100644 --- a/.github/workflows/cicd.yml +++ b/.github/workflows/cicd.yml @@ -152,7 +152,7 @@ jobs: build: false - name: Release | Add distribution artifacts to GitHub Release Assets - uses: python-semantic-release/publish-action@e5e3010f6a207cd5d6f5d3dccedbea355484ca02 # v10.0.2 + uses: python-semantic-release/publish-action@ca88900e4d435c6645d47e5f1e7f108e94c77f05 # v10.1.0 if: steps.release.outputs.released == 'true' with: github_token: ${{ secrets.GITHUB_TOKEN }} From f2ffdc0e335c72a679185bee1b9aae5bbdb905c0 Mon Sep 17 00:00:00 2001 From: Dzmitry Sliaptsou Date: Fri, 20 Jun 2025 07:59:44 +0200 Subject: [PATCH 35/37] test(cmd-version): add post-release branch update merge repo test (#1268) This commit adds a repo fixture and the rebuild repo test case to handle the situation where a branch was started before a release but then a release occured and the developer used a merge from the default branch (rather than rebase). ref: #1252 --- tests/const.py | 1 + ...test_repo_1_channel_branch_update_merge.py | 174 +++++++ tests/fixtures/git_repo.py | 13 +- tests/fixtures/repos/github_flow/__init__.py | 1 + ...w_default_release_w_branch_update_merge.py | 471 ++++++++++++++++++ 5 files changed, 657 insertions(+), 3 deletions(-) create mode 100644 tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel_branch_update_merge.py create mode 100644 tests/fixtures/repos/github_flow/repo_w_default_release_w_branch_update_merge.py diff --git a/tests/const.py b/tests/const.py index 41df4533d..c7cc0a8b4 100644 --- a/tests/const.py +++ b/tests/const.py @@ -328,3 +328,4 @@ def _read_long_description(): """.lstrip() # noqa: E501 RELEASE_NOTES = "# Release Notes" +DEFAULT_MERGE_STRATEGY_OPTION = "theirs" diff --git a/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel_branch_update_merge.py b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel_branch_update_merge.py new file mode 100644 index 000000000..1c494cfb4 --- /dev/null +++ b/tests/e2e/cmd_version/bump_version/github_flow/test_repo_1_channel_branch_update_merge.py @@ -0,0 +1,174 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING + +import pytest +import tomlkit +from flatdict import FlatDict +from freezegun import freeze_time + +from tests.const import ( + DEFAULT_BRANCH_NAME, +) +from tests.fixtures.repos.github_flow import ( + repo_w_github_flow_w_default_release_n_branch_update_merge_conventional_commits, + repo_w_github_flow_w_default_release_n_branch_update_merge_emoji_commits, + repo_w_github_flow_w_default_release_n_branch_update_merge_scipy_commits, +) +from tests.util import temporary_working_directory + +if TYPE_CHECKING: + from pathlib import Path + from unittest.mock import MagicMock + + from requests_mock import Mocker + + from tests.e2e.cmd_version.bump_version.conftest import ( + InitMirrorRepo4RebuildFn, + RunPSReleaseFn, + ) + from tests.e2e.conftest import GetSanitizedChangelogContentFn + from tests.fixtures.example_project import ExProjectDir + from tests.fixtures.git_repo import ( + BuildRepoFromDefinitionFn, + BuildSpecificRepoFn, + CommitConvention, + GetGitRepo4DirFn, + RepoActionConfigure, + RepoActionRelease, + RepoActions, + SplitRepoActionsByReleaseTagsFn, + ) + + +@pytest.mark.xfail( + reason="Should pass after [#1252](https://github.com/python-semantic-release/python-semantic-release/issues/1252) is fixed", +) +@pytest.mark.parametrize( + "repo_fixture_name", + [ + pytest.param(repo_fixture_name, marks=pytest.mark.comprehensive) + for repo_fixture_name in [ + repo_w_github_flow_w_default_release_n_branch_update_merge_conventional_commits.__name__, + repo_w_github_flow_w_default_release_n_branch_update_merge_emoji_commits.__name__, + repo_w_github_flow_w_default_release_n_branch_update_merge_scipy_commits.__name__, + ] + ], +) +def test_github_flow_repo_w_default_release_n_branch_update_merge( + repo_fixture_name: str, + run_psr_release: RunPSReleaseFn, + build_github_flow_repo_w_default_release_n_branch_update_merge: BuildSpecificRepoFn, + split_repo_actions_by_release_tags: SplitRepoActionsByReleaseTagsFn, + init_mirror_repo_for_rebuild: InitMirrorRepo4RebuildFn, + example_project_dir: ExProjectDir, + git_repo_for_directory: GetGitRepo4DirFn, + build_repo_from_definition: BuildRepoFromDefinitionFn, + mocked_git_push: MagicMock, + post_mocker: Mocker, + default_tag_format_str: str, + version_py_file: Path, + get_sanitized_md_changelog_content: GetSanitizedChangelogContentFn, + get_sanitized_rst_changelog_content: GetSanitizedChangelogContentFn, +): + # build target repo into a temporary directory + target_repo_dir = example_project_dir / repo_fixture_name + commit_type: CommitConvention = ( + repo_fixture_name.split("commits", 1)[0].split("_")[-2] # type: ignore[assignment] + ) + target_repo_definition = ( + build_github_flow_repo_w_default_release_n_branch_update_merge( + repo_name=repo_fixture_name, + commit_type=commit_type, + dest_dir=target_repo_dir, + ) + ) + target_git_repo = git_repo_for_directory(target_repo_dir) + target_repo_pyproject_toml = FlatDict( + tomlkit.loads((target_repo_dir / "pyproject.toml").read_text(encoding="utf-8")), + delimiter=".", + ) + tag_format_str: str = target_repo_pyproject_toml.get( # type: ignore[assignment] + "tool.semantic_release.tag_format", + default_tag_format_str, + ) + + # split repo actions by release actions + releasetags_2_steps: dict[str, list[RepoActions]] = ( + split_repo_actions_by_release_tags(target_repo_definition, tag_format_str) + ) + configuration_step: RepoActionConfigure = releasetags_2_steps.pop("")[0] # type: ignore[assignment] + + # Create the mirror repo directory + mirror_repo_dir = init_mirror_repo_for_rebuild( + mirror_repo_dir=(example_project_dir / "mirror"), + configuration_step=configuration_step, + ) + mirror_git_repo = git_repo_for_directory(mirror_repo_dir) + + # rebuild repo from scratch stopping before each release tag + for curr_release_tag, steps in releasetags_2_steps.items(): + # make sure mocks are clear + mocked_git_push.reset_mock() + post_mocker.reset_mock() + + # Extract expected result from target repo + head_reference_name = ( + curr_release_tag + if curr_release_tag != "Unreleased" + else DEFAULT_BRANCH_NAME + ) + target_git_repo.git.checkout(head_reference_name, detach=True) + expected_md_changelog_content = get_sanitized_md_changelog_content( + repo_dir=target_repo_dir + ) + expected_rst_changelog_content = get_sanitized_rst_changelog_content( + repo_dir=target_repo_dir + ) + expected_pyproject_toml_content = ( + target_repo_dir / "pyproject.toml" + ).read_text() + expected_version_file_content = (target_repo_dir / version_py_file).read_text() + expected_release_commit_text = target_git_repo.head.commit.message + + # In our repo env, start building the repo from the definition + build_repo_from_definition( + dest_dir=mirror_repo_dir, + repo_construction_steps=steps[:-1], # stop before the release step + ) + release_action_step: RepoActionRelease = steps[-1] # type: ignore[assignment] + + # Act: run PSR on the repo instead of the RELEASE step + with freeze_time( + release_action_step["details"]["datetime"] + ), temporary_working_directory(mirror_repo_dir): + run_psr_release( + next_version_str=release_action_step["details"]["version"], + git_repo=mirror_git_repo, + ) + + # take measurement after running the version command + actual_release_commit_text = mirror_git_repo.head.commit.message + actual_pyproject_toml_content = (mirror_repo_dir / "pyproject.toml").read_text() + actual_version_file_content = (mirror_repo_dir / version_py_file).read_text() + actual_md_changelog_content = get_sanitized_md_changelog_content( + repo_dir=mirror_repo_dir + ) + actual_rst_changelog_content = get_sanitized_rst_changelog_content( + repo_dir=mirror_repo_dir + ) + + # Evaluate (normal release actions should have occurred as expected) + # ------------------------------------------------------------------ + # Make sure version file is updated + assert expected_pyproject_toml_content == actual_pyproject_toml_content + assert expected_version_file_content == actual_version_file_content + # Make sure changelog is updated + assert expected_md_changelog_content == actual_md_changelog_content + assert expected_rst_changelog_content == actual_rst_changelog_content + # Make sure commit is created + assert expected_release_commit_text == actual_release_commit_text + # Make sure tag is created + assert curr_release_tag in [tag.name for tag in mirror_git_repo.tags] + assert mocked_git_push.call_count == 2 # 1 for commit, 1 for tag + assert post_mocker.call_count == 1 # vcs release creation occured diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 106bc55e4..7ad6ac0be 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -33,6 +33,7 @@ from tests.const import ( COMMIT_MESSAGE, DEFAULT_BRANCH_NAME, + DEFAULT_MERGE_STRATEGY_OPTION, EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, @@ -233,6 +234,7 @@ def __call__( branch_name: str, commit_def: CommitDef, fast_forward: bool = True, + strategy_option: str = DEFAULT_MERGE_STRATEGY_OPTION, ) -> CommitDef: ... class CreateSquashMergeCommitFn(Protocol): @@ -241,7 +243,7 @@ def __call__( git_repo: Repo, branch_name: str, commit_def: CommitDef, - strategy_option: str = "theirs", + strategy_option: str = DEFAULT_MERGE_STRATEGY_OPTION, ) -> CommitDef: ... class CommitSpec(TypedDict): @@ -311,7 +313,7 @@ class RepoActionGitMergeDetails(DetailsBase): branch_name: str commit_def: CommitDef fast_forward: Literal[False] - # strategy_option: str + strategy_option: NotRequired[str] class RepoActionGitFFMergeDetails(DetailsBase): branch_name: str @@ -763,6 +765,7 @@ def _create_merge_commit( branch_name: str, commit_def: CommitDef, fast_forward: bool = True, + strategy_option: str = DEFAULT_MERGE_STRATEGY_OPTION, ) -> CommitDef: curr_dt = stable_now_date() commit_dt = ( @@ -784,6 +787,7 @@ def _create_merge_commit( ff=fast_forward, no_ff=bool(not fast_forward), m=commit_def["msg"], + strategy_option=strategy_option, ) # return the commit definition with the sha & message updated @@ -804,7 +808,7 @@ def _create_squash_merge_commit( git_repo: Repo, branch_name: str, commit_def: CommitDef, - strategy_option: str = "theirs", + strategy_option: str = DEFAULT_MERGE_STRATEGY_OPTION, ) -> CommitDef: curr_dt = stable_now_date() commit_dt = ( @@ -1404,6 +1408,9 @@ def _build_repo_from_definition( # noqa: C901, its required and its just test c branch_name=merge_def["branch_name"], commit_def=merge_def["commit_def"], fast_forward=merge_def["fast_forward"], + strategy_option=merge_def.get( + "strategy_option", DEFAULT_MERGE_STRATEGY_OPTION + ), ) if merge_def["commit_def"]["include_in_changelog"]: current_commits.append(merge_def["commit_def"]) diff --git a/tests/fixtures/repos/github_flow/__init__.py b/tests/fixtures/repos/github_flow/__init__.py index 47e11144d..ccf2901a6 100644 --- a/tests/fixtures/repos/github_flow/__init__.py +++ b/tests/fixtures/repos/github_flow/__init__.py @@ -1,2 +1,3 @@ from tests.fixtures.repos.github_flow.repo_w_default_release import * +from tests.fixtures.repos.github_flow.repo_w_default_release_w_branch_update_merge import * from tests.fixtures.repos.github_flow.repo_w_release_channels import * diff --git a/tests/fixtures/repos/github_flow/repo_w_default_release_w_branch_update_merge.py b/tests/fixtures/repos/github_flow/repo_w_default_release_w_branch_update_merge.py new file mode 100644 index 000000000..1d6e8bf95 --- /dev/null +++ b/tests/fixtures/repos/github_flow/repo_w_default_release_w_branch_update_merge.py @@ -0,0 +1,471 @@ +from __future__ import annotations + +from datetime import timedelta +from itertools import count +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest + +from semantic_release.cli.config import ChangelogOutputFormat + +import tests.conftest +import tests.const +import tests.util +from tests.const import ( + DEFAULT_BRANCH_NAME, + EXAMPLE_HVCS_DOMAIN, + INITIAL_COMMIT_MESSAGE, + RepoActionStep, +) + +if TYPE_CHECKING: + from typing import Sequence + + from tests.conftest import ( + GetCachedRepoDataFn, + GetMd5ForSetOfFilesFn, + GetStableDateNowFn, + ) + from tests.fixtures.example_project import ( + ExProjectDir, + ) + from tests.fixtures.git_repo import ( + BuildRepoFromDefinitionFn, + BuildRepoOrCopyCacheFn, + BuildSpecificRepoFn, + BuiltRepoResult, + CommitConvention, + ConvertCommitSpecsToCommitDefsFn, + ConvertCommitSpecToCommitDefFn, + ExProjectGitRepoFn, + FormatGitMergeCommitMsgFn, + GetRepoDefinitionFn, + RepoActions, + RepoActionWriteChangelogsDestFile, + TomlSerializableTypes, + ) + + +FEAT_BRANCH_NAME = "feat/feature" + + +@pytest.fixture(scope="session") +def deps_files_4_repo_w_default_release_n_branch_update_merge( + deps_files_4_example_git_project: list[Path], +) -> list[Path]: + return [ + *deps_files_4_example_git_project, + # This file + Path(__file__).absolute(), + # because of imports + Path(tests.const.__file__).absolute(), + Path(tests.util.__file__).absolute(), + # because of the fixtures + Path(tests.conftest.__file__).absolute(), + ] + + +@pytest.fixture(scope="session") +def build_spec_hash_4_repo_w_default_release_n_branch_update_merge( + get_md5_for_set_of_files: GetMd5ForSetOfFilesFn, + deps_files_4_repo_w_default_release_n_branch_update_merge: list[Path], +) -> str: + # Generates a hash of the build spec to set when to invalidate the cache + return get_md5_for_set_of_files( + deps_files_4_repo_w_default_release_n_branch_update_merge + ) + + +@pytest.fixture(scope="session") +def get_repo_definition_4_github_flow_repo_w_default_release_n_branch_update_merge( + convert_commit_specs_to_commit_defs: ConvertCommitSpecsToCommitDefsFn, + convert_commit_spec_to_commit_def: ConvertCommitSpecToCommitDefFn, + format_merge_commit_msg_git: FormatGitMergeCommitMsgFn, + changelog_md_file: Path, + changelog_rst_file: Path, + stable_now_date: GetStableDateNowFn, +) -> GetRepoDefinitionFn: + """ + This fixture provides a function that builds a repository definition for a trunk-based development + where a release in the default branch is made in parallel to a work in a feature branch, + feature branch is updated with the latest changes from the default branch and them merged back + into the default branch with a release. + + It is the minimal reproducible example of the issue + [#1252](https://github.com/python-semantic-release/python-semantic-release/issues/1252). + """ + + def _get_repo_from_definition( + commit_type: CommitConvention, + hvcs_client_name: str = "github", + hvcs_domain: str = EXAMPLE_HVCS_DOMAIN, + tag_format_str: str | None = None, + extra_configs: dict[str, TomlSerializableTypes] | None = None, + mask_initial_release: bool = True, + ignore_merge_commits: bool = True, + ) -> Sequence[RepoActions]: + stable_now_datetime = stable_now_date() + commit_timestamp_gen = ( + (stable_now_datetime + timedelta(seconds=i)).isoformat(timespec="seconds") + for i in count(step=1) + ) + + changelog_file_definitions: Sequence[RepoActionWriteChangelogsDestFile] = [ + { + "path": changelog_md_file, + "format": ChangelogOutputFormat.MARKDOWN, + }, + { + "path": changelog_rst_file, + "format": ChangelogOutputFormat.RESTRUCTURED_TEXT, + }, + ] + + repo_construction_steps: list[RepoActions] = [] + + repo_construction_steps.append( + { + "action": RepoActionStep.CONFIGURE, + "details": { + "commit_type": commit_type, + "hvcs_client_name": hvcs_client_name, + "hvcs_domain": hvcs_domain, + "tag_format_str": tag_format_str, + "mask_initial_release": mask_initial_release, + "extra_configs": { + # Set the default release branch + "tool.semantic_release.branches.main": { + "match": r"^(main|master)$", + "prerelease": False, + }, + "tool.semantic_release.allow_zero_version": True, + **(extra_configs or {}), + }, + }, + } + ) + + # Make initial release + new_version = "0.1.0" + + repo_construction_steps.extend( + [ + { + "action": RepoActionStep.MAKE_COMMITS, + "details": { + "commits": convert_commit_specs_to_commit_defs( + [ + { + "conventional": INITIAL_COMMIT_MESSAGE, + "emoji": INITIAL_COMMIT_MESSAGE, + "scipy": INITIAL_COMMIT_MESSAGE, + "datetime": next(commit_timestamp_gen), + "include_in_changelog": bool( + commit_type == "emoji" + ), + }, + { + "conventional": "feat: add new feature", + "emoji": ":sparkles: add new feature", + "scipy": "ENH: add new feature", + "datetime": next(commit_timestamp_gen), + "include_in_changelog": True, + }, + ], + commit_type, + ), + }, + }, + { + "action": RepoActionStep.RELEASE, + "details": { + "version": new_version, + "datetime": next(commit_timestamp_gen), + "pre_actions": [ + { + "action": RepoActionStep.WRITE_CHANGELOGS, + "details": { + "new_version": new_version, + "dest_files": changelog_file_definitions, + }, + }, + ], + }, + }, + ] + ) + + # Create a feature branch (without commits yet, just to pin a commit) + repo_construction_steps.extend( + [ + { + "action": RepoActionStep.GIT_CHECKOUT, + "details": { + "create_branch": { + "name": FEAT_BRANCH_NAME, + "start_branch": DEFAULT_BRANCH_NAME, + } + }, + }, + { + "action": RepoActionStep.GIT_CHECKOUT, + "details": {"branch": DEFAULT_BRANCH_NAME}, + }, + ] + ) + + # Make another release in default branch + new_version = "0.2.0" + + repo_construction_steps.extend( + [ + { + "action": RepoActionStep.MAKE_COMMITS, + "details": { + "commits": convert_commit_specs_to_commit_defs( + [ + { + "conventional": "feat: add another feature", + "emoji": ":sparkles: add another feature", + "scipy": "ENH: add another feature", + "datetime": next(commit_timestamp_gen), + "include_in_changelog": True, + }, + ], + commit_type, + ), + }, + }, + { + "action": RepoActionStep.RELEASE, + "details": { + "version": new_version, + "datetime": next(commit_timestamp_gen), + "pre_actions": [ + { + "action": RepoActionStep.WRITE_CHANGELOGS, + "details": { + "new_version": new_version, + "dest_files": changelog_file_definitions, + }, + }, + ], + }, + }, + ] + ) + + # Add commit to the feature branch + repo_construction_steps.extend( + [ + { + "action": RepoActionStep.GIT_CHECKOUT, + "details": {"branch": FEAT_BRANCH_NAME}, + }, + { + "action": RepoActionStep.MAKE_COMMITS, + "details": { + "commits": convert_commit_specs_to_commit_defs( + [ + { + "conventional": "feat: add new feature in the feature branch", + "emoji": ":sparkles: add new feature in the feature branch", + "scipy": "ENH: add new feature in the feature branch", + "datetime": next(commit_timestamp_gen), + "include_in_changelog": True, + }, + ], + commit_type, + ), + }, + }, + ] + ) + + # Merge default branch into the feature branch to keep it up to date + repo_construction_steps.extend( + [ + { + "action": RepoActionStep.GIT_MERGE, + "details": { + "branch_name": DEFAULT_BRANCH_NAME, + "fast_forward": False, + "commit_def": convert_commit_spec_to_commit_def( + { + "conventional": format_merge_commit_msg_git( + branch_name=DEFAULT_BRANCH_NAME, + tgt_branch_name=FEAT_BRANCH_NAME, + ), + "emoji": format_merge_commit_msg_git( + branch_name=DEFAULT_BRANCH_NAME, + tgt_branch_name=FEAT_BRANCH_NAME, + ), + "scipy": format_merge_commit_msg_git( + branch_name=DEFAULT_BRANCH_NAME, + tgt_branch_name=FEAT_BRANCH_NAME, + ), + "datetime": next(commit_timestamp_gen), + "include_in_changelog": not ignore_merge_commits, + }, + commit_type, + ), + }, + }, + { + "action": RepoActionStep.GIT_CHECKOUT, + "details": {"branch": DEFAULT_BRANCH_NAME}, + }, + ] + ) + + # Merge the feature branch into the default branch and make a release + new_version = "0.3.0" + + repo_construction_steps.extend( + [ + { + "action": RepoActionStep.GIT_MERGE, + "details": { + "branch_name": FEAT_BRANCH_NAME, + "fast_forward": False, + "commit_def": convert_commit_spec_to_commit_def( + { + "conventional": format_merge_commit_msg_git( + branch_name=FEAT_BRANCH_NAME, + tgt_branch_name=DEFAULT_BRANCH_NAME, + ), + "emoji": format_merge_commit_msg_git( + branch_name=FEAT_BRANCH_NAME, + tgt_branch_name=DEFAULT_BRANCH_NAME, + ), + "scipy": format_merge_commit_msg_git( + branch_name=FEAT_BRANCH_NAME, + tgt_branch_name=DEFAULT_BRANCH_NAME, + ), + "datetime": next(commit_timestamp_gen), + "include_in_changelog": not ignore_merge_commits, + }, + commit_type, + ), + }, + }, + { + "action": RepoActionStep.RELEASE, + "details": { + "version": new_version, + "datetime": next(commit_timestamp_gen), + "pre_actions": [ + { + "action": RepoActionStep.WRITE_CHANGELOGS, + "details": { + "new_version": new_version, + "dest_files": changelog_file_definitions, + }, + }, + ], + }, + }, + ] + ) + + return repo_construction_steps + + return _get_repo_from_definition + + +@pytest.fixture(scope="session") +def build_github_flow_repo_w_default_release_n_branch_update_merge( + build_repo_from_definition: BuildRepoFromDefinitionFn, + get_repo_definition_4_github_flow_repo_w_default_release_n_branch_update_merge: GetRepoDefinitionFn, + get_cached_repo_data: GetCachedRepoDataFn, + build_repo_or_copy_cache: BuildRepoOrCopyCacheFn, + build_spec_hash_4_repo_w_default_release_n_branch_update_merge: str, +) -> BuildSpecificRepoFn: + def _build_specific_repo_type( + repo_name: str, commit_type: CommitConvention, dest_dir: Path + ) -> Sequence[RepoActions]: + def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]: + repo_construction_steps = get_repo_definition_4_github_flow_repo_w_default_release_n_branch_update_merge( + commit_type=commit_type, + ) + return build_repo_from_definition(cached_repo_path, repo_construction_steps) + + build_repo_or_copy_cache( + repo_name=repo_name, + build_spec_hash=build_spec_hash_4_repo_w_default_release_n_branch_update_merge, + build_repo_func=_build_repo, + dest_dir=dest_dir, + ) + + if not (cached_repo_data := get_cached_repo_data(proj_dirname=repo_name)): + raise ValueError("Failed to retrieve repo data from cache") + + return cached_repo_data["build_definition"] + + return _build_specific_repo_type + + +# --------------------------------------------------------------------------- # +# Test-level fixtures that will cache the built directory & set up test case # +# --------------------------------------------------------------------------- # + + +@pytest.fixture +def repo_w_github_flow_w_default_release_n_branch_update_merge_conventional_commits( + build_github_flow_repo_w_default_release_n_branch_update_merge: BuildSpecificRepoFn, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +) -> BuiltRepoResult: + repo_name = repo_w_github_flow_w_default_release_n_branch_update_merge_conventional_commits.__name__ + commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] + + return { + "definition": build_github_flow_repo_w_default_release_n_branch_update_merge( + repo_name=repo_name, + commit_type=commit_type, + dest_dir=example_project_dir, + ), + "repo": example_project_git_repo(), + } + + +@pytest.fixture +def repo_w_github_flow_w_default_release_n_branch_update_merge_emoji_commits( + build_github_flow_repo_w_default_release_n_branch_update_merge: BuildSpecificRepoFn, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +) -> BuiltRepoResult: + repo_name = repo_w_github_flow_w_default_release_n_branch_update_merge_emoji_commits.__name__ + commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] + + return { + "definition": build_github_flow_repo_w_default_release_n_branch_update_merge( + repo_name=repo_name, + commit_type=commit_type, + dest_dir=example_project_dir, + ), + "repo": example_project_git_repo(), + } + + +@pytest.fixture +def repo_w_github_flow_w_default_release_n_branch_update_merge_scipy_commits( + build_github_flow_repo_w_default_release_n_branch_update_merge: BuildSpecificRepoFn, + example_project_git_repo: ExProjectGitRepoFn, + example_project_dir: ExProjectDir, + change_to_ex_proj_dir: None, +) -> BuiltRepoResult: + repo_name = repo_w_github_flow_w_default_release_n_branch_update_merge_scipy_commits.__name__ + commit_type: CommitConvention = repo_name.split("_")[-2] # type: ignore[assignment] + + return { + "definition": build_github_flow_repo_w_default_release_n_branch_update_merge( + repo_name=repo_name, + commit_type=commit_type, + dest_dir=example_project_dir, + ), + "repo": example_project_git_repo(), + } From 0f8b1b3c7b560ac40ca482ab80f759d3a6ad0bb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 23:05:07 -0700 Subject: [PATCH 36/37] build(deps-dev): bump `mypy` from 1.16.0 to 1.16.1 (#1279) * chore(config): bump `mypy` plugin version to 1.16.1 in pre-commit --- .pre-commit-config.yaml | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f68bac6c..5ae875b90 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -50,7 +50,7 @@ repos: name: ruff (format) - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.16.0" + rev: "v1.16.1" hooks: - id: mypy additional_dependencies: diff --git a/pyproject.toml b/pyproject.toml index 541520dc3..34d3379ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -84,7 +84,7 @@ dev = [ "ruff == 0.6.1" ] mypy = [ - "mypy == 1.16.0", + "mypy == 1.16.1", "types-Deprecated ~= 1.2", "types-requests ~= 2.32.0", "types-pyyaml ~= 6.0", From c1fc8afd57594a02f7bf57c3979b768421380653 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 23:08:43 -0700 Subject: [PATCH 37/37] ci(deps): bump `mikepenz/action-junit-report@v5.6.0` action to `v5.6.1` (#1278) --- .github/workflows/validate.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 0cb01af4b..5248ddc99 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -195,7 +195,7 @@ jobs: --junit-xml=tests/reports/pytest-results.xml - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@65fe03598d8d251738592a497a9e8547a5c48eaa # v5.6.0 + uses: mikepenz/action-junit-report@a83fd2b5d58d4fc702e690c1ea688d702d28d281 # v5.6.1 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -285,7 +285,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@65fe03598d8d251738592a497a9e8547a5c48eaa # v5.6.0 + uses: mikepenz/action-junit-report@a83fd2b5d58d4fc702e690c1ea688d702d28d281 # v5.6.1 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml @@ -383,7 +383,7 @@ jobs: retention-days: 1 - name: Report | Upload Test Results - uses: mikepenz/action-junit-report@65fe03598d8d251738592a497a9e8547a5c48eaa # v5.6.0 + uses: mikepenz/action-junit-report@a83fd2b5d58d4fc702e690c1ea688d702d28d281 # v5.6.1 if: ${{ always() && steps.tests.outcome != 'skipped' }} with: report_paths: ./tests/reports/*.xml