Skip to content

Commit

Permalink
fix: invalid version in Git history should not cause a release failure (
Browse files Browse the repository at this point in the history
  • Loading branch information
bernardcooke53 committed Jul 17, 2023
1 parent 9b6ddfe commit 254430b
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 11 deletions.
12 changes: 8 additions & 4 deletions semantic_release/cli/commands/changelog.py
Expand Up @@ -77,16 +77,20 @@ def changelog(ctx: click.Context, release_tag: str | None = None) -> None:
if release_tag and runtime.global_cli_options.noop:
noop_report(f"would have posted changelog to the release for tag {release_tag}")
elif release_tag:
v = translator.from_tag(release_tag)
version = translator.from_tag(release_tag)
if not version:
ctx.fail(
f"Tag {release_tag!r} doesn't match tag format {translator.tag_format!r}"
)

try:
release = rh.released[v]
release = rh.released[version]
except KeyError:
ctx.fail(f"tag {release_tag} not in release history")

release_notes = render_release_notes(
template_environment=env, version=v, release=release
template_environment=env, version=version, release=release
)
version = translator.from_tag(release_tag)
try:
hvcs_client.create_or_update_release(
release_tag, release_notes, prerelease=version.is_prerelease
Expand Down
21 changes: 19 additions & 2 deletions semantic_release/version/algorithm.py
Expand Up @@ -32,9 +32,26 @@ def tags_and_versions(
Return a list of 2-tuples, where each element is a tuple (tag, version)
from the tags in the Git repo and their corresponding `Version` according
to `Version.from_tag`. The returned list is sorted according to semver
ordering rules
ordering rules.
Tags which are not matched by `translator` are ignored.
"""
ts_and_vs = [(t, translator.from_tag(t.name)) for t in tags]
ts_and_vs: list[tuple[Tag, Version]] = []
for tag in tags:
try:
version = translator.from_tag(tag.name)
except NotImplementedError as e:
log.warning(
"Couldn't parse tag %s as as Version: %s",
tag.name,
str(e),
exc_info=log.isEnabledFor(logging.DEBUG),
)
continue

if version:
ts_and_vs.append((tag, version))

log.info("found %s previous tags", len(ts_and_vs))
return sorted(ts_and_vs, reverse=True, key=lambda v: v[1])

Expand Down
8 changes: 3 additions & 5 deletions semantic_release/version/translator.py
Expand Up @@ -59,18 +59,16 @@ def from_string(self, version_str: str) -> Version:
prerelease_token=self.prerelease_token,
)

def from_tag(self, tag: str) -> Version:
def from_tag(self, tag: str) -> Version | None:
"""
Return a Version instance from a Git tag, if tag_format matches the format
which would have generated the tag from a version.
which would have generated the tag from a version. Otherwise return None.
For example, a tag of 'v1.2.3' should be matched if `tag_format = 'v{version}`,
but not if `tag_format = staging--v{version}`.
"""
tag_match = self.from_tag_re.match(tag)
if not tag_match:
raise ValueError(
f"Tag {tag!r} doesn't match tag format {self.tag_format!r}"
)
return None
raw_version_str = tag_match.group("version")
return self.from_string(raw_version_str)

Expand Down
53 changes: 53 additions & 0 deletions tests/unit/semantic_release/version/test_algorithm.py
Expand Up @@ -56,6 +56,59 @@ def test_sorted_repo_tags_and_versions(tags, sorted_tags):
assert actual == sorted_tags


@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",
],
),
(
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",
],
),
],
)
def test_tags_and_versions_ignores_invalid_tags_as_versions(
tag_format, invalid_tags, valid_tags
):
repo = Repo()
translator = VersionTranslator(tag_format=tag_format)
tagrefs = [repo.tag(tag) for tag in (*valid_tags, *invalid_tags)]
actual = [t.name for t, _ in tags_and_versions(tagrefs, translator)]
assert set(actual) == set(valid_tags)


@pytest.mark.parametrize(
"latest_version, latest_full_version, latest_full_version_in_history, level_bump, "
"prerelease, prerelease_token, expected_version",
Expand Down

0 comments on commit 254430b

Please sign in to comment.