From df8d37dc7d91001259ee6d9cda02ec045ad3753e Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 9 Nov 2025 13:20:27 -0700 Subject: [PATCH 1/3] test(algorithm): update unit tests for `tag_format` to match likely usage --- .../version/test_algorithm.py | 93 +++++++++---------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/tests/unit/semantic_release/version/test_algorithm.py b/tests/unit/semantic_release/version/test_algorithm.py index a7fded2ef..f470e78ce 100644 --- a/tests/unit/semantic_release/version/test_algorithm.py +++ b/tests/unit/semantic_release/version/test_algorithm.py @@ -155,56 +155,49 @@ def test_sorted_repo_tags_and_versions(tags: list[str], sorted_tags: list[str]): @pytest.mark.parametrize( "tag_format, invalid_tags, valid_tags", [ - ( - "v{version}", - ("test-v1.1.0", "v1.1.0-test-test"), - [ - "v1.0.0-rc.1", - "v1.0.0-beta.2", - "v1.0.0-beta.11", - "v1.0.0-alpha.1", - "v1.0.0-alpha.beta.1", - "v1.0.0", - ], - ), - ( - "v{version}", - ("0.3", "0.4"), - [ - "v1.0.0-rc.1", - "v1.0.0-beta.2", - "v1.0.0-beta.11", - "v1.0.0-alpha.1", - "v1.0.0-alpha.beta.1", - "v1.0.0", - ], - ), - ( - r"(\w+--)?v{version}", - ("v1.1.0-test-test", "test_v1.1.0"), - [ - "v1.0.0-rc.1", - "test--v1.1.0", - "v1.0.0-beta.2", - "v1.0.0-beta.11", - "v1.0.0-alpha.1", - "v1.0.0-alpha.beta.1", - "v1.0.0", - ], - ), - ( - r"(?Pfeature|fix)/v{version}--(?Pdev|stg|prod)", - ("v1.1.0--test", "test_v1.1.0", "docs/v1.2.0--dev"), - [ - "feature/v1.0.0-rc.1--dev", - "fix/v1.1.0--stg", - "feature/v1.0.0-beta.2--stg", - "fix/v1.0.0-beta.11--dev", - "fix/v1.0.0-alpha.1--dev", - "feature/v1.0.0-alpha.beta.1--dev", - "feature/v1.0.0--prod", - ], - ), + pytest.param( + tag_format, + invalid_tags, + valid_tags, + id=test_id, + ) + for test_id, tag_format, invalid_tags, valid_tags in [ + ( + "traditional-v-prefixed-versions", + "v{version}", + ( + "0.3", # no v-prefix + "test-v1.1.0", # extra prefix + "v1.1.0-test-test", # bad suffix + ), + [ + "v1.0.0-rc.1", + "v1.0.0-beta.2", + "v1.0.0-beta.11", + "v1.0.0-alpha.1", + "v1.0.0-alpha.beta.1", + "v1.0.0", + ], + ), + ( + "monorepo-style-versions", + "pkg1-v{version}", + ( + "0.3", # no pkg or version prefix + "v1.1.0", # no pkg prefix + "pkg1-v1.1.0-test-test", # bad suffix + "pkg2-v1.1.0", # wrong package prefix + ), + [ + "pkg1-v1.0.0-rc.1", + "pkg1-v1.0.0-beta.2", + "pkg1-v1.0.0-beta.11", + "pkg1-v1.0.0-alpha.1", + "pkg1-v1.0.0-alpha.beta.1", + "pkg1-v1.0.0", + ], + ), + ] ], ) def test_tags_and_versions_ignores_invalid_tags_as_versions( From 8b07089c9e962fb804ccc7e30e726113067643b4 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 13 Sep 2025 23:53:05 -0600 Subject: [PATCH 2/3] fix(cmd-version): prevent regular expression errors on `tag_format` --- src/semantic_release/version/translator.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/semantic_release/version/translator.py b/src/semantic_release/version/translator.py index 6340701da..16885af10 100644 --- a/src/semantic_release/version/translator.py +++ b/src/semantic_release/version/translator.py @@ -1,12 +1,16 @@ from __future__ import annotations -import re +from re import VERBOSE, compile as regexp, escape as regex_escape +from typing import TYPE_CHECKING from semantic_release.const import SEMVER_REGEX from semantic_release.globals import logger from semantic_release.helpers import check_tag_format from semantic_release.version.version import Version +if TYPE_CHECKING: + from re import Pattern + class VersionTranslator: """ @@ -17,7 +21,7 @@ class VersionTranslator: _VERSION_REGEX = SEMVER_REGEX @classmethod - def _invert_tag_format_to_re(cls, tag_format: str) -> re.Pattern[str]: + def _invert_tag_format_to_re(cls, tag_format: str) -> Pattern[str]: r""" Unpick the "tag_format" format string and create a regex which can be used to convert a tag to a version string. @@ -31,9 +35,11 @@ def _invert_tag_format_to_re(cls, tag_format: str) -> re.Pattern[str]: >>> assert m is not None >>> assert m.expand(r"\g") == version """ - pat = re.compile( - tag_format.replace(r"{version}", r"(?P.*)"), - flags=re.VERBOSE, + pat = regexp( + regex_escape(tag_format).replace( + regex_escape(r"{version}"), r"(?P.+)" + ), + flags=VERBOSE, ) logger.debug("inverted tag_format %r to %r", tag_format, pat.pattern) return pat From 39c66dc26d51eb49c14ce13d3b9e0aff6a1ef50d Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 9 Nov 2025 13:19:47 -0700 Subject: [PATCH 3/3] docs(configuration): fix `tag_format` definition --- docs/configuration/configuration.rst | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/docs/configuration/configuration.rst b/docs/configuration/configuration.rst index cdd3604fb..685bfa8e4 100644 --- a/docs/configuration/configuration.rst +++ b/docs/configuration/configuration.rst @@ -1170,17 +1170,8 @@ from the :ref:`remote.name ` location of your git repository **Type:** ``str`` Specify the format to be used for the Git tag that will be added to the repo during -a release invoked via :ref:`cmd-version`. The format string is a regular expression, -which also must include the format keys below, otherwise an exception will be thrown. -It *may* include any of the optional format keys, in which case the contents -described will be formatted into the specified location in the Git tag that is created. - -For example, ``"(dev|stg|prod)-v{version}"`` is a valid ``tag_format`` matching tags such -as: - -- ``dev-v1.2.3`` -- ``stg-v0.1.0-rc.1`` -- ``prod-v2.0.0+20230701`` +a release invoked via :ref:`cmd-version`. The string is used as a template for the tag +name, and must include the ``{version}`` format key. This format will also be used for parsing tags already present in the repository into semantic versions; therefore if the tag format changes at some point in the @@ -1196,6 +1187,13 @@ Format Key Mandatory Contents Tags which do not match this format will not be considered as versions of your project. +This is critical for Monorepo projects where the tag format defines which package the +version tag belongs to. Generally, the tag format for each package of the monorepo will +include the package name as the prefix of the tag format. For example, if the package +is named ``pkg1``, the tag format would be ``pkg1-v{version}`` and in the other package +``pkg2``, the tag format would be ``pkg2-v{version}``. This allows PSR to determine +which tags to use to determine the version for each package. + **Default:** ``"v{version}"`` ----