Skip to content

fix(cmd-version): fix tag format on default version when force bump for initial release #1138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
16 changes: 14 additions & 2 deletions src/semantic_release/cli/commands/version.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
from semantic_release.errors import (
BuildDistributionsError,
GitCommitEmptyIndexError,
InternalError,
UnexpectedResponse,
)
from semantic_release.gitproject import GitProject
Expand All @@ -35,7 +36,6 @@
tags_and_versions,
)
from semantic_release.version.translator import VersionTranslator
from semantic_release.version.version import Version

if TYPE_CHECKING: # pragma: no cover
from pathlib import Path
Expand All @@ -45,6 +45,7 @@

from semantic_release.cli.cli_context import CliContextObj
from semantic_release.version.declaration import VersionDeclarationABC
from semantic_release.version.version import Version


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -90,7 +91,18 @@ def version_from_forced_level(

# If we have no tags, return the default version
if not ts_and_vs:
return Version.parse(DEFAULT_VERSION).bump(forced_level_bump)
# Since the translator is configured by the user, we can't guarantee that it will
# be able to parse the default version. So we first cast it to a tag using the default
# value and the users configured tag format, then parse it back to a version object
default_initial_version = translator.from_tag(
translator.str_to_tag(DEFAULT_VERSION)
)
if default_initial_version is None:
# This should never happen, but if it does, it's a bug
raise InternalError(
"Translator was unable to parse the embedded default version"
)
return default_initial_version.bump(forced_level_bump)

_, latest_version = ts_and_vs[0]
if forced_level_bump is not LevelBump.PRERELEASE_REVISION:
Expand Down
1 change: 1 addition & 0 deletions src/semantic_release/version/algorithm.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ def next_version(
translator.str_to_tag(DEFAULT_VERSION)
)
if default_initial_version is None:
# This should never happen, but if it does, it's a bug
raise InternalError(
"Translator was unable to parse the embedded default version"
)
Expand Down
159 changes: 109 additions & 50 deletions tests/e2e/cmd_version/test_version_print.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
repo_w_trunk_only_angular_commits,
repo_w_trunk_only_angular_commits_using_tag_format,
)
from tests.fixtures.repos.trunk_based_dev.repo_w_no_tags import (
repo_w_no_tags_angular_commits_using_tag_format,
)
from tests.util import (
add_text_to_file,
assert_exit_code,
Expand Down Expand Up @@ -149,52 +152,107 @@ def test_version_print_next_version(
@pytest.mark.parametrize(
"repo_result, commits, force_args, next_release_version",
[
(
lazy_fixture(repo_fixture_name),
lazy_fixture(angular_minor_commits.__name__),
cli_args,
next_release_version,
)
for repo_fixture_name in (
repo_w_trunk_only_angular_commits.__name__,
repo_w_trunk_only_angular_commits_using_tag_format.__name__,
)
for cli_args, next_release_version in (
# Dynamic version bump determination (based on commits)
([], "0.2.0"),
# Dynamic version bump determination (based on commits) with build metadata
(["--build-metadata", "build.12345"], "0.2.0+build.12345"),
# Forced version bump
(["--prerelease"], "0.1.1-rc.1"),
(["--patch"], "0.1.2"),
(["--minor"], "0.2.0"),
(["--major"], "1.0.0"),
# Forced version bump with --build-metadata
(["--patch", "--build-metadata", "build.12345"], "0.1.2+build.12345"),
# Forced version bump with --as-prerelease
(["--prerelease", "--as-prerelease"], "0.1.1-rc.1"),
(["--patch", "--as-prerelease"], "0.1.2-rc.1"),
(["--minor", "--as-prerelease"], "0.2.0-rc.1"),
(["--major", "--as-prerelease"], "1.0.0-rc.1"),
# Forced version bump with --as-prerelease and modified --prerelease-token
(
["--patch", "--as-prerelease", "--prerelease-token", "beta"],
"0.1.2-beta.1",
),
# Forced version bump with --as-prerelease and modified --prerelease-token
# and --build-metadata
(
[
"--patch",
"--as-prerelease",
"--prerelease-token",
"beta",
"--build-metadata",
"build.12345",
],
"0.1.2-beta.1+build.12345",
),
)
*[
pytest.param(
lazy_fixture(repo_fixture_name),
lazy_fixture(angular_minor_commits.__name__),
cli_args,
next_release_version,
marks=marks if marks else [],
)
for repo_fixture_name, marks in (
(repo_w_trunk_only_angular_commits.__name__, None),
(
repo_w_trunk_only_angular_commits_using_tag_format.__name__,
pytest.mark.comprehensive,
),
)
for cli_args, next_release_version in (
# Dynamic version bump determination (based on commits)
([], "0.2.0"),
# Dynamic version bump determination (based on commits) with build metadata
(["--build-metadata", "build.12345"], "0.2.0+build.12345"),
# Forced version bump
(["--prerelease"], "0.1.1-rc.1"),
(["--patch"], "0.1.2"),
(["--minor"], "0.2.0"),
(["--major"], "1.0.0"),
# Forced version bump with --build-metadata
(["--patch", "--build-metadata", "build.12345"], "0.1.2+build.12345"),
# Forced version bump with --as-prerelease
(["--prerelease", "--as-prerelease"], "0.1.1-rc.1"),
(["--patch", "--as-prerelease"], "0.1.2-rc.1"),
(["--minor", "--as-prerelease"], "0.2.0-rc.1"),
(["--major", "--as-prerelease"], "1.0.0-rc.1"),
# Forced version bump with --as-prerelease and modified --prerelease-token
(
["--patch", "--as-prerelease", "--prerelease-token", "beta"],
"0.1.2-beta.1",
),
# Forced version bump with --as-prerelease and modified --prerelease-token
# and --build-metadata
(
[
"--patch",
"--as-prerelease",
"--prerelease-token",
"beta",
"--build-metadata",
"build.12345",
],
"0.1.2-beta.1+build.12345",
),
)
],
*[
pytest.param(
lazy_fixture(repo_fixture_name),
[],
cli_args,
next_release_version,
marks=pytest.mark.comprehensive,
)
for repo_fixture_name in (
repo_w_no_tags_angular_commits.__name__,
repo_w_no_tags_angular_commits_using_tag_format.__name__,
)
for cli_args, next_release_version in (
# Dynamic version bump determination (based on commits)
([], "0.1.0"),
# Dynamic version bump determination (based on commits) with build metadata
(["--build-metadata", "build.12345"], "0.1.0+build.12345"),
# Forced version bump
(["--prerelease"], "0.0.0-rc.1"),
(["--patch"], "0.0.1"),
(["--minor"], "0.1.0"),
(["--major"], "1.0.0"),
# Forced version bump with --build-metadata
(["--patch", "--build-metadata", "build.12345"], "0.0.1+build.12345"),
# Forced version bump with --as-prerelease
(["--prerelease", "--as-prerelease"], "0.0.0-rc.1"),
(["--patch", "--as-prerelease"], "0.0.1-rc.1"),
(["--minor", "--as-prerelease"], "0.1.0-rc.1"),
(["--major", "--as-prerelease"], "1.0.0-rc.1"),
# Forced version bump with --as-prerelease and modified --prerelease-token
(
["--patch", "--as-prerelease", "--prerelease-token", "beta"],
"0.0.1-beta.1",
),
# Forced version bump with --as-prerelease and modified --prerelease-token
# and --build-metadata
(
[
"--patch",
"--as-prerelease",
"--prerelease-token",
"beta",
"--build-metadata",
"build.12345",
],
"0.0.1-beta.1+build.12345",
),
)
],
],
)
def test_version_print_tag_prints_next_tag(
Expand Down Expand Up @@ -227,10 +285,11 @@ def test_version_print_tag_prints_next_tag(
tag_format_str: str = get_cfg_value_from_def(repo_def, "tag_format_str") # type: ignore[assignment]
next_release_tag = tag_format_str.format(version=next_release_version)

# Make a commit to ensure we have something to release
# otherwise the "no release will be made" logic will kick in first
add_text_to_file(repo, file_in_repo)
repo.git.commit(m=commits[-1], a=True)
if len(commits) > 1:
# Make a commit to ensure we have something to release
# otherwise the "no release will be made" logic will kick in first
add_text_to_file(repo, file_in_repo)
repo.git.commit(m=commits[-1], a=True)

# Setup: take measurement before running the version command
repo_status_before = repo.git.status(short=True)
Expand Down
44 changes: 44 additions & 0 deletions tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,50 @@ def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]:
# --------------------------------------------------------------------------- #


@pytest.fixture
def repo_w_no_tags_angular_commits_using_tag_format(
build_repo_from_definition: BuildRepoFromDefinitionFn,
get_repo_definition_4_trunk_only_repo_w_no_tags: GetRepoDefinitionFn,
get_cached_repo_data: GetCachedRepoDataFn,
build_repo_or_copy_cache: BuildRepoOrCopyCacheFn,
build_spec_hash_4_repo_w_no_tags: str,
example_project_git_repo: ExProjectGitRepoFn,
example_project_dir: ExProjectDir,
change_to_ex_proj_dir: None,
) -> BuiltRepoResult:
"""
Replicates repo with no tags, but with a tag format X{version}

Follows tag format defined in python-semantic-release#1137
"""
repo_name = repo_w_no_tags_angular_commits_using_tag_format.__name__
commit_type: CommitConvention = (
repo_name.split("_commits", maxsplit=1)[0].split("_")[-1] # type: ignore[assignment]
)

def _build_repo(cached_repo_path: Path) -> Sequence[RepoActions]:
repo_construction_steps = get_repo_definition_4_trunk_only_repo_w_no_tags(
commit_type=commit_type,
tag_format_str="X{version}",
)
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_no_tags,
build_repo_func=_build_repo,
dest_dir=example_project_dir,
)

if not (cached_repo_data := get_cached_repo_data(proj_dirname=repo_name)):
raise ValueError("Failed to retrieve repo data from cache")

return {
"definition": cached_repo_data["build_definition"],
"repo": example_project_git_repo(),
}


@pytest.fixture
def repo_w_no_tags_angular_commits(
build_trunk_only_repo_w_no_tags: BuildSpecificRepoFn,
Expand Down
Loading