Skip to content

Commit

Permalink
Fixed tag version extraction.
Browse files Browse the repository at this point in the history
The output of `git describe` uses `-` as a delimiter. Parsing tags caused splits in the parsing of version numbers.

This joins all the remaining parts of the `git describe` with a `-`.
  • Loading branch information
coordt committed Jul 3, 2024
1 parent 0386073 commit 67eea3d
Show file tree
Hide file tree
Showing 4 changed files with 134 additions and 4 deletions.
10 changes: 6 additions & 4 deletions bumpversion/scm.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,12 @@ def get_version_from_tag(cls, tag: str, tag_name: str, parse_pattern: str) -> Op
"""Return the version from a tag."""
version_pattern = parse_pattern.replace("\\\\", "\\")
version_pattern, regex_flags = extract_regex_flags(version_pattern)
rep = tag_name.replace("{new_version}", f"(?P<current_version>{version_pattern})")
rep = f"{regex_flags}{rep}"
parts = tag_name.split("{new_version}", maxsplit=1)
prefix = parts[0]
suffix = parts[1]
rep = f"{regex_flags}{re.escape(prefix)}(?P<current_version>{version_pattern}){re.escape(suffix)}"
tag_regex = re.compile(rep)
return match["current_version"] if (match := tag_regex.match(tag)) else None
return match["current_version"] if (match := tag_regex.search(tag)) else None

@classmethod
def commit_to_scm(
Expand Down Expand Up @@ -286,7 +288,7 @@ def latest_tag_info(cls, tag_name: str, parse_pattern: str) -> SCMInfo:

info.commit_sha = describe_out.pop().lstrip("g")
info.distance_to_latest_tag = int(describe_out.pop())
version = cls.get_version_from_tag(describe_out[-1], tag_name, parse_pattern)
version = cls.get_version_from_tag("-".join(describe_out), tag_name, parse_pattern)
info.current_version = version or "-".join(describe_out).lstrip("v")

return info
Expand Down
57 changes: 57 additions & 0 deletions tests/test_cli/test_bump.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import traceback
from datetime import datetime
from pathlib import Path
from textwrap import dedent

import pytest
from pytest import param
Expand Down Expand Up @@ -255,3 +256,59 @@ def test_ignores_missing_files_with_option(tmp_path, fixtures_path, runner):
print(traceback.print_exception(result.exc_info[1]))

assert result.exit_code == 0


def test_dash_in_tag_matches_current_version(git_repo, runner, caplog):
import logging

caplog.set_level(logging.DEBUG, logger="bumpversion")

repo_path: Path = git_repo
with inside_dir(repo_path):
# Arrange
config_toml = dedent(
"""
[tool.bumpversion]
current_version = "4.0.3-beta+build.1047"
parse = "(?P<major>\\\\d+)\\\\.(?P<minor>\\\\d+)\\\\.(?P<patch>\\\\d+)(-(?P<release>[0-9A-Za-z]+))?(\\\\+build\\\\.(?P<build>.[0-9A-Za-z]+))?"
serialize = [
"{major}.{minor}.{patch}-{release}+build.{build}",
"{major}.{minor}.{patch}+build.{build}"
]
commit = true
message = "Bump version: {current_version} -> {new_version}"
tag = false
tag_name = "app/{new_version}"
tag_message = "Version app/{new_version}"
allow_dirty = false
[tool.bumpversion.parts.release]
values = [
"beta",
"rc",
"final"
]
optional_value = "final"
[tool.bumpversion.parts.build]
first_value = 1000
independent = true
always_increment = true
"""
)
repo_path.joinpath(".bumpversion.toml").write_text(config_toml, encoding="utf-8")
subprocess.run(["git", "add", ".bumpversion.toml"], check=True)

Check warning on line 300 in tests/test_cli/test_bump.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_cli/test_bump.py#L300

subprocess call - check for execution of untrusted input.
subprocess.run(["git", "commit", "-m", "added file"], check=True)

Check warning on line 301 in tests/test_cli/test_bump.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_cli/test_bump.py#L301

subprocess call - check for execution of untrusted input.
subprocess.run(["git", "tag", "-a", "app/4.0.3-beta+build.1047", "-m", "added tag"], check=True)

Check warning on line 302 in tests/test_cli/test_bump.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_cli/test_bump.py#L302

subprocess call - check for execution of untrusted input.

# Act
result: Result = runner.invoke(cli.cli, ["bump", "-vv", "--tag", "--commit", "patch"])

# Assert
if result.exit_code != 0:
import traceback

print(caplog.text)
print(traceback.print_exception(result.exc_info[1]))

assert result.exit_code == 0
37 changes: 37 additions & 0 deletions tests/test_config/test_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Tests for the config.__init__ module."""

from dataclasses import dataclass
from typing import Optional

from bumpversion.scm import SCMInfo
from bumpversion.config import check_current_version


@dataclass
class MockConfig:
"""Just includes the current_version and scm_info attributes."""

current_version: Optional[str]
scm_info: SCMInfo


class TestCheckCurrentVersion:
"""
Tests for the check_current_version function.
- tag may not match serialization
- tag may not match current version in config
"""

def test_uses_tag_when_missing_current_version(self):
"""When the config does not have a current_version, the last tag is used."""
# Assemble
scm_info = SCMInfo()
scm_info.current_version = "1.2.3"
config = MockConfig(current_version=None, scm_info=scm_info)

# Act
result = check_current_version(config)

# Assert
assert result == "1.2.3"

Check notice on line 37 in tests/test_config/test_init.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/test_config/test_init.py#L37

The application was found using `assert` in non-test code.
34 changes: 34 additions & 0 deletions tests/test_scm.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,40 @@
from tests.conftest import get_config_data, inside_dir


class TestpSourceCodeManager:
"""Tests related to SourceCodeManager."""

class TestGetVersionFromTag:
"""Tests for the get_version_from_tag classmethod."""

simple_pattern = r"(?P<major>\\d+)\\.(?P<minor>\\d+)\.(?P<patch>\\d+)"
complex_pattern = r"(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(-(?P<release>[0-9A-Za-z]+))?(\\+build\\.(?P<build>.[0-9A-Za-z]+))?"

@pytest.mark.parametrize(
["tag", "tag_name", "parse_pattern", "expected"],
[
param("1.2.3", "{new_version}", simple_pattern, "1.2.3", id="no-prefix, no-suffix"),
param("v/1.2.3", "v/{new_version}", simple_pattern, "1.2.3", id="prefix, no-suffix"),
param("v/1.2.3/12345", "v/{new_version}/{something}", simple_pattern, "1.2.3", id="prefix, suffix"),
param("1.2.3/2024-01-01", "{new_version}/{today}", simple_pattern, "1.2.3", id="no-prefix, suffix"),
param(
"app/4.0.3-beta+build.1047",
"app/{new_version}",
complex_pattern,
"4.0.3-beta+build.1047",
id="complex",
),
],
)
def returns_version_from_pattern(self, tag: str, tag_name: str, parse_pattern: str, expected: str) -> None:
"""It properly returns the version from the tag."""
# Act
version = scm.SourceCodeManager.get_version_from_tag(tag, tag_name, parse_pattern)

# Assert
assert version == expected


def test_format_and_raise_error(git_repo: Path) -> None:
"""The output formatting from called process error string works as expected."""
with inside_dir(git_repo):
Expand Down

0 comments on commit 67eea3d

Please sign in to comment.