From 848052e4a1cfa16f93558709cf0bd164cb76cc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20J=C3=A4ckle?= Date: Tue, 8 Feb 2022 11:37:25 +0200 Subject: [PATCH 01/11] chore: add initial todos --- semantic_release/cli.py | 14 +++++++------- semantic_release/history/__init__.py | 2 +- semantic_release/history/logs.py | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/semantic_release/cli.py b/semantic_release/cli.py index 0950e9d2c..9b1acab5e 100644 --- a/semantic_release/cli.py +++ b/semantic_release/cli.py @@ -91,7 +91,7 @@ def common_options(func): func = option(func) return func - +# TODO: prerelease publish def print_version(*, current=False, force_level=None, **kwargs): """ Print the current or new version to standard output. @@ -115,7 +115,7 @@ def print_version(*, current=False, force_level=None, **kwargs): print("No release will be made.", file=sys.stderr) return False - +# TODO: prerelease publish def version(*, retry=False, noop=False, force_level=None, **kwargs): """ Detect the new version according to git log and semver. @@ -151,7 +151,7 @@ def version(*, retry=False, noop=False, force_level=None, **kwargs): bump_version(new_version, level_bump) return True - +# TODO: compare prerelease def should_bump_version(*, current_version, new_version, retry=False, noop=False): """Test whether the version should be bumped.""" if new_version == current_version and not retry: @@ -233,7 +233,7 @@ def changelog(*, unreleased=False, noop=False, post=False, **kwargs): else: logger.error("Missing token: cannot post changelog to HVCS") - +# TODO: prerelease publish def publish(retry: bool = False, noop: bool = False, **kwargs): """Run the version task, then push to git and upload to an artifact repository / GitHub Releases.""" current_version = get_current_version() @@ -395,7 +395,7 @@ def main(**kwargs): obj[key] = config.get(key) logger.debug(f"Main config: {obj}") - +# TODO: add --prerelease flag @main.command(name="publish", help=publish.__doc__) @common_options def cmd_publish(**kwargs): @@ -419,7 +419,7 @@ def cmd_changelog(**kwargs): logger.error(filter_output_for_secrets(str(error))) exit(1) - +# TODO: add --prerelease flag @main.command(name="version", help=version.__doc__) @common_options def cmd_version(**kwargs): @@ -429,7 +429,7 @@ def cmd_version(**kwargs): logger.error(filter_output_for_secrets(str(error))) exit(1) - +# TODO: add --prerelease flag @main.command(name="print-version", help=print_version.__doc__) @common_options @click.option( diff --git a/semantic_release/history/__init__.py b/semantic_release/history/__init__.py index b7160a0c5..aff0573b1 100644 --- a/semantic_release/history/__init__.py +++ b/semantic_release/history/__init__.py @@ -226,7 +226,7 @@ def get_current_version() -> str: return get_current_version_by_tag() return get_current_version_by_config_file() - +# TODO: prerelease version bump @LoggedFunction(logger) def get_new_version(current_version: str, level_bump: str) -> str: """ diff --git a/semantic_release/history/logs.py b/semantic_release/history/logs.py index e4d274847..e5e83e76b 100644 --- a/semantic_release/history/logs.py +++ b/semantic_release/history/logs.py @@ -17,7 +17,7 @@ 3: "major", } - +# TODO: detect version bump @LoggedFunction(logger) def evaluate_version_bump(current_version: str, force: str = None) -> Optional[str]: """ From bd81f31ae3a760f13eba8f00ea1bbd1a725a0645 Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Tue, 8 Feb 2022 11:38:47 +0200 Subject: [PATCH 02/11] feat: add prerelease tag option --- semantic_release/defaults.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/semantic_release/defaults.cfg b/semantic_release/defaults.cfg index 22c271349..4e108e2c6 100644 --- a/semantic_release/defaults.cfg +++ b/semantic_release/defaults.cfg @@ -40,3 +40,4 @@ upload_to_repository=true upload_to_pypi=true upload_to_release=true version_source=commit +prerelease_tag=beta From 89f1448f95078cba43abe6d2eff90e72555dbf6e Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Tue, 8 Feb 2022 11:41:47 +0200 Subject: [PATCH 03/11] feat: add prerelease cli flag --- semantic_release/cli.py | 26 +++++++- tests/test_cli.py | 127 ++++++++++++++++++++++++++++------------ 2 files changed, 111 insertions(+), 42 deletions(-) diff --git a/semantic_release/cli.py b/semantic_release/cli.py index 9b1acab5e..bfd4d3bfa 100644 --- a/semantic_release/cli.py +++ b/semantic_release/cli.py @@ -66,8 +66,12 @@ click.option( "--patch", "force_level", flag_value="patch", help="Force patch version." ), + click.option( + "--prerelease", is_flag=True, help="Creates a prerelease version." + ), click.option("--post", is_flag=True, help="Post changelog."), - click.option("--retry", is_flag=True, help="Retry the same release, do not bump."), + click.option("--retry", is_flag=True, + help="Retry the same release, do not bump."), click.option( "--noop", is_flag=True, @@ -92,6 +96,8 @@ def common_options(func): return func # TODO: prerelease publish + + def print_version(*, current=False, force_level=None, **kwargs): """ Print the current or new version to standard output. @@ -116,6 +122,8 @@ def print_version(*, current=False, force_level=None, **kwargs): return False # TODO: prerelease publish + + def version(*, retry=False, noop=False, force_level=None, **kwargs): """ Detect the new version according to git log and semver. @@ -152,6 +160,8 @@ def version(*, retry=False, noop=False, force_level=None, **kwargs): return True # TODO: compare prerelease + + def should_bump_version(*, current_version, new_version, retry=False, noop=False): """Test whether the version should be bumped.""" if new_version == current_version and not retry: @@ -228,12 +238,15 @@ def changelog(*, unreleased=False, noop=False, post=False, **kwargs): owner, name, current_version, - markdown_changelog(owner, name, current_version, log, header=False), + markdown_changelog( + owner, name, current_version, log, header=False), ) else: logger.error("Missing token: cannot post changelog to HVCS") # TODO: prerelease publish + + def publish(retry: bool = False, noop: bool = False, **kwargs): """Run the version task, then push to git and upload to an artifact repository / GitHub Releases.""" current_version = get_current_version() @@ -248,7 +261,8 @@ def publish(retry: bool = False, noop: bool = False, **kwargs): current_version = get_previous_version(current_version) else: # Calculate the new version - level_bump = evaluate_version_bump(current_version, kwargs.get("force_level")) + level_bump = evaluate_version_bump( + current_version, kwargs.get("force_level")) new_version = get_new_version(current_version, level_bump) owner, name = get_repository_owner_and_name() @@ -396,6 +410,8 @@ def main(**kwargs): logger.debug(f"Main config: {obj}") # TODO: add --prerelease flag + + @main.command(name="publish", help=publish.__doc__) @common_options def cmd_publish(**kwargs): @@ -420,6 +436,8 @@ def cmd_changelog(**kwargs): exit(1) # TODO: add --prerelease flag + + @main.command(name="version", help=version.__doc__) @common_options def cmd_version(**kwargs): @@ -430,6 +448,8 @@ def cmd_version(**kwargs): exit(1) # TODO: add --prerelease flag + + @main.command(name="print-version", help=print_version.__doc__) @common_options @click.option( diff --git a/tests/test_cli.py b/tests/test_cli.py index a50323141..ea1d9e79d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -22,6 +22,7 @@ def test_main_should_call_correct_function(mocker, runner): noop=False, post=False, force_level=None, + prerelease=False, retry=False, define=(), ) @@ -36,7 +37,8 @@ def test_version_by_commit_should_call_correct_functions(mocker): ), ) mock_tag_new_version = mocker.patch("semantic_release.cli.tag_new_version") - mock_commit_new_version = mocker.patch("semantic_release.cli.commit_new_version") + mock_commit_new_version = mocker.patch( + "semantic_release.cli.commit_new_version") mock_set_new_version = mocker.patch("semantic_release.cli.set_new_version") mock_new_version = mocker.patch( "semantic_release.cli.get_new_version", return_value="2.0.0" @@ -72,7 +74,8 @@ def test_version_by_tag_with_commit_version_number_should_call_correct_functions mock_set_new_version = mocker.patch("semantic_release.cli.set_new_version") mock_tag_new_version = mocker.patch("semantic_release.cli.tag_new_version") - mock_commit_new_version = mocker.patch("semantic_release.cli.commit_new_version") + mock_commit_new_version = mocker.patch( + "semantic_release.cli.commit_new_version") mock_new_version = mocker.patch( "semantic_release.cli.get_new_version", return_value="2.0.0" ) @@ -126,7 +129,8 @@ def test_version_by_commit_without_tag_should_call_correct_functions(mocker): ) mock_set_new_version = mocker.patch("semantic_release.cli.set_new_version") mock_tag_new_version = mocker.patch("semantic_release.cli.tag_new_version") - mock_commit_new_version = mocker.patch("semantic_release.cli.commit_new_version") + mock_commit_new_version = mocker.patch( + "semantic_release.cli.commit_new_version") mock_new_version = mocker.patch( "semantic_release.cli.get_new_version", return_value="2.0.0" ) @@ -154,6 +158,7 @@ def test_force_major(mocker, runner): noop=False, post=False, force_level="major", + prerelease=False, retry=False, define=(), ) @@ -168,6 +173,7 @@ def test_force_minor(mocker, runner): noop=False, post=False, force_level="minor", + prerelease=False, retry=False, define=(), ) @@ -182,6 +188,7 @@ def test_force_patch(mocker, runner): noop=False, post=False, force_level="patch", + prerelease=False, retry=False, define=(), ) @@ -196,6 +203,7 @@ def test_retry(mocker, runner): noop=False, post=False, force_level=None, + prerelease=False, retry=True, define=(), ) @@ -206,7 +214,8 @@ def test_noop_mode(mocker): mock_tag_new_version = mocker.patch("semantic_release.cli.tag_new_version") mock_set_new = mocker.patch("semantic_release.cli.commit_new_version") mock_commit_new = mocker.patch("semantic_release.cli.set_new_version") - mocker.patch("semantic_release.cli.evaluate_version_bump", lambda *x: "major") + mocker.patch("semantic_release.cli.evaluate_version_bump", + lambda *x: "major") version(noop=True) @@ -221,6 +230,7 @@ def test_cli_print_version(mocker, runner): mock_print_version.assert_called_once_with( current=False, force_level=None, + prerelease=False, noop=False, post=False, retry=False, @@ -235,6 +245,7 @@ def test_cli_print_version_force_major(mocker, runner): mock_print_version.assert_called_once_with( current=False, force_level="major", + prerelease=False, noop=False, post=False, retry=False, @@ -249,6 +260,7 @@ def test_cli_print_version_current(mocker, runner): mock_print_version.assert_called_once_with( current=True, force_level=None, + prerelease=False, noop=False, post=False, retry=False, @@ -263,6 +275,7 @@ def test_cli_print_version_next(mocker, runner): mock_print_version.assert_called_once_with( current=False, force_level=None, + prerelease=False, noop=False, post=False, retry=False, @@ -328,7 +341,8 @@ def test_print_version_force_major(mocker, runner, capsys): def test_version_no_change(mocker, runner): mock_tag_new_version = mocker.patch("semantic_release.cli.tag_new_version") - mock_commit_new_version = mocker.patch("semantic_release.cli.commit_new_version") + mock_commit_new_version = mocker.patch( + "semantic_release.cli.commit_new_version") mock_set_new_version = mocker.patch("semantic_release.cli.set_new_version") mock_new_version = mocker.patch( "semantic_release.cli.get_new_version", return_value="1.2.3" @@ -358,7 +372,8 @@ def test_version_check_build_status_fails(mocker): mock_commit_new = mocker.patch("semantic_release.cli.commit_new_version") mock_set_new = mocker.patch("semantic_release.cli.set_new_version") mocker.patch("semantic_release.cli.config.get", lambda *x: True) - mocker.patch("semantic_release.cli.evaluate_version_bump", lambda *x: "major") + mocker.patch("semantic_release.cli.evaluate_version_bump", + lambda *x: "major") version() @@ -374,7 +389,8 @@ def test_version_by_commit_check_build_status_succeeds(mocker): "semantic_release.cli.check_build_status", return_value=True ) mock_tag_new_version = mocker.patch("semantic_release.cli.tag_new_version") - mocker.patch("semantic_release.cli.evaluate_version_bump", lambda *x: "major") + mocker.patch("semantic_release.cli.evaluate_version_bump", + lambda *x: "major") mock_commit_new = mocker.patch("semantic_release.cli.commit_new_version") mock_set_new = mocker.patch("semantic_release.cli.set_new_version") @@ -400,7 +416,8 @@ def test_version_by_tag_check_build_status_succeeds(mocker): ) mock_set_new_version = mocker.patch("semantic_release.cli.set_new_version") mock_tag_new_version = mocker.patch("semantic_release.cli.tag_new_version") - mocker.patch("semantic_release.cli.evaluate_version_bump", lambda *x: "major") + mocker.patch("semantic_release.cli.evaluate_version_bump", + lambda *x: "major") version() @@ -410,10 +427,12 @@ def test_version_by_tag_check_build_status_succeeds(mocker): def test_version_check_build_status_not_called_if_disabled(mocker): - mock_check_build_status = mocker.patch("semantic_release.cli.check_build_status") + mock_check_build_status = mocker.patch( + "semantic_release.cli.check_build_status") mocker.patch("semantic_release.cli.config.get", lambda *x, **y: False) mocker.patch("semantic_release.cli.tag_new_version") - mocker.patch("semantic_release.cli.evaluate_version_bump", lambda *x: "major") + mocker.patch("semantic_release.cli.evaluate_version_bump", + lambda *x: "major") mocker.patch("semantic_release.cli.commit_new_version") mocker.patch("semantic_release.cli.set_new_version") @@ -424,7 +443,8 @@ def test_version_check_build_status_not_called_if_disabled(mocker): def test_version_retry_and_giterror(mocker): mocker.patch( - "semantic_release.cli.get_current_version", mock.Mock(side_effect=GitError()) + "semantic_release.cli.get_current_version", mock.Mock( + side_effect=GitError()) ) result = version(retry=True) @@ -460,7 +480,8 @@ def test_publish_should_not_run_pre_commit_by_default(mocker): mocker.patch("semantic_release.cli.post_changelog", lambda *x: True) mocker.patch("semantic_release.cli.push_new_version", return_value=True) mocker.patch("semantic_release.cli.should_bump_version", return_value=True) - mocker.patch("semantic_release.cli.markdown_changelog", lambda *x, **y: "CHANGES") + mocker.patch("semantic_release.cli.markdown_changelog", + lambda *x, **y: "CHANGES") mocker.patch("semantic_release.cli.update_changelog_file") mocker.patch("semantic_release.cli.bump_version") mocker.patch("semantic_release.cli.get_new_version", lambda *x: "2.0.0") @@ -474,7 +495,8 @@ def test_publish_should_not_run_pre_commit_by_default(mocker): upload_to_release=False, ), ) - mocker.patch("semantic_release.cli.update_changelog_file", lambda *x, **y: None) + mocker.patch("semantic_release.cli.update_changelog_file", + lambda *x, **y: None) publish() @@ -489,7 +511,8 @@ def test_publish_should_not_run_pre_commit_with_empty_command(mocker): mocker.patch("semantic_release.cli.post_changelog", lambda *x: True) mocker.patch("semantic_release.cli.push_new_version", return_value=True) mocker.patch("semantic_release.cli.should_bump_version", return_value=True) - mocker.patch("semantic_release.cli.markdown_changelog", lambda *x, **y: "CHANGES") + mocker.patch("semantic_release.cli.markdown_changelog", + lambda *x, **y: "CHANGES") mocker.patch("semantic_release.cli.update_changelog_file") mocker.patch("semantic_release.cli.bump_version") mocker.patch("semantic_release.cli.get_new_version", lambda *x: "2.0.0") @@ -504,7 +527,8 @@ def test_publish_should_not_run_pre_commit_with_empty_command(mocker): pre_commit_command="", ), ) - mocker.patch("semantic_release.cli.update_changelog_file", lambda *x, **y: None) + mocker.patch("semantic_release.cli.update_changelog_file", + lambda *x, **y: None) publish() @@ -519,7 +543,8 @@ def test_publish_should_run_pre_commit_if_provided(mocker): mocker.patch("semantic_release.cli.post_changelog", lambda *x: True) mocker.patch("semantic_release.cli.push_new_version", return_value=True) mocker.patch("semantic_release.cli.should_bump_version", return_value=True) - mocker.patch("semantic_release.cli.markdown_changelog", lambda *x, **y: "CHANGES") + mocker.patch("semantic_release.cli.markdown_changelog", + lambda *x, **y: "CHANGES") mocker.patch("semantic_release.cli.update_changelog_file") mocker.patch("semantic_release.cli.bump_version") mocker.patch("semantic_release.cli.get_new_version", lambda *x: "2.0.0") @@ -534,7 +559,8 @@ def test_publish_should_run_pre_commit_if_provided(mocker): pre_commit_command='echo "Hello, world."', ), ) - mocker.patch("semantic_release.cli.update_changelog_file", lambda *x, **y: None) + mocker.patch("semantic_release.cli.update_changelog_file", + lambda *x, **y: None) publish() @@ -545,11 +571,14 @@ def test_publish_should_not_upload_to_pypi_if_option_is_false(mocker): mocker.patch("semantic_release.cli.checkout") mocker.patch("semantic_release.cli.ci_checks.check") mock_repository = mocker.patch.object(ArtifactRepo, "upload") - mock_upload_release = mocker.patch("semantic_release.cli.upload_to_release") + mock_upload_release = mocker.patch( + "semantic_release.cli.upload_to_release") mocker.patch("semantic_release.cli.post_changelog", lambda *x: True) mocker.patch("semantic_release.cli.push_new_version", return_value=True) - mocker.patch("semantic_release.cli.should_bump_version", return_value=False) - mocker.patch("semantic_release.cli.markdown_changelog", lambda *x, **y: "CHANGES") + mocker.patch("semantic_release.cli.should_bump_version", + return_value=False) + mocker.patch("semantic_release.cli.markdown_changelog", + lambda *x, **y: "CHANGES") mocker.patch("semantic_release.cli.update_changelog_file") mocker.patch("semantic_release.cli.bump_version") mocker.patch("semantic_release.cli.get_new_version", lambda *x: "2.0.0") @@ -562,7 +591,8 @@ def test_publish_should_not_upload_to_pypi_if_option_is_false(mocker): upload_to_release=False, ), ) - mocker.patch("semantic_release.cli.update_changelog_file", lambda *x, **y: None) + mocker.patch("semantic_release.cli.update_changelog_file", + lambda *x, **y: None) publish() @@ -574,11 +604,14 @@ def test_publish_should_not_upload_to_repository_if_option_is_false(mocker): mocker.patch("semantic_release.cli.checkout") mocker.patch("semantic_release.cli.ci_checks.check") mock_repository = mocker.patch.object(ArtifactRepo, "upload") - mock_upload_release = mocker.patch("semantic_release.cli.upload_to_release") + mock_upload_release = mocker.patch( + "semantic_release.cli.upload_to_release") mocker.patch("semantic_release.cli.post_changelog", lambda *x: True) mocker.patch("semantic_release.cli.push_new_version", return_value=True) - mocker.patch("semantic_release.cli.should_bump_version", return_value=False) - mocker.patch("semantic_release.cli.markdown_changelog", lambda *x, **y: "CHANGES") + mocker.patch("semantic_release.cli.should_bump_version", + return_value=False) + mocker.patch("semantic_release.cli.markdown_changelog", + lambda *x, **y: "CHANGES") mocker.patch("semantic_release.cli.update_changelog_file") mocker.patch("semantic_release.cli.bump_version") mocker.patch("semantic_release.cli.get_new_version", lambda *x: "2.0.0") @@ -591,7 +624,8 @@ def test_publish_should_not_upload_to_repository_if_option_is_false(mocker): upload_to_release=False, ), ) - mocker.patch("semantic_release.cli.update_changelog_file", lambda *x, **y: None) + mocker.patch("semantic_release.cli.update_changelog_file", + lambda *x, **y: None) publish() @@ -602,11 +636,13 @@ def test_publish_should_not_upload_to_repository_if_option_is_false(mocker): def test_publish_should_do_nothing_when_not_should_bump_version(mocker): mocker.patch("semantic_release.cli.checkout") mocker.patch("semantic_release.cli.get_new_version", lambda *x: "2.0.0") - mocker.patch("semantic_release.cli.evaluate_version_bump", lambda *x: "feature") + mocker.patch("semantic_release.cli.evaluate_version_bump", + lambda *x: "feature") mocker.patch("semantic_release.cli.generate_changelog") mock_log = mocker.patch("semantic_release.cli.post_changelog") mock_repository = mocker.patch.object(ArtifactRepo, "upload") - mock_upload_release = mocker.patch("semantic_release.cli.upload_to_release") + mock_upload_release = mocker.patch( + "semantic_release.cli.upload_to_release") mock_push = mocker.patch("semantic_release.cli.push_new_version") mock_ci_check = mocker.patch("semantic_release.ci_checks.check") mock_should_bump_version = mocker.patch( @@ -646,9 +682,11 @@ def test_publish_should_call_functions(mocker): "semantic_release.cli.get_repository_owner_and_name", return_value=("relekang", "python-semantic-release"), ) - mocker.patch("semantic_release.cli.evaluate_version_bump", lambda *x: "feature") + mocker.patch("semantic_release.cli.evaluate_version_bump", + lambda *x: "feature") mocker.patch("semantic_release.cli.generate_changelog") - mocker.patch("semantic_release.cli.markdown_changelog", lambda *x, **y: "CHANGES") + mocker.patch("semantic_release.cli.markdown_changelog", + lambda *x, **y: "CHANGES") mocker.patch("semantic_release.cli.update_changelog_file") mocker.patch("semantic_release.cli.bump_version") mocker.patch("semantic_release.cli.get_new_version", lambda *x: "2.0.0") @@ -692,9 +730,11 @@ def test_publish_should_skip_build_when_command_is_empty(mocker): "semantic_release.cli.get_repository_owner_and_name", return_value=("relekang", "python-semantic-release"), ) - mocker.patch("semantic_release.cli.evaluate_version_bump", lambda *x: "feature") + mocker.patch("semantic_release.cli.evaluate_version_bump", + lambda *x: "feature") mocker.patch("semantic_release.cli.generate_changelog") - mocker.patch("semantic_release.cli.markdown_changelog", lambda *x, **y: "CHANGES") + mocker.patch("semantic_release.cli.markdown_changelog", + lambda *x, **y: "CHANGES") mocker.patch("semantic_release.cli.update_changelog_file") mocker.patch("semantic_release.cli.bump_version") mocker.patch("semantic_release.cli.get_new_version", lambda *x: "2.0.0") @@ -774,7 +814,8 @@ def test_publish_bad_token(mocker): remove_dist=False, ), ) - mock_should_bump_version = mocker.patch("semantic_release.cli.should_bump_version") + mock_should_bump_version = mocker.patch( + "semantic_release.cli.should_bump_version") mock_get_token = mocker.patch( "semantic_release.cli.get_token", return_value="SUPERTOKEN" ) @@ -857,7 +898,8 @@ def test_publish_giterror_when_posting(mocker): "semantic_release.cli.update_changelog_file" ) mock_post = mocker.patch( - "semantic_release.cli.post_changelog", mock.Mock(side_effect=GitError()) + "semantic_release.cli.post_changelog", mock.Mock( + side_effect=GitError()) ) publish(noop=False, retry=False, force_level=False) @@ -871,7 +913,8 @@ def test_publish_giterror_when_posting(mocker): mock_should_bump_version.assert_called_once_with( current_version="current", new_version="new", noop=False, retry=False ) - mock_update_changelog_file.assert_called_once_with("new", "super md changelog") + mock_update_changelog_file.assert_called_once_with( + "new", "super md changelog") mock_bump_version.assert_called_once_with("new", "patch") mock_get_token.assert_called_once_with() mock_get_domain.assert_called_once_with() @@ -892,17 +935,20 @@ def test_publish_giterror_when_posting(mocker): header=False, previous_version="current", ) - mock_post.assert_called_once_with("owner", "name", "new", "super md changelog") + mock_post.assert_called_once_with( + "owner", "name", "new", "super md changelog") def test_changelog_should_call_functions(mocker, runner): - mock_changelog = mocker.patch("semantic_release.cli.changelog", return_value=True) + mock_changelog = mocker.patch( + "semantic_release.cli.changelog", return_value=True) result = runner.invoke(main, ["changelog"]) assert result.exit_code == 0 mock_changelog.assert_called_once_with( noop=False, post=False, force_level=None, + prerelease=False, retry=False, unreleased=False, define=(), @@ -929,7 +975,8 @@ def test_overload_by_cli(mocker, runner): def test_changelog_noop(mocker): - mocker.patch("semantic_release.cli.get_current_version", return_value="current") + mocker.patch("semantic_release.cli.get_current_version", + return_value="current") mock_previous_version = mocker.patch( "semantic_release.cli.get_previous_version", return_value="previous" ) @@ -954,7 +1001,8 @@ def test_changelog_noop(mocker): def test_changelog_post_unreleased_no_token(mocker): - mocker.patch("semantic_release.cli.get_current_version", return_value="current") + mocker.patch("semantic_release.cli.get_current_version", + return_value="current") mock_previous_version = mocker.patch( "semantic_release.cli.get_previous_version", return_value="previous" ) @@ -983,7 +1031,8 @@ def test_changelog_post_unreleased_no_token(mocker): def test_changelog_post_complete(mocker): - mocker.patch("semantic_release.cli.get_current_version", return_value="current") + mocker.patch("semantic_release.cli.get_current_version", + return_value="current") mock_previous_version = mocker.patch( "semantic_release.cli.get_previous_version", return_value="previous" ) From bac65fef37bb60b9131830b931ec65b785ca248a Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Tue, 8 Feb 2022 15:22:49 +0200 Subject: [PATCH 04/11] feat: omit_pattern for previouse and current version getters --- semantic_release/cli.py | 2 +- semantic_release/history/__init__.py | 21 ++++++++++----- semantic_release/vcs_helpers.py | 10 ++++--- tests/history/test_version.py | 15 +++++++++++ tests/test_cli.py | 15 +++++++++++ tests/test_vcs_helpers.py | 40 ++++++++++++++++++++++++++++ 6 files changed, 92 insertions(+), 11 deletions(-) diff --git a/semantic_release/cli.py b/semantic_release/cli.py index bfd4d3bfa..7e5d22e97 100644 --- a/semantic_release/cli.py +++ b/semantic_release/cli.py @@ -98,7 +98,7 @@ def common_options(func): # TODO: prerelease publish -def print_version(*, current=False, force_level=None, **kwargs): +def print_version(*, current=False, force_level=None, prerelease=False, **kwargs): """ Print the current or new version to standard output. """ diff --git a/semantic_release/history/__init__.py b/semantic_release/history/__init__.py index aff0573b1..3892973d1 100644 --- a/semantic_release/history/__init__.py +++ b/semantic_release/history/__init__.py @@ -177,13 +177,13 @@ def swap_version(m): @LoggedFunction(logger) -def get_current_version_by_tag() -> str: +def get_current_version_by_tag(omit_pattern=None) -> str: """ Find the current version of the package in the current working directory using git tags. :return: A string with the version number or 0.0.0 on failure. """ - version = get_last_version() + version = get_last_version(omit_pattern=omit_pattern) if version: return version @@ -192,7 +192,7 @@ def get_current_version_by_tag() -> str: @LoggedFunction(logger) -def get_current_version_by_config_file() -> str: +def get_current_version_by_config_file(omit_pattern=None) -> str: """ Get current version from the version variable defined in the configuration. @@ -219,12 +219,17 @@ def get_current_version_by_config_file() -> str: def get_current_version() -> str: """ Get current version from tag or version variable, depending on configuration. + This will not return prerelease versions. :return: A string with the current version number """ + omit_pattern = "-" + config.get("prerelease_tag") if config.get("version_source") == "tag": - return get_current_version_by_tag() - return get_current_version_by_config_file() + return get_current_version_by_tag(omit_pattern) + current_version = get_current_version_by_config_file(omit_pattern) + if omit_pattern in current_version: + return get_previous_version(current_version) + return current_version # TODO: prerelease version bump @LoggedFunction(logger) @@ -244,7 +249,7 @@ def get_new_version(current_version: str, level_bump: str) -> str: @LoggedFunction(logger) -def get_previous_version(version: str) -> Optional[str]: +def get_previous_version(version: str, omit_pattern: str = None) -> Optional[str]: """ Return the version prior to the given version. @@ -260,12 +265,14 @@ def get_previous_version(version: str) -> Optional[str]: continue if found_version: + if omit_pattern and omit_pattern in commit_message: + continue matches = re.match(r"v?(\d+.\d+.\d+)", commit_message) if matches: logger.debug(f"Version matches regex {commit_message}") return matches.group(1).strip() - return get_last_version([version, get_formatted_tag(version)]) + return get_last_version([version, get_formatted_tag(version)], omit_pattern=omit_pattern) @LoggedFunction(logger) diff --git a/semantic_release/vcs_helpers.py b/semantic_release/vcs_helpers.py index 78c6e5a28..a58d98480 100644 --- a/semantic_release/vcs_helpers.py +++ b/semantic_release/vcs_helpers.py @@ -63,7 +63,7 @@ def get_commit_log(from_rev=None): @check_repo @LoggedFunction(logger) -def get_last_version(skip_tags=None) -> Optional[str]: +def get_last_version(skip_tags=None, omit_pattern=None) -> Optional[str]: """ Find the latest version using repo tags. @@ -78,8 +78,12 @@ def version_finder(tag): for i in sorted(repo.tags, reverse=True, key=version_finder): match = re.search(r"\d+\.\d+\.\d+", i.name) - if match and i.name not in skip_tags: - return match.group(0) # Return only numeric vesion like 1.2.3 + if match: + # check if the omit pattern is present in the tag (e.g. -beta for pre-release tags) + if omit_pattern and omit_pattern in i.name: + continue + if i.name not in skip_tags: + return match.group(0) # Return only numeric vesion like 1.2.3 return None diff --git a/tests/history/test_version.py b/tests/history/test_version.py index a8c111cb5..80dfb9413 100644 --- a/tests/history/test_version.py +++ b/tests/history/test_version.py @@ -65,6 +65,21 @@ def test_should_return_correct_version(self): def test_should_return_correct_version_with_v(self): assert get_previous_version("0.10.0") == "0.9.0" + @mock.patch( + "semantic_release.history.get_commit_log", + lambda: [("211", "0.10.0-beta"), ("13", "0.9.0")], + ) + def test_should_return_correct_version_from_prerelease(self): + assert get_previous_version("0.10.0-beta") == "0.9.0" + + @mock.patch( + "semantic_release.history.get_commit_log", + lambda: [("211", "0.10.0"), ("13", "0.10.0-beta"), ("13", "0.9.0")], + ) + def test_should_return_correct_version_skip_prerelease(self): + assert get_previous_version( + "0.10.0-beta", omit_pattern="-beta") == "0.9.0" + class TestGetNewVersion: def test_major_bump(self): diff --git a/tests/test_cli.py b/tests/test_cli.py index ea1d9e79d..013a4966e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -254,6 +254,21 @@ def test_cli_print_version_force_major(mocker, runner): assert result.exit_code == 0 +def test_cli_print_version_prerelease(mocker, runner): + mock_print_version = mocker.patch("semantic_release.cli.print_version") + result = runner.invoke(main, ["print-version", "--prerelease"]) + mock_print_version.assert_called_once_with( + current=False, + force_level=None, + prerelease=True, + noop=False, + post=False, + retry=False, + define=(), + ) + assert result.exit_code == 0 + + def test_cli_print_version_current(mocker, runner): mock_print_version = mocker.patch("semantic_release.cli.print_version") result = runner.invoke(main, ["print-version", "--current"]) diff --git a/tests/test_vcs_helpers.py b/tests/test_vcs_helpers.py index 0227d0b91..45107aa1c 100644 --- a/tests/test_vcs_helpers.py +++ b/tests/test_vcs_helpers.py @@ -348,6 +348,46 @@ def __init__(self, name, sha, date, is_tag_object): assert expected_result == get_last_version(skip_tags) +@pytest.mark.parametrize( + "skip_tags,expected_result", + [ + (None, "2.0.0"), + (["v2.0.0"], "1.1.0"), + (["v0.1.0", "v1.0.0", "v1.1.0", "v2.0.0"], None), + ], +) +def test_get_last_version_with_omit_pattern(skip_tags, expected_result): + class FakeCommit: + def __init__(self, com_date): + self.committed_date = com_date + + class FakeTagObject: + def __init__(self, tag_date): + self.tagged_date = tag_date + + class FakeTag: + def __init__(self, name, sha, date, is_tag_object): + self.name = name + self.tag = FakeTagObject(date) + if is_tag_object: + self.commit = TagObject(Repo(), sha) + else: + self.commit = FakeCommit(date) + + mock.patch("semantic_release.vcs_helpers.check_repo") + git.repo.base.Repo.tags = mock.PropertyMock( + return_value=[ + FakeTag("v0.1.0", "aaaaaaaaaaaaaaaaaaaa", 1, True), + FakeTag("v2.0.0", "dddddddddddddddddddd", 4, True), + FakeTag("v2.1.0-beta", "ffffffffffffffffffff", 6, True), + FakeTag("badly_formatted", "eeeeeeeeeeeeeeeeeeee", 5, False), + FakeTag("v1.1.0", "cccccccccccccccccccc", 3, True), + FakeTag("v1.0.0", "bbbbbbbbbbbbbbbbbbbb", 2, False), + ] + ) + assert expected_result == get_last_version(skip_tags, omit_pattern="-beta") + + @pytest.mark.parametrize( "tag_name,expected_version", [ From d8a8a324626afd575b6117b74a129c24f7acb49b Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Tue, 8 Feb 2022 16:36:05 +0200 Subject: [PATCH 05/11] feat: print_version with prerelease bump --- semantic_release/cli.py | 7 +++++++ semantic_release/history/__init__.py | 10 +++++++--- tests/test_cli.py | 22 ++++++++++++++++++++++ 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/semantic_release/cli.py b/semantic_release/cli.py index 7e5d22e97..19e99b47f 100644 --- a/semantic_release/cli.py +++ b/semantic_release/cli.py @@ -114,6 +114,13 @@ def print_version(*, current=False, force_level=None, prerelease=False, **kwargs # Find what the new version number should be level_bump = evaluate_version_bump(current_version, force_level) new_version = get_new_version(current_version, level_bump) + if prerelease: + current_prerelease_version = get_current_version(prerelease_version=True) + if current_prerelease_version: + prerelease_num = int(current_prerelease_version.split(".")[-1]) + 1 + else: + prerelease_num = 0 + new_version = new_version + get_prerelease_pattern() + str(prerelease_num) if should_bump_version(current_version=current_version, new_version=new_version): print(new_version, end="") return True diff --git a/semantic_release/history/__init__.py b/semantic_release/history/__init__.py index 3892973d1..e0e8d5301 100644 --- a/semantic_release/history/__init__.py +++ b/semantic_release/history/__init__.py @@ -176,6 +176,10 @@ def swap_version(m): self.path.write_text(new_content) +def get_prerelease_pattern() -> str: + return "-" + config.get("prerelease_tag") + "." + + @LoggedFunction(logger) def get_current_version_by_tag(omit_pattern=None) -> str: """ @@ -216,18 +220,18 @@ def get_current_version_by_config_file(omit_pattern=None) -> str: return version -def get_current_version() -> str: +def get_current_version(prerelease_version: bool = False) -> str: """ Get current version from tag or version variable, depending on configuration. This will not return prerelease versions. :return: A string with the current version number """ - omit_pattern = "-" + config.get("prerelease_tag") + omit_pattern = None if prerelease_version else get_prerelease_pattern() if config.get("version_source") == "tag": return get_current_version_by_tag(omit_pattern) current_version = get_current_version_by_config_file(omit_pattern) - if omit_pattern in current_version: + if omit_pattern and omit_pattern in current_version: return get_previous_version(current_version) return current_version diff --git a/tests/test_cli.py b/tests/test_cli.py index 013a4966e..fbefb29a3 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -337,6 +337,28 @@ def test_print_version_change(mocker, runner, capsys): mock_evaluate_bump.assert_called_once_with("1.2.3", None) +def test_print_version_change_prerelease_bump(mocker, runner, capsys): + def get_current_version_mock(prerelease_version: bool = False): + if prerelease_version: + return "1.3.0-beta.0" + return "1.2.3" + mock_current_version = mocker.patch( + "semantic_release.cli.get_current_version", side_effect=get_current_version_mock + ) + mock_evaluate_bump = mocker.patch( + "semantic_release.cli.evaluate_version_bump", return_value="minor" + ) + + print_version(prerelease=True) + outerr = capsys.readouterr() + assert outerr.out == "1.3.0-beta.1" + assert outerr.err == "" + + mock_current_version.assert_any_call() + mock_current_version.assert_any_call(prerelease_version=True) + mock_evaluate_bump.assert_called_once_with("1.2.3", None) + + def test_print_version_force_major(mocker, runner, capsys): mock_current_version = mocker.patch( "semantic_release.cli.get_current_version", return_value="1.2.3" From b1aca66c732accb9e983eaac688fca46524c1f2a Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Tue, 8 Feb 2022 16:41:45 +0200 Subject: [PATCH 06/11] feat: make print_version prerelease ready --- semantic_release/cli.py | 7 ++++--- tests/test_cli.py | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/semantic_release/cli.py b/semantic_release/cli.py index 19e99b47f..d3f5fcfa3 100644 --- a/semantic_release/cli.py +++ b/semantic_release/cli.py @@ -19,6 +19,7 @@ get_new_version, get_previous_version, set_new_version, + get_prerelease_pattern ) from .history.logs import generate_changelog from .hvcs import ( @@ -115,9 +116,9 @@ def print_version(*, current=False, force_level=None, prerelease=False, **kwargs level_bump = evaluate_version_bump(current_version, force_level) new_version = get_new_version(current_version, level_bump) if prerelease: - current_prerelease_version = get_current_version(prerelease_version=True) - if current_prerelease_version: - prerelease_num = int(current_prerelease_version.split(".")[-1]) + 1 + potentialy_prereleased_current_version = get_current_version(prerelease_version=True) + if get_prerelease_pattern() in potentialy_prereleased_current_version: + prerelease_num = int(potentialy_prereleased_current_version.split(".")[-1]) + 1 else: prerelease_num = 0 new_version = new_version + get_prerelease_pattern() + str(prerelease_num) diff --git a/tests/test_cli.py b/tests/test_cli.py index fbefb29a3..49cbf8d57 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -337,6 +337,24 @@ def test_print_version_change(mocker, runner, capsys): mock_evaluate_bump.assert_called_once_with("1.2.3", None) +def test_print_version_change_prerelease(mocker, runner, capsys): + mock_current_version = mocker.patch( + "semantic_release.cli.get_current_version", return_value="1.2.3" + ) + mock_evaluate_bump = mocker.patch( + "semantic_release.cli.evaluate_version_bump", return_value="minor" + ) + + print_version(prerelease=True) + outerr = capsys.readouterr() + assert outerr.out == "1.3.0-beta.0" + assert outerr.err == "" + + mock_current_version.assert_any_call() + mock_current_version.assert_any_call(prerelease_version=True) + mock_evaluate_bump.assert_called_once_with("1.2.3", None) + + def test_print_version_change_prerelease_bump(mocker, runner, capsys): def get_current_version_mock(prerelease_version: bool = False): if prerelease_version: @@ -376,6 +394,24 @@ def test_print_version_force_major(mocker, runner, capsys): mock_evaluate_bump.assert_called_once_with("1.2.3", "major") +def test_print_version_force_major_prerelease(mocker, runner, capsys): + mock_current_version = mocker.patch( + "semantic_release.cli.get_current_version", return_value="1.2.3" + ) + mock_evaluate_bump = mocker.patch( + "semantic_release.cli.evaluate_version_bump", return_value="major" + ) + + print_version(force_level="major", prerelease=True) + outerr = capsys.readouterr() + assert outerr.out == "2.0.0-beta.0" + assert outerr.err == "" + + mock_current_version.assert_any_call() + mock_current_version.assert_any_call(prerelease_version=True) + mock_evaluate_bump.assert_called_once_with("1.2.3", "major") + + def test_version_no_change(mocker, runner): mock_tag_new_version = mocker.patch("semantic_release.cli.tag_new_version") mock_commit_new_version = mocker.patch( From 7be3e365f68fa72b566d9f30b1347ef1d4e164db Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Wed, 9 Feb 2022 09:17:29 +0200 Subject: [PATCH 07/11] feat: move prerelease determination to get_new_version --- semantic_release/cli.py | 14 ++------------ semantic_release/history/__init__.py | 25 ++++++++++++++++++++----- semantic_release/history/logs.py | 2 +- tests/history/test_version.py | 21 +++++++++++++++++---- tests/test_cli.py | 20 ++++++++------------ 5 files changed, 48 insertions(+), 34 deletions(-) diff --git a/semantic_release/cli.py b/semantic_release/cli.py index d3f5fcfa3..ddf6916f3 100644 --- a/semantic_release/cli.py +++ b/semantic_release/cli.py @@ -18,8 +18,7 @@ get_current_version, get_new_version, get_previous_version, - set_new_version, - get_prerelease_pattern + set_new_version ) from .history.logs import generate_changelog from .hvcs import ( @@ -114,14 +113,7 @@ def print_version(*, current=False, force_level=None, prerelease=False, **kwargs # Find what the new version number should be level_bump = evaluate_version_bump(current_version, force_level) - new_version = get_new_version(current_version, level_bump) - if prerelease: - potentialy_prereleased_current_version = get_current_version(prerelease_version=True) - if get_prerelease_pattern() in potentialy_prereleased_current_version: - prerelease_num = int(potentialy_prereleased_current_version.split(".")[-1]) + 1 - else: - prerelease_num = 0 - new_version = new_version + get_prerelease_pattern() + str(prerelease_num) + new_version = get_new_version(current_version, level_bump, prerelease) if should_bump_version(current_version=current_version, new_version=new_version): print(new_version, end="") return True @@ -167,8 +159,6 @@ def version(*, retry=False, noop=False, force_level=None, **kwargs): bump_version(new_version, level_bump) return True -# TODO: compare prerelease - def should_bump_version(*, current_version, new_version, retry=False, noop=False): """Test whether the version should be bumped.""" diff --git a/semantic_release/history/__init__.py b/semantic_release/history/__init__.py index e0e8d5301..47bc36332 100644 --- a/semantic_release/history/__init__.py +++ b/semantic_release/history/__init__.py @@ -235,21 +235,36 @@ def get_current_version(prerelease_version: bool = False) -> str: return get_previous_version(current_version) return current_version -# TODO: prerelease version bump + @LoggedFunction(logger) -def get_new_version(current_version: str, level_bump: str) -> str: +def get_new_version(current_version: str, level_bump: str, prerelease: bool = False) -> str: """ Calculate the next version based on the given bump level with semver. :param current_version: The version the package has now. :param level_bump: The level of the version number that should be bumped. Should be `'major'`, `'minor'` or `'patch'`. + :param prerelease: Should the version bump be marked as a prerelease :return: A string with the next version number. """ if not level_bump: - logger.debug("No bump requested, returning input version") - return current_version - return str(semver.VersionInfo.parse(current_version).next_version(part=level_bump)) + logger.debug("No bump requested, using input version") + new_version = current_version + else: + new_version = str(semver.VersionInfo.parse(current_version).next_version(part=level_bump)) + + if prerelease: + logger.debug("Prerelease requested") + potentialy_prereleased_current_version = get_current_version(prerelease_version=True) + if get_prerelease_pattern() in potentialy_prereleased_current_version: + logger.debug("Previouse prerelease detected, increment prerelease version") + prerelease_num = int(potentialy_prereleased_current_version.split(".")[-1]) + 1 + else: + logger.debug("No previouse prerelease detected, starting from 0") + prerelease_num = 0 + new_version = new_version + get_prerelease_pattern() + str(prerelease_num) + + return new_version @LoggedFunction(logger) diff --git a/semantic_release/history/logs.py b/semantic_release/history/logs.py index e5e83e76b..e4d274847 100644 --- a/semantic_release/history/logs.py +++ b/semantic_release/history/logs.py @@ -17,7 +17,7 @@ 3: "major", } -# TODO: detect version bump + @LoggedFunction(logger) def evaluate_version_bump(current_version: str, force: str = None) -> Optional[str]: """ diff --git a/tests/history/test_version.py b/tests/history/test_version.py index 80dfb9413..a37df1b6f 100644 --- a/tests/history/test_version.py +++ b/tests/history/test_version.py @@ -103,6 +103,19 @@ def test_patch_bump(self): def test_none_bump(self): assert get_new_version("1.0.0", None) == "1.0.0" + def test_prerelease(self): + assert get_new_version("1.0.0", None, True) == "1.0.0-beta.0" + assert get_new_version("1.0.0", "major", True) == "2.0.0-beta.0" + assert get_new_version("1.0.0", "minor", True) == "1.1.0-beta.0" + assert get_new_version("1.0.0", "patch", True) == "1.0.1-beta.0" + + def test_prerelease_bump(self, mocker): + mocker.patch( + "semantic_release.history.get_current_version", + return_value="1.0.0-beta.0" + ) + assert get_new_version("1.0.0", None, True) == "1.0.0-beta.1" + @mock.patch( "semantic_release.history.config.get", @@ -268,11 +281,11 @@ def test_toml_parse(self, tmp_path, key, content, hits): name = "my-package" version = "0.1.0" description = "A super package" - + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" - + [tool.semantic_release] version_toml = "pyproject.toml:tool.poetry.version" """ @@ -283,11 +296,11 @@ def test_toml_parse(self, tmp_path, key, content, hits): name = "my-package" version = "-" description = "A super package" - + [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" - + [tool.semantic_release] version_toml = "pyproject.toml:tool.poetry.version" """ diff --git a/tests/test_cli.py b/tests/test_cli.py index 49cbf8d57..c33ecff72 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -317,7 +317,7 @@ def test_print_version_no_change(mocker, runner, capsys): mock_current_version.assert_called_once_with() mock_evaluate_bump.assert_called_once_with("1.2.3", None) - mock_new_version.assert_called_once_with("1.2.3", None) + mock_new_version.assert_called_once_with("1.2.3", None, False) def test_print_version_change(mocker, runner, capsys): @@ -350,18 +350,16 @@ def test_print_version_change_prerelease(mocker, runner, capsys): assert outerr.out == "1.3.0-beta.0" assert outerr.err == "" - mock_current_version.assert_any_call() - mock_current_version.assert_any_call(prerelease_version=True) + mock_current_version.assert_called_once() mock_evaluate_bump.assert_called_once_with("1.2.3", None) def test_print_version_change_prerelease_bump(mocker, runner, capsys): - def get_current_version_mock(prerelease_version: bool = False): - if prerelease_version: - return "1.3.0-beta.0" - return "1.2.3" mock_current_version = mocker.patch( - "semantic_release.cli.get_current_version", side_effect=get_current_version_mock + "semantic_release.cli.get_current_version", return_value="1.2.3" + ) + mock_current_version = mocker.patch( + "semantic_release.history.get_current_version", return_value="1.3.0-beta.0" ) mock_evaluate_bump = mocker.patch( "semantic_release.cli.evaluate_version_bump", return_value="minor" @@ -372,8 +370,7 @@ def get_current_version_mock(prerelease_version: bool = False): assert outerr.out == "1.3.0-beta.1" assert outerr.err == "" - mock_current_version.assert_any_call() - mock_current_version.assert_any_call(prerelease_version=True) + mock_current_version.assert_called_once() mock_evaluate_bump.assert_called_once_with("1.2.3", None) @@ -407,8 +404,7 @@ def test_print_version_force_major_prerelease(mocker, runner, capsys): assert outerr.out == "2.0.0-beta.0" assert outerr.err == "" - mock_current_version.assert_any_call() - mock_current_version.assert_any_call(prerelease_version=True) + mock_current_version.assert_called_once() mock_evaluate_bump.assert_called_once_with("1.2.3", "major") From 2c32fc675767c3193158ea7fc55b6b352263d03f Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Wed, 9 Feb 2022 09:24:14 +0200 Subject: [PATCH 08/11] test: improve get_last_version test --- tests/test_vcs_helpers.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_vcs_helpers.py b/tests/test_vcs_helpers.py index 45107aa1c..1fbabf950 100644 --- a/tests/test_vcs_helpers.py +++ b/tests/test_vcs_helpers.py @@ -378,9 +378,10 @@ def __init__(self, name, sha, date, is_tag_object): git.repo.base.Repo.tags = mock.PropertyMock( return_value=[ FakeTag("v0.1.0", "aaaaaaaaaaaaaaaaaaaa", 1, True), - FakeTag("v2.0.0", "dddddddddddddddddddd", 4, True), - FakeTag("v2.1.0-beta", "ffffffffffffffffffff", 6, True), - FakeTag("badly_formatted", "eeeeeeeeeeeeeeeeeeee", 5, False), + FakeTag("v2.0.0", "dddddddddddddddddddd", 5, True), + FakeTag("v2.1.0-beta", "ffffffffffffffffffff", 7, True), + FakeTag("badly_formatted", "eeeeeeeeeeeeeeeeeeee", 6, False), + FakeTag("v2.0.0-beta", "ffffffffffffffffffff", 4, True), FakeTag("v1.1.0", "cccccccccccccccccccc", 3, True), FakeTag("v1.0.0", "bbbbbbbbbbbbbbbbbbbb", 2, False), ] From 26b560dd6eb6967b8a0abb80f64fbbbbc404eb9f Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Wed, 9 Feb 2022 09:36:15 +0200 Subject: [PATCH 09/11] docs: added basic infos about prereleases --- docs/commands.rst | 6 ++++++ docs/configuration.rst | 27 +++++++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/commands.rst b/docs/commands.rst index fa1d7e638..018482918 100644 --- a/docs/commands.rst +++ b/docs/commands.rst @@ -74,6 +74,12 @@ Force a minor release, ignoring the version bump determined from commit messages Force a major release, ignoring the version bump determined from commit messages. +``--prerelease`` +........... + +Makes the next release a prerelease, version bumps are still determined or can be forced, +but the `prerelease_tag` (see :ref:`config-prerelease_tag`) will be appended to version number. + ``--noop`` .......... diff --git a/docs/configuration.rst b/docs/configuration.rst index 03019b3e3..abbd875c8 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -33,12 +33,12 @@ The file and variable name of where the version number is stored, for example:: semantic_release/__init__.py:__version__ -You can specify multiple version variables (i.e. in different files) by +You can specify multiple version variables (i.e. in different files) by providing comma-separated list of such strings:: semantic_release/__init__.py:__version__,docs/conf.py:version -In ``pyproject.toml`` specifically, you can also use the TOML list syntax to +In ``pyproject.toml`` specifically, you can also use the TOML list syntax to specify multiple versions: .. code-block:: toml @@ -67,14 +67,14 @@ identified using an arbitrary regular expression:: README.rst:VERSION (\d+\.\d+\.\d+) -The regular expression must contain a parenthesized group that matches the -version number itself. Anything outside that group is just context. For -example, the above specifies that there is a version number in ``README.rst`` +The regular expression must contain a parenthesized group that matches the +version number itself. Anything outside that group is just context. For +example, the above specifies that there is a version number in ``README.rst`` preceded by the string "VERSION". -If the pattern contains the string ``{version}``, it will be replaced with the -regular expression used internally by ``python-semantic-release`` to match -semantic version numbers. So the above example would probably be better +If the pattern contains the string ``{version}``, it will be replaced with the +regular expression used internally by ``python-semantic-release`` to match +semantic version numbers. So the above example would probably be better written as:: README.rst:VERSION {version} @@ -95,6 +95,17 @@ The way we get and set the new version. Can be `commit` or `tag`. Default: `commit` +.. _config-prerelease_tag: + +``prerelease_tag`` +------------------ +Defined the prerelease marker appended to the version when doing a prerelease. + +- The format of a prerelease version will be `{tag_format}-{prerelease_tag}.`, + e.g. `1.0.0-beta.0` or `1.1.0-beta.1` + +Default: `beta` + .. _config-tag_commit: ``tag_commit`` From 884e9a360fd02fa407aed452205f89074122094d Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Tue, 15 Feb 2022 16:05:26 +0200 Subject: [PATCH 10/11] feat: add prerelease flag to version and publish --- semantic_release/cli.py | 8 ++++---- tests/test_cli.py | 14 +++++++------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/semantic_release/cli.py b/semantic_release/cli.py index ddf6916f3..65c092234 100644 --- a/semantic_release/cli.py +++ b/semantic_release/cli.py @@ -124,7 +124,7 @@ def print_version(*, current=False, force_level=None, prerelease=False, **kwargs # TODO: prerelease publish -def version(*, retry=False, noop=False, force_level=None, **kwargs): +def version(*, retry=False, noop=False, force_level=None, prerelease=False, **kwargs): """ Detect the new version according to git log and semver. @@ -144,7 +144,7 @@ def version(*, retry=False, noop=False, force_level=None, **kwargs): return False # Find what the new version number should be level_bump = evaluate_version_bump(current_version, force_level) - new_version = get_new_version(current_version, level_bump) + new_version = get_new_version(current_version, level_bump, prerelease) if not should_bump_version( current_version=current_version, new_version=new_version, retry=retry, noop=noop @@ -245,7 +245,7 @@ def changelog(*, unreleased=False, noop=False, post=False, **kwargs): # TODO: prerelease publish -def publish(retry: bool = False, noop: bool = False, **kwargs): +def publish(retry: bool = False, noop: bool = False, prerelease=False, **kwargs): """Run the version task, then push to git and upload to an artifact repository / GitHub Releases.""" current_version = get_current_version() @@ -261,7 +261,7 @@ def publish(retry: bool = False, noop: bool = False, **kwargs): # Calculate the new version level_bump = evaluate_version_bump( current_version, kwargs.get("force_level")) - new_version = get_new_version(current_version, level_bump) + new_version = get_new_version(current_version, level_bump, prerelease) owner, name = get_repository_owner_and_name() diff --git a/tests/test_cli.py b/tests/test_cli.py index c33ecff72..821fd073d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -54,7 +54,7 @@ def test_version_by_commit_should_call_correct_functions(mocker): mock_current_version.assert_called_once_with() mock_evaluate_bump.assert_called_once_with("1.2.3", None) - mock_new_version.assert_called_once_with("1.2.3", "major") + mock_new_version.assert_called_once_with("1.2.3", "major", False) mock_set_new_version.assert_called_once_with("2.0.0") mock_commit_new_version.assert_called_once_with("2.0.0") mock_tag_new_version.assert_called_once_with("2.0.0") @@ -90,7 +90,7 @@ def test_version_by_tag_with_commit_version_number_should_call_correct_functions mock_current_version.assert_called_once_with() mock_evaluate_bump.assert_called_once_with("1.2.3", None) - mock_new_version.assert_called_once_with("1.2.3", "major") + mock_new_version.assert_called_once_with("1.2.3", "major", False) mock_set_new_version.assert_called_once_with("2.0.0") mock_commit_new_version.assert_called_once_with("2.0.0") mock_tag_new_version.assert_called_once_with("2.0.0") @@ -117,7 +117,7 @@ def test_version_by_tag_should_call_correct_functions(mocker): mock_current_version.assert_called_once_with() mock_evaluate_bump.assert_called_once_with("1.2.3", None) - mock_new_version.assert_called_once_with("1.2.3", "major") + mock_new_version.assert_called_once_with("1.2.3", "major", False) mock_set_new_version.assert_called_once_with("2.0.0") mock_tag_new_version.assert_called_once_with("2.0.0") @@ -145,7 +145,7 @@ def test_version_by_commit_without_tag_should_call_correct_functions(mocker): mock_current_version.assert_called_once_with() mock_evaluate_bump.assert_called_once_with("1.2.3", None) - mock_new_version.assert_called_once_with("1.2.3", "major") + mock_new_version.assert_called_once_with("1.2.3", "major", False) mock_set_new_version.assert_called_once_with("2.0.0") mock_commit_new_version.assert_called_once_with("2.0.0") assert not mock_tag_new_version.called @@ -427,7 +427,7 @@ def test_version_no_change(mocker, runner): mock_current_version.assert_called_once_with() mock_evaluate_bump.assert_called_once_with("1.2.3", None) - mock_new_version.assert_called_once_with("1.2.3", None) + mock_new_version.assert_called_once_with("1.2.3", None, False) assert not mock_set_new_version.called assert not mock_commit_new_version.called assert not mock_tag_new_version.called @@ -538,7 +538,7 @@ def test_version_retry(mocker): assert result mock_get_current.assert_called_once_with() mock_evaluate_bump.assert_called_once_with("current", False) - mock_get_new.assert_called_once_with("current", "patch") + mock_get_new.assert_called_once_with("current", "patch", False) def test_publish_should_not_run_pre_commit_by_default(mocker): @@ -975,7 +975,7 @@ def test_publish_giterror_when_posting(mocker): mock_get_current.assert_called_once_with() mock_evaluate.assert_called_once_with("current", False) - mock_get_new.assert_called_once_with("current", "patch") + mock_get_new.assert_called_once_with("current", "patch", False) mock_get_owner_name.assert_called_once_with() mock_ci_check.assert_called() mock_checkout.assert_called_once_with("my_branch") From 804d303d02794766d6b9dd1ca20234513f0bb0e4 Mon Sep 17 00:00:00 2001 From: Mario Jaeckle Date: Mon, 7 Mar 2022 12:34:30 +0200 Subject: [PATCH 11/11] feat: remove leftover todos --- semantic_release/cli.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/semantic_release/cli.py b/semantic_release/cli.py index 65c092234..9052b9b1d 100644 --- a/semantic_release/cli.py +++ b/semantic_release/cli.py @@ -95,8 +95,6 @@ def common_options(func): func = option(func) return func -# TODO: prerelease publish - def print_version(*, current=False, force_level=None, prerelease=False, **kwargs): """ @@ -121,8 +119,6 @@ def print_version(*, current=False, force_level=None, prerelease=False, **kwargs print("No release will be made.", file=sys.stderr) return False -# TODO: prerelease publish - def version(*, retry=False, noop=False, force_level=None, prerelease=False, **kwargs): """ @@ -242,8 +238,6 @@ def changelog(*, unreleased=False, noop=False, post=False, **kwargs): else: logger.error("Missing token: cannot post changelog to HVCS") -# TODO: prerelease publish - def publish(retry: bool = False, noop: bool = False, prerelease=False, **kwargs): """Run the version task, then push to git and upload to an artifact repository / GitHub Releases.""" @@ -407,8 +401,6 @@ def main(**kwargs): obj[key] = config.get(key) logger.debug(f"Main config: {obj}") -# TODO: add --prerelease flag - @main.command(name="publish", help=publish.__doc__) @common_options @@ -433,8 +425,6 @@ def cmd_changelog(**kwargs): logger.error(filter_output_for_secrets(str(error))) exit(1) -# TODO: add --prerelease flag - @main.command(name="version", help=version.__doc__) @common_options @@ -445,8 +435,6 @@ def cmd_version(**kwargs): logger.error(filter_output_for_secrets(str(error))) exit(1) -# TODO: add --prerelease flag - @main.command(name="print-version", help=print_version.__doc__) @common_options