Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions docs/configuration/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1170,17 +1170,8 @@ from the :ref:`remote.name <config-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
Expand All @@ -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}"``

----
Expand Down
16 changes: 11 additions & 5 deletions src/semantic_release/version/translator.py
Original file line number Diff line number Diff line change
@@ -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:
"""
Expand All @@ -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.
Expand All @@ -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>") == version
"""
pat = re.compile(
tag_format.replace(r"{version}", r"(?P<version>.*)"),
flags=re.VERBOSE,
pat = regexp(
regex_escape(tag_format).replace(
regex_escape(r"{version}"), r"(?P<version>.+)"
),
flags=VERBOSE,
)
logger.debug("inverted tag_format %r to %r", tag_format, pat.pattern)
return pat
Expand Down
93 changes: 43 additions & 50 deletions tests/unit/semantic_release/version/test_algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"(?P<type>feature|fix)/v{version}--(?P<env>dev|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(
Expand Down
Loading