From 2bdd08538a93961d12960fb63b34fe86cb23e6b5 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 13:48:07 -0500 Subject: [PATCH 01/22] test(fixtures): add common fixture to write default changelog from repo definition --- tests/const.py | 5 ++++ tests/fixtures/git_repo.py | 60 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/tests/const.py b/tests/const.py index d53ddd967..7108f892a 100644 --- a/tests/const.py +++ b/tests/const.py @@ -1,3 +1,5 @@ +from datetime import datetime + A_FULL_VERSION_STRING = "1.11.567" A_PRERELEASE_VERSION_STRING = "2.3.4-dev.23" A_FULL_VERSION_STRING_WITH_BUILD_METADATA = "4.2.3+build.12345" @@ -6,6 +8,9 @@ EXAMPLE_REPO_NAME = "example_repo" EXAMPLE_HVCS_DOMAIN = "example.com" +TODAY_DATE_STR = datetime.now().strftime("%Y-%m-%d") +"""Date formatted as how it would appear in the changelog (Must match local timezone)""" + COMMIT_MESSAGE = "{version}\n\nAutomatically generated by python-semantic-release\n" # Different in-scope commits that produce a certain release type diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 7c55b5a2c..e06a4ea13 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -11,6 +11,7 @@ EXAMPLE_HVCS_DOMAIN, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, + TODAY_DATE_STR, ) from tests.util import ( add_text_to_file, @@ -103,6 +104,15 @@ class GetRepoDefinitionFn(Protocol): def __call__(self, commit_type: CommitConvention = "angular") -> RepoDefinition: ... + class SimulateDefaultChangelogCreationFn(Protocol): + def __call__( + self, + repo_definition: RepoDefinition, + dest_file: Path | None = None, + tag_format: str = ... + ) -> str: + ... + @pytest.fixture(scope="session") def commit_author(): @@ -314,6 +324,56 @@ def _build_configured_base_repo( return _build_configured_base_repo +@pytest.fixture(scope="session") +def simulate_default_changelog_creation( + default_tag_format_str: str, +) -> SimulateDefaultChangelogCreationFn: + def build_version_entry(version: VersionStr, version_def: RepoVersionDef, tag_format: str) -> str: + version_entry = [] + if version == "Unreleased": + version_entry.append(f"## {version}\n") + else: + version_entry.append( + # TODO: artificial newline in front due to template when no Unreleased changes exist + f"\n## {tag_format.format(version=version)} ({TODAY_DATE_STR})\n" + ) + + for section_def in version_def["changelog_sections"]: + version_entry.append(f"### {section_def['section']}\n") + for i in section_def["i_commits"]: + version_entry.append(f"* {version_def['commits'][i]}\n") + + return str.join("\n", version_entry) + + def _mimic_semantic_release_default_changelog( + repo_definition: RepoDefinition, + dest_file: Path | None = None, + tag_format: str = default_tag_format_str, + ) -> str: + header = "# CHANGELOG" + version_entries = [] + + for version, version_def in repo_definition.items(): + # prepend entries to force reverse ordering + version_entries.insert( + 0, build_version_entry(version, version_def, tag_format) + ) + + changelog_content = str.join("\n" * 3, [ + header, + str.join("\n", [ + entry for entry in version_entries + ]) + ]) + + if dest_file is not None: + dest_file.write_text(changelog_content) + + return changelog_content + + return _mimic_semantic_release_default_changelog + + @pytest.fixture def example_project_git_repo( example_project_dir: ExProjectDir, From 4f30dabbb44695dbaadcec2ff87b26dd2e4bd9a6 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Mon, 1 Jan 2024 18:26:27 -0500 Subject: [PATCH 02/22] test(cli-changelog): refactor changelog re-gen test to show debuggable results --- tests/command_line/test_changelog.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 0f7a4de05..d044159df 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -114,14 +114,10 @@ def test_changelog_noop_is_noop( ) def test_changelog_content_regenerated( repo: Repo, - tmp_path_factory: pytest.TempPathFactory, - example_project_dir: ExProjectDir, example_changelog_md: Path, cli_runner: CliRunner, ): - tempdir = tmp_path_factory.mktemp("test_changelog") - remove_dir_tree(tempdir.resolve(), force=True) - shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) + expected_changelog_content = example_changelog_md.read_text() # Remove the changelog and then check that we can regenerate it os.remove(str(example_changelog_md.resolve())) @@ -129,10 +125,10 @@ def test_changelog_content_regenerated( result = cli_runner.invoke(main, [changelog.name or "changelog"]) assert result.exit_code == 0 - dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) + actual_content = example_changelog_md.read_text() - differing_files = flatten_dircmp(dcmp) - assert not differing_files + # Check that the changelog content is the same as before + assert expected_changelog_content == actual_content # Just need to test that it works for "a" project, not all @@ -189,12 +185,12 @@ def test_changelog_post_to_release( ) as mocker, monkeypatch.context() as m: m.delenv("GITHUB_REPOSITORY", raising=False) m.delenv("CI_PROJECT_NAMESPACE", raising=False) - result = cli_runner.invoke(main, [changelog.name, *args]) + result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) assert result.exit_code == 0 assert mocker.called - assert mock_adapter.called + assert mock_adapter.called and mock_adapter.last_request is not None assert mock_adapter.last_request.url == ( "https://{api_url}/repos/{owner}/{repo_name}/releases".format( api_url=Github.DEFAULT_API_DOMAIN, @@ -220,11 +216,15 @@ def test_custom_release_notes_template( # Arrange release_history = get_release_history_from_context(runtime_context_with_tags) tag = runtime_context_with_tags.repo.tags[-1].name + version = runtime_context_with_tags.version_translator.from_tag(tag) + if version is None: + raise ValueError(f"Tag {tag} not in release history") + release = release_history.released[version] # Act - resp = cli_runner.invoke(main, [changelog.name, "--post-to-release-tag", tag]) + resp = cli_runner.invoke(main, [changelog.name or "changelog", "--post-to-release-tag", tag]) expected_release_notes = runtime_context_with_tags.template_environment.from_string( EXAMPLE_RELEASE_NOTES_TEMPLATE ).render(version=version, release=release) @@ -235,5 +235,5 @@ def test_custom_release_notes_template( f"'semantic-release {changelog.name} --post-to-release-tag {tag}': " + resp.stderr ) - assert post_mocker.call_count == 1 + assert post_mocker.call_count == 1 and post_mocker.last_request is not None assert expected_release_notes == post_mocker.last_request.json()["body"] From 9349a4f44ad2931cc1e875e92a1abf2d6457a641 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 7 Jan 2024 00:23:36 -0500 Subject: [PATCH 03/22] test(changelog): add assertion to check for changelog file exist before read --- tests/command_line/test_changelog.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index d044159df..c7cfbf2dd 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -125,6 +125,9 @@ def test_changelog_content_regenerated( result = cli_runner.invoke(main, [changelog.name or "changelog"]) assert result.exit_code == 0 + # Check that the changelog file was re-created + assert example_changelog_md.exists() + actual_content = example_changelog_md.read_text() # Check that the changelog content is the same as before From 2e0ba5513df12d0b5861e8a3e4d7f9b343544f27 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 2 Jan 2024 01:02:03 -0500 Subject: [PATCH 04/22] test: improve reliability & error readability of assertions --- tests/command_line/test_version.py | 15 ++++++----- tests/scenario/test_release_history.py | 36 ++++++++++++++------------ 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index ba2fbeaed..d8e0d5c8d 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -423,13 +423,13 @@ def test_version_no_push_force_level( assert head_before in repo.head.commit.parents dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) - differing_files = flatten_dircmp(dcmp) + differing_files = sorted(flatten_dircmp(dcmp)) # Changelog already reflects changes this should introduce - assert differing_files == [ + assert differing_files == sorted([ "pyproject.toml", f"src/{EXAMPLE_PROJECT_NAME}/_version.py", - ] + ]) # Compare pyproject.toml new_pyproject_toml = tomlkit.loads( @@ -699,13 +699,16 @@ def test_version_only_update_files_no_git_actions( ) dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) - differing_files = flatten_dircmp(dcmp) + differing_files = sorted(flatten_dircmp(dcmp)) # Files that should receive version change - assert differing_files == [ + expected_changed_files = sorted([ + # CHANGELOG.md is not included as no modification to Git History + # (no commit or tag) has been made "pyproject.toml", f"src/{EXAMPLE_PROJECT_NAME}/_version.py", - ] + ]) + assert expected_changed_files == differing_files # Compare pyproject.toml new_pyproject_toml = tomlkit.loads( diff --git a/tests/scenario/test_release_history.py b/tests/scenario/test_release_history.py index f71cb7396..42c1e2f18 100644 --- a/tests/scenario/test_release_history.py +++ b/tests/scenario/test_release_history.py @@ -233,15 +233,15 @@ def test_release_history( ), ) for k in expected_release_history.released: - expected, actual = expected_release_history.released[k], released[k]["elements"] - actual_released_messages = [ - res.commit.message for results in actual.values() for res in results - ] - assert all( - msg in actual_released_messages - for bucket in expected.values() - for msg in bucket - ) + expected = expected_release_history.released[k] + actual = released[k]["elements"] + actual_released_messages = str.join("\n\n", sorted([ + str(res.commit.message) for results in actual.values() for res in results + ])) + expected_released_messages = str.join("\n\n", sorted([ + msg for bucket in expected.values() for msg in bucket + ])) + assert expected_released_messages == actual_released_messages for commit_message in ANGULAR_COMMITS_MINOR: add_text_to_file(repo, file_in_repo) @@ -251,17 +251,21 @@ def test_release_history( new_unreleased, new_released = ReleaseHistory.from_git_history( repo, translator, default_angular_parser ) - actual_unreleased_messages = [ - res.commit.message for results in new_unreleased.values() for res in results - ] - assert all( - msg in actual_unreleased_messages + + actual_unreleased_messages = str.join("\n\n", sorted([ + str(res.commit.message) for results in new_unreleased.values() for res in results + ])) + + expected_unreleased_messages = str.join("\n\n", sorted([ + msg for bucket in [ + ANGULAR_COMMITS_MINOR[::-1], *expected_release_history.unreleased.values(), - ANGULAR_COMMITS_MINOR, ] for msg in bucket - ) + ])) + + assert expected_unreleased_messages == actual_unreleased_messages assert ( new_released == released ), "something that shouldn't be considered release has been released" From 6cc0704525444baa349bdc791ab9f3fbba220235 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 2 Jan 2024 01:02:55 -0500 Subject: [PATCH 05/22] test(cli-version): ensure CHANGELOG is included in changed files --- tests/command_line/test_version.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index d8e0d5c8d..824511178 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -427,6 +427,7 @@ def test_version_no_push_force_level( # Changelog already reflects changes this should introduce assert differing_files == sorted([ + "CHANGELOG.md", "pyproject.toml", f"src/{EXAMPLE_PROJECT_NAME}/_version.py", ]) From b262d3ba79217a5226fb9f5f1b932178bb3ee411 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 23:53:46 -0500 Subject: [PATCH 06/22] test(fixtures): remove changelog generation prevention --- tests/fixtures/example_project.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 6d79ba402..7cc28872d 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -107,7 +107,6 @@ def cached_example_project( setup_py_file: Path, changelog_md_file: Path, cached_files_dir: Path, - changelog_template_dir: Path, teardown_cached_dir: TeardownCachedDirFn, ) -> Path: """ @@ -158,11 +157,6 @@ def hello_world() -> None: # write file contents abs_filepath.write_text(contents) - # create the changelog template directory - cached_project_path.joinpath(changelog_template_dir).mkdir( - parents=True, exist_ok=True - ) - # trigger automatic cleanup of cache directory during teardown return teardown_cached_dir(cached_project_path) From 34ddf60d7255e793b382ce3524dd632568342e45 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 16 Jan 2024 23:56:13 -0500 Subject: [PATCH 07/22] test(fixtures): trigger changelog generation in repo fixtures --- tests/fixtures/git_repo.py | 12 ++++-------- .../repos/git_flow/repo_w_2_release_channels.py | 9 +++++++++ .../repos/git_flow/repo_w_3_release_channels.py | 9 +++++++++ .../repos/github_flow/repo_w_release_channels.py | 9 +++++++++ .../fixtures/repos/trunk_based_dev/repo_w_no_tags.py | 9 +++++++++ .../repos/trunk_based_dev/repo_w_prereleases.py | 9 +++++++++ tests/fixtures/repos/trunk_based_dev/repo_w_tags.py | 9 +++++++++ 7 files changed, 58 insertions(+), 8 deletions(-) diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index e06a4ea13..208a63e86 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -109,7 +109,6 @@ def __call__( self, repo_definition: RepoDefinition, dest_file: Path | None = None, - tag_format: str = ... ) -> str: ... @@ -325,17 +324,15 @@ def _build_configured_base_repo( @pytest.fixture(scope="session") -def simulate_default_changelog_creation( - default_tag_format_str: str, -) -> SimulateDefaultChangelogCreationFn: - def build_version_entry(version: VersionStr, version_def: RepoVersionDef, tag_format: str) -> str: +def simulate_default_changelog_creation() -> SimulateDefaultChangelogCreationFn: + def build_version_entry(version: VersionStr, version_def: RepoVersionDef) -> str: version_entry = [] if version == "Unreleased": version_entry.append(f"## {version}\n") else: version_entry.append( # TODO: artificial newline in front due to template when no Unreleased changes exist - f"\n## {tag_format.format(version=version)} ({TODAY_DATE_STR})\n" + f"\n## v{version} ({TODAY_DATE_STR})\n" ) for section_def in version_def["changelog_sections"]: @@ -348,7 +345,6 @@ def build_version_entry(version: VersionStr, version_def: RepoVersionDef, tag_fo def _mimic_semantic_release_default_changelog( repo_definition: RepoDefinition, dest_file: Path | None = None, - tag_format: str = default_tag_format_str, ) -> str: header = "# CHANGELOG" version_entries = [] @@ -356,7 +352,7 @@ def _mimic_semantic_release_default_changelog( for version, version_def in repo_definition.items(): # prepend entries to force reverse ordering version_entries.insert( - 0, build_version_entry(version, version_def, tag_format) + 0, build_version_entry(version, version_def) ) changelog_content = str.join("\n" * 3, [ diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 29acb7c9e..1f78d7f4d 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -223,7 +224,9 @@ def build_git_flow_repo_with_2_release_channels( get_commits_for_git_flow_repo_with_2_release_channels: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: """ @@ -378,6 +381,12 @@ def _build_git_flow_repo_with_2_release_channels( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a 2nd alpha prerelease (v1.2.0-alpha.2) on the feature branch create_release_tagged_commit(git_repo, next_version, tag_format) diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index e333b0615..5744b3d7d 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -218,7 +219,9 @@ def build_git_flow_repo_w_3_release_channels( get_commits_for_git_flow_repo_w_3_release_channels: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_git_flow_repo_w_3_release_channels( @@ -373,6 +376,12 @@ def _build_git_flow_repo_w_3_release_channels( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a 3rd alpha prerelease (v1.1.0-alpha.3) create_release_tagged_commit(git_repo, next_version, tag_format) diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 0b2c2cd97..2b696c3f8 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -156,7 +157,9 @@ def build_github_flow_repo_w_feature_release_channel( get_commits_for_github_flow_repo_w_feature_release_channel: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_github_flow_repo_w_feature_release_channel( @@ -255,6 +258,12 @@ def _build_github_flow_repo_w_feature_release_channel( git_repo, next_version_def["commits"], hvcs ) + # Write the expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a feature level beta release (v0.3.0-beta.1) create_release_tagged_commit(git_repo, next_version, tag_format) diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 56782f616..5ee445a1d 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -25,6 +25,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -125,7 +126,9 @@ def _get_versions_for_trunk_only_repo_w_no_tags() -> list[VersionStr]: def build_trunk_only_repo_w_no_tags( get_commits_for_trunk_only_repo_w_no_tags: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_no_tags( dest_dir: Path | str, @@ -155,6 +158,12 @@ def _build_trunk_only_repo_w_no_tags( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + return repo_dir, hvcs return _build_trunk_only_repo_w_no_tags diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index ef000809f..496f15940 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -138,7 +139,9 @@ def build_trunk_only_repo_w_prerelease_tags( get_commits_for_trunk_only_repo_w_prerelease_tags: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_prerelease_tags( @@ -210,6 +213,12 @@ def _build_trunk_only_repo_w_prerelease_tags( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a full release create_release_tagged_commit(git_repo, next_version, tag_format) diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 6d4ba14ee..4b933bd41 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -26,6 +26,7 @@ GetVersionStringsFn, RepoDefinition, SimulateChangeCommitsNReturnChangelogEntryFn, + SimulateDefaultChangelogCreationFn, TomlSerializableTypes, VersionStr, ) @@ -106,7 +107,9 @@ def build_trunk_only_repo_w_tags( get_commits_for_trunk_only_repo_w_tags: GetRepoDefinitionFn, build_configured_base_repo: BuildRepoFn, default_tag_format_str: str, + changelog_md_file: Path, simulate_change_commits_n_rtn_changelog_entry: SimulateChangeCommitsNReturnChangelogEntryFn, + simulate_default_changelog_creation: SimulateDefaultChangelogCreationFn, create_release_tagged_commit: CreateReleaseFn, ) -> BuildRepoFn: def _build_trunk_only_repo_w_tags( @@ -155,6 +158,12 @@ def _build_trunk_only_repo_w_tags( git_repo, next_version_def["commits"], hvcs ) + # write expected changelog (should match template changelog) + simulate_default_changelog_creation( + repo_def, + repo_dir.joinpath(changelog_md_file), + ) + # Make a patch level release (v0.1.1) create_release_tagged_commit(git_repo, next_version, tag_format) From 615c4b01890319b42bfe7d4c13aa1c3699cafc62 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Tue, 2 Jan 2024 00:59:24 -0500 Subject: [PATCH 08/22] test(repo-commits): fix angular syntax for scopes in commits --- .../git_flow/repo_w_2_release_channels.py | 10 ++++----- .../git_flow/repo_w_3_release_channels.py | 16 +++++++------- .../github_flow/repo_w_release_channels.py | 2 +- tests/scenario/test_release_history.py | 22 +++++++++---------- .../changelog/test_default_changelog.py | 10 ++++----- .../changelog/test_release_notes.py | 2 +- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 1f78d7f4d..4e03ebda4 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -108,7 +108,7 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition }, "commits": [ { - "angular": "feat: (dev) add some more text", + "angular": "feat(dev): add some more text", "emoji": ":sparkles: (dev) add some more text", "scipy": "ENH: (dev) add some more text", "tag": ":sparkles: (dev) add some more text", @@ -124,7 +124,7 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition }, "commits": [ { - "angular": "fix: (dev) add some more text", + "angular": "fix(dev): add some more text", "emoji": ":bug: (dev) add some more text", "scipy": "MAINT: (dev) add some more text", "tag": ":nut_and_bolt: (dev) add some more text", @@ -140,7 +140,7 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", @@ -171,13 +171,13 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", }, { - "angular": "fix: (feature) add some missing text", + "angular": "fix(feature): add some missing text", "emoji": ":bug: (feature) add some missing text", "scipy": "MAINT: (feature) add some missing text", "tag": ":nut_and_bolt: (feature) add some missing text", diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index 5744b3d7d..a10b57de3 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -108,7 +108,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "feat: (dev) add some more text", + "angular": "feat(dev): add some more text", "emoji": ":sparkles: (dev) add some more text", "scipy": "ENH: (dev) add some more text", "tag": ":sparkles: (dev) add some more text", @@ -124,7 +124,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "fix: (dev) add some more text", + "angular": "fix(dev): add some more text", "emoji": ":bug: (dev) add some more text", "scipy": "MAINT: (dev) add some more text", "tag": ":nut_and_bolt: (dev) add some more text", @@ -140,7 +140,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", @@ -156,7 +156,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", @@ -172,10 +172,10 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: }, "commits": [ { - "angular": "fix: (feature) add some more text", - "emoji": ":bug: (feature) add some more text", - "scipy": "MAINT: (feature) add some more text", - "tag": ":nut_and_bolt: (feature) add some more text", + "angular": "fix(feature): add some missing text", + "emoji": ":bug: (feature) add some missing text", + "scipy": "MAINT: (feature) add some missing text", + "tag": ":nut_and_bolt: (feature) add some missing text", }, ], }, diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 2b696c3f8..5c84162c7 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -108,7 +108,7 @@ def get_commits_for_github_flow_repo_w_feature_release_channel() -> GetRepoDefin }, "commits": [ { - "angular": "feat: (feature) add some more text", + "angular": "feat(feature): add some more text", "emoji": ":sparkles: (feature) add some more text", "scipy": "ENH: (feature) add some more text", "tag": ":sparkles: (feature) add some more text", diff --git a/tests/scenario/test_release_history.py b/tests/scenario/test_release_history.py index 42c1e2f18..ce73e8ffb 100644 --- a/tests/scenario/test_release_history.py +++ b/tests/scenario/test_release_history.py @@ -93,7 +93,7 @@ class FakeReleaseHistoryElements(NamedTuple): "unknown": [COMMIT_MESSAGE.format(version="0.2.0")], }, Version.parse("0.3.0-beta.1"): { - "feature": ["feat: (feature) add some more text\n"], + "feature": ["feat(feature): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="0.3.0-beta.1")], }, }, @@ -119,20 +119,20 @@ class FakeReleaseHistoryElements(NamedTuple): "unknown": [COMMIT_MESSAGE.format(version="1.0.0")], }, Version.parse("1.1.0"): { - "feature": ["feat: (dev) add some more text\n"], + "feature": ["feat(dev): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0")], }, Version.parse("1.1.1"): { - "fix": ["fix: (dev) add some more text\n"], + "fix": ["fix(dev): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.1")], }, Version.parse("1.2.0-alpha.1"): { - "feature": ["feat: (feature) add some more text\n"], + "feature": ["feat(feature): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.2.0-alpha.1")], }, Version.parse("1.2.0-alpha.2"): { - "feature": ["feat: (feature) add some more text\n"], - "fix": ["fix: (feature) add some missing text\n"], + "feature": ["feat(feature): add some more text\n"], + "fix": ["fix(feature): add some missing text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.2.0-alpha.2")], }, }, @@ -158,23 +158,23 @@ class FakeReleaseHistoryElements(NamedTuple): "unknown": [COMMIT_MESSAGE.format(version="1.0.0")], }, Version.parse("1.1.0-rc.1"): { - "feature": ["feat: (dev) add some more text\n"], + "feature": ["feat(dev): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-rc.1")], }, Version.parse("1.1.0-rc.2"): { - "fix": ["fix: (dev) add some more text\n"], + "fix": ["fix(dev): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-rc.2")], }, Version.parse("1.1.0-alpha.1"): { - "feature": ["feat: (feature) add some more text\n"], + "feature": ["feat(feature): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-alpha.1")], }, Version.parse("1.1.0-alpha.2"): { - "feature": ["feat: (feature) add some more text\n"], + "feature": ["feat(feature): add some more text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-alpha.2")], }, Version.parse("1.1.0-alpha.3"): { - "fix": ["fix: (feature) add some more text\n"], + "fix": ["fix(feature): add some missing text\n"], "unknown": [COMMIT_MESSAGE.format(version="1.1.0-alpha.3")], }, }, diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index e79fe323b..6867ac43d 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -28,27 +28,27 @@ def _cm_rstripped(version: str) -> str: # CHANGELOG ## v1.1.0-alpha.3 ({today_as_str}) ### Fix -* fix: (feature) add some more text +* fix(feature): add some missing text ### Unknown * {_cm_rstripped("1.1.0-alpha.3")} ## v1.1.0-alpha.2 ({today_as_str}) ### Feature -* feat: (feature) add some more text +* feat(feature): add some more text ### Unknown * {_cm_rstripped("1.1.0-alpha.2")} ## v1.1.0-alpha.1 ({today_as_str}) ### Feature -* feat: (feature) add some more text +* feat(feature): add some more text ### Unknown * {_cm_rstripped("1.1.0-alpha.1")} ## v1.1.0-rc.2 ({today_as_str}) ### Fix -* fix: (dev) add some more text +* fix(dev): add some more text ### Unknown * {_cm_rstripped("1.1.0-rc.2")} ## v1.1.0-rc.1 ({today_as_str}) ### Feature -* feat: (dev) add some more text +* feat(dev): add some more text ### Unknown * {_cm_rstripped("1.1.0-rc.1")} ## v1.0.0 ({today_as_str}) diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index 441522612..f7c67be64 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -27,7 +27,7 @@ def _cm_rstripped(version: str) -> str: EXPECTED_CONTENT = f"""\ # v1.1.0-alpha.3 ({today_as_str}) ## Fix -* fix: (feature) add some more text +* fix(feature): add some missing text ## Unknown * {_cm_rstripped("1.1.0-alpha.3")} """ From 4b42df3824912d95ff35e9ed13bdaaa5acaad718 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 Feb 2024 09:56:08 -0500 Subject: [PATCH 09/22] test(unit-changelog): drop context test & duplicate/incorrect template Drop the test related changelog template as that is one more thing to maintain and does not actually match the embedded template provided to the user. Secondly, drop the context unit test as it does not provide value that the template ultimately will fail in other unit tests. --- .../changelog/TEST_CHANGELOG.md.j2 | 24 --- .../changelog/test_changelog_context.py | 144 ------------------ 2 files changed, 168 deletions(-) delete mode 100644 tests/unit/semantic_release/changelog/TEST_CHANGELOG.md.j2 delete mode 100644 tests/unit/semantic_release/changelog/test_changelog_context.py diff --git a/tests/unit/semantic_release/changelog/TEST_CHANGELOG.md.j2 b/tests/unit/semantic_release/changelog/TEST_CHANGELOG.md.j2 deleted file mode 100644 index e28faed21..000000000 --- a/tests/unit/semantic_release/changelog/TEST_CHANGELOG.md.j2 +++ /dev/null @@ -1,24 +0,0 @@ -{# - NOTE: this changelog test doesn't include commit hashes from the default template as - they always change - which makes it notoriously difficult to check exact content -#}# CHANGELOG -{% if context.history.unreleased | length > 0 %} -## Unreleased -{% for type_, commits in context.history.unreleased | dictsort %} -### {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} -{% else %} -* {{ commit.message.rstrip() }} -{% endif %}{% endfor %} -{% endfor %}{% endif %} -{% for version, release in context.history.released.items() %} -## {{ version.as_semver_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) -{% for type_, commits in release["elements"] | dictsort %} -### {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} -{% else %} -* {{ commit.message.rstrip() }} -{% endif %}{% endfor %} -{% endfor %}{% endfor %} diff --git a/tests/unit/semantic_release/changelog/test_changelog_context.py b/tests/unit/semantic_release/changelog/test_changelog_context.py deleted file mode 100644 index dcb62124c..000000000 --- a/tests/unit/semantic_release/changelog/test_changelog_context.py +++ /dev/null @@ -1,144 +0,0 @@ -import pytest -from git.objects.base import Object -from pytest_lazyfixture import lazy_fixture - -from semantic_release.changelog import environment, make_changelog_context -from semantic_release.changelog.release_history import ReleaseHistory -from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab -from semantic_release.version.translator import VersionTranslator - -NULL_HEX_SHA = Object.NULL_HEX_SHA -SHORT_SHA = NULL_HEX_SHA[:7] - - -# Test with just one project for the moment - can be expanded to all -# example projects later - -CHANGELOG_TEMPLATE = r""" -# CHANGELOG -{% if context.history.unreleased | length > 0 %} -## Unreleased -{% for type_, commits in context.history.unreleased.items() %} -### {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) -{% else %} -* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) -{% endif %}{% endfor %}{% endfor %}{% endif %} -{% for version, release in context.history.released.items() %} -## {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) -{% for type_, commits in release["elements"].items() %} -### {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) -{% else %} -* {{ commit.message.rstrip() }} ([`{{ commit.short_hash }}`]({{ commit.hexsha | commit_hash_url }})) -{% endif %}{% endfor %}{% endfor %}{% endfor %} -""" # noqa: E501 - -EXPECTED_CHANGELOG_CONTENT_ANGULAR = r""" -# CHANGELOG -## v0.2.0 -### Feature -* feat: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.2.0-rc.1 -### Feature -* feat: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.1-rc.1 -### Fix -* fix: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.0 -### Unknown -* Initial commit ([`{SHORT_SHA}`]({commit_url})) -""" - - -EXPECTED_CHANGELOG_CONTENT_EMOJI = r""" -# CHANGELOG -## v0.2.0 -### :sparkles: -* :sparkles: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.2.0-rc.1 -### :sparkles: -* :sparkles: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.1-rc.1 -### :bug: -* :bug: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.0 -### Unknown -* Initial commit ([`{SHORT_SHA}`]({commit_url})) -""" - -EXPECTED_CHANGELOG_CONTENT_SCIPY = r""" -# CHANGELOG -## v0.2.0 -### ENH: -* ENH: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.2.0-rc.1 -### ENH: -* ENH: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.1-rc.1 -### MAINT: -* MAINT: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.0 -### Unknown -* Initial commit ([`{SHORT_SHA}`]({commit_url})) -""" - -EXPECTED_CHANGELOG_CONTENT_TAG = r""" -# CHANGELOG -## v0.2.0 -### :sparkles: -* :sparkles: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.2.0-rc.1 -### :sparkles: -* :sparkles: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.1-rc.1 -### :nut_and_bolt: -* :nut_and_bolt: add some more text ([`{SHORT_SHA}`]({commit_url})) -## v0.1.0 -### Unknown -* Initial commit ([`{SHORT_SHA}`]({commit_url})) -""" - - -@pytest.mark.parametrize("changelog_template", (CHANGELOG_TEMPLATE,)) -@pytest.mark.parametrize( - "repo, commit_parser, expected_changelog", - [ - ( - lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("default_angular_parser"), - EXPECTED_CHANGELOG_CONTENT_ANGULAR, - ), - ( - lazy_fixture("repo_with_single_branch_and_prereleases_emoji_commits"), - lazy_fixture("default_emoji_parser"), - EXPECTED_CHANGELOG_CONTENT_EMOJI, - ), - ( - lazy_fixture("repo_with_single_branch_and_prereleases_scipy_commits"), - lazy_fixture("default_scipy_parser"), - EXPECTED_CHANGELOG_CONTENT_SCIPY, - ), - ( - lazy_fixture("repo_with_single_branch_and_prereleases_tag_commits"), - lazy_fixture("default_tag_parser"), - EXPECTED_CHANGELOG_CONTENT_TAG, - ), - ], -) -@pytest.mark.parametrize("hvcs_client_class", (Github, Gitlab, Gitea, Bitbucket)) -@pytest.mark.usefixtures("expected_changelog") -def test_changelog_context(repo, changelog_template, commit_parser, hvcs_client_class): - # NOTE: this test only checks that the changelog can be rendered with the - # contextual information we claim to offer. Testing that templates render - # appropriately is the responsibility of the template engine's authors, - # so we shouldn't be re-testing that here. - hvcs_client = hvcs_client_class(remote_url=repo.remote().url) - env = environment(lstrip_blocks=True, keep_trailing_newline=True, trim_blocks=True) - rh = ReleaseHistory.from_git_history(repo, VersionTranslator(), commit_parser) - context = make_changelog_context(hvcs_client=hvcs_client, release_history=rh) - context.bind_to_environment(env) - actual_content = env.from_string(changelog_template).render() - assert actual_content From ca8b08659dae369ac4bc39618d5523f1b7bb535d Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 Feb 2024 11:05:46 -0500 Subject: [PATCH 10/22] test(unit-release-notes): refactor template testing Drop the test related release notes template as that is one more thing to maintain and use the one provided to the user. --- .../changelog/test_release_notes.md.j2 | 8 -- .../changelog/test_release_notes.py | 118 +++++++++++++----- 2 files changed, 86 insertions(+), 40 deletions(-) delete mode 100644 tests/unit/semantic_release/changelog/test_release_notes.md.j2 diff --git a/tests/unit/semantic_release/changelog/test_release_notes.md.j2 b/tests/unit/semantic_release/changelog/test_release_notes.md.j2 deleted file mode 100644 index 088337c84..000000000 --- a/tests/unit/semantic_release/changelog/test_release_notes.md.j2 +++ /dev/null @@ -1,8 +0,0 @@ -# {{ version.as_tag() }} ({{ release.tagged_date.strftime("%Y-%m-%d") }}) -{% for type_, commits in release["elements"] | dictsort %} -## {{ type_ | capitalize }} -{% for commit in commits %}{% if type_ != "unknown" %} -* {{ commit.message.rstrip() }} -{% else %} -* {{ commit.message.rstrip() }} -{% endif %}{% endfor %}{% endfor %} diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index f7c67be64..cd2adf0e9 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -1,53 +1,107 @@ -# NOTE: use backport with newer API +from __future__ import annotations + from datetime import datetime +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest +from git import Commit, Repo +from git.objects import Object +# NOTE: use backport with newer API to support 3.7 from importlib_resources import files +import semantic_release from semantic_release.changelog.context import make_changelog_context -from semantic_release.changelog.release_history import ReleaseHistory +from semantic_release.changelog.release_history import Release, ReleaseHistory from semantic_release.changelog.template import environment -from semantic_release.hvcs import Github -from semantic_release.version import Version, VersionTranslator +from semantic_release.commit_parser import ParsedCommit +from semantic_release.enums import LevelBump +from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.version import Version -from tests.const import COMMIT_MESSAGE +from tests.const import TODAY_DATE_STR -default_release_notes_template = ( - files("tests") - .joinpath("unit/semantic_release/changelog/test_release_notes.md.j2") - .read_text(encoding="utf-8") -) +if TYPE_CHECKING: + from git import Actor -today_as_str = datetime.now().strftime("%Y-%m-%d") + from semantic_release.hvcs import HvcsBase -def _cm_rstripped(version: str) -> str: - return COMMIT_MESSAGE.format(version=version).rstrip() +@pytest.fixture +def artificial_release_history(commit_author: Actor): + version = Version.parse("1.1.0-alpha.3") + commit_subject = "fix(cli): fix a problem" + fix_commit = Commit( + Repo("."), + Object.NULL_HEX_SHA[:20].encode("utf-8"), + message=commit_subject, + ) -EXPECTED_CONTENT = f"""\ -# v1.1.0-alpha.3 ({today_as_str}) -## Fix -* fix(feature): add some missing text -## Unknown -* {_cm_rstripped("1.1.0-alpha.3")} -""" + fix_commit_parsed = ParsedCommit( + bump=LevelBump.PATCH, + type="fix", + scope="cli", + descriptions=[commit_subject], + breaking_descriptions=[], + commit=fix_commit, + ) + + return ReleaseHistory( + unreleased={}, + released={ + version: Release( + tagger=commit_author, + committer=commit_author, + tagged_date=datetime.utcnow(), + elements={ + "fix": [fix_commit_parsed], + } + ) + } + ) -def test_default_changelog_template( - repo_with_git_flow_and_release_channels_angular_commits, default_angular_parser +@pytest.fixture +def release_notes_template() -> str: + """Retrieve the semantic-release default release notes template.""" + version_notes_template = files(semantic_release.__name__).joinpath( + Path("data", "templates", "release_notes.md.j2") + ) + return version_notes_template.read_text(encoding="utf-8") + + +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +def test_default_release_notes_template( + example_git_https_url: str, + hvcs_client: type[HvcsBase], + release_notes_template: str, + artificial_release_history: ReleaseHistory, ): - version = Version.parse("1.1.0-alpha.3") - repo = repo_with_git_flow_and_release_channels_angular_commits + """ + Unit test goal: just make sure it renders the release notes template without error. + + Scenarios are better suited for all the variations (commit types). + """ + version_str = "1.1.0-alpha.3" + version = Version.parse(version_str) + commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] + commit_url = hvcs_client(example_git_https_url).commit_hash_url(commit_obj.commit.hexsha) + commit_description = str.join('\n', commit_obj.descriptions) + expected_content = str.join("\n", [ + f"# v{version_str} ({TODAY_DATE_STR})", + "## Fix", + f"* {commit_description} ([`{commit_obj.commit.hexsha[:7]}`]({commit_url}))", + "", + ]) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) - rh = ReleaseHistory.from_git_history( - repo=repo, translator=VersionTranslator(), commit_parser=default_angular_parser - ) context = make_changelog_context( - hvcs_client=Github(remote_url=repo.remote().url), release_history=rh + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release_history=artificial_release_history, ) context.bind_to_environment(env) - release = rh.released[version] - actual_content = env.from_string(default_release_notes_template).render( - version=version, release=release + actual_content = env.from_string(release_notes_template).render( + version=version, release=context.history.released[version] ) - assert actual_content == EXPECTED_CONTENT + assert expected_content == actual_content From 475a3e0e65d702e7e79f0552dd6c33bb901d03e5 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 4 Feb 2024 23:23:03 -0500 Subject: [PATCH 11/22] test(unit-changelog): refactor template testing to be fast & simple --- tests/command_line/test_changelog.py | 1 + .../changelog/test_default_changelog.py | 238 +++++++++++------- 2 files changed, 151 insertions(+), 88 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index c7cfbf2dd..bcff9d2ae 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -110,6 +110,7 @@ def test_changelog_noop_is_noop( lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), lazy_fixture("repo_with_git_flow_angular_commits"), lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), + lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits_using_tag_format"), ], ) def test_changelog_content_regenerated( diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index 6867ac43d..cc138b0de 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -1,109 +1,171 @@ -# NOTE: use backport with newer API +from __future__ import annotations + from datetime import datetime +from pathlib import Path +from typing import TYPE_CHECKING + +import pytest +from git import Commit, Object, Repo +# NOTE: use backport with newer API from importlib_resources import files +import semantic_release from semantic_release.changelog.context import make_changelog_context -from semantic_release.changelog.release_history import ReleaseHistory +from semantic_release.changelog.release_history import Release, ReleaseHistory from semantic_release.changelog.template import environment -from semantic_release.hvcs import Github -from semantic_release.version.translator import VersionTranslator - -from tests.const import COMMIT_MESSAGE - -default_changelog_template = ( - files("tests") - .joinpath("unit/semantic_release/changelog/TEST_CHANGELOG.md.j2") - .read_text(encoding="utf-8") -) - -today_as_str = datetime.now().strftime("%Y-%m-%d") - - -def _cm_rstripped(version: str) -> str: - return COMMIT_MESSAGE.format(version=version).rstrip() - - -EXPECTED_CONTENT = f"""\ -# CHANGELOG -## v1.1.0-alpha.3 ({today_as_str}) -### Fix -* fix(feature): add some missing text -### Unknown -* {_cm_rstripped("1.1.0-alpha.3")} -## v1.1.0-alpha.2 ({today_as_str}) -### Feature -* feat(feature): add some more text -### Unknown -* {_cm_rstripped("1.1.0-alpha.2")} -## v1.1.0-alpha.1 ({today_as_str}) -### Feature -* feat(feature): add some more text -### Unknown -* {_cm_rstripped("1.1.0-alpha.1")} -## v1.1.0-rc.2 ({today_as_str}) -### Fix -* fix(dev): add some more text -### Unknown -* {_cm_rstripped("1.1.0-rc.2")} -## v1.1.0-rc.1 ({today_as_str}) -### Feature -* feat(dev): add some more text -### Unknown -* {_cm_rstripped("1.1.0-rc.1")} -## v1.0.0 ({today_as_str}) -### Feature -* feat: add some more text -### Unknown -* {_cm_rstripped("1.0.0")} -## v1.0.0-rc.1 ({today_as_str}) -### Breaking -* feat!: add some more text -### Unknown -* {_cm_rstripped("1.0.0-rc.1")} -## v0.1.1-rc.1 ({today_as_str}) -### Fix -* fix: add some more text -### Unknown -* {_cm_rstripped("0.1.1-rc.1")} -## v0.1.0 ({today_as_str}) -### Unknown -* {_cm_rstripped("0.1.0")} -* Initial commit -""" +from semantic_release.commit_parser import ParsedCommit +from semantic_release.enums import LevelBump +from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.version.translator import Version + +from tests.const import TODAY_DATE_STR + +if TYPE_CHECKING: + from git import Actor + + from semantic_release.hvcs import HvcsBase + + +@pytest.fixture +def default_changelog_template() -> str: + """Retrieve the semantic-release default changelog template.""" + version_notes_template = files(semantic_release.__name__).joinpath( + Path("data", "templates", "CHANGELOG.md.j2") + ) + return version_notes_template.read_text(encoding="utf-8") + + +@pytest.fixture +def artificial_release_history(commit_author: Actor): + version = Version.parse("1.0.0") + + commit_subject = "fix(cli): fix a problem" + + fix_commit = Commit( + Repo("."), + Object.NULL_HEX_SHA[:20].encode("utf-8"), + message=commit_subject, + ) + + fix_commit_parsed = ParsedCommit( + bump=LevelBump.PATCH, + type="fix", + scope="cli", + descriptions=[commit_subject], + breaking_descriptions=[], + commit=fix_commit, + ) + commit_subject = "feat(cli): add a new feature" + feat_commit = Commit( + Repo("."), + Object.NULL_HEX_SHA[:20].encode("utf-8"), + message=commit_subject, + ) + + feat_commit_parsed = ParsedCommit( + bump=LevelBump.MINOR, + type="feat", + scope="cli", + descriptions=[commit_subject], + breaking_descriptions=[], + commit=feat_commit, + ) + + return ReleaseHistory( + unreleased={ + "feature": [feat_commit_parsed], + }, + released={ + version: Release( + tagger=commit_author, + committer=commit_author, + tagged_date=datetime.utcnow(), + elements={ + "feature": [feat_commit_parsed], + "fix": [fix_commit_parsed], + } + ) + } + ) + + +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) def test_default_changelog_template( - repo_with_git_flow_and_release_channels_angular_commits, default_angular_parser + default_changelog_template: str, + hvcs_client: type[HvcsBase], + example_git_https_url: str, + artificial_release_history: ReleaseHistory, ): - repo = repo_with_git_flow_and_release_channels_angular_commits + version_str = "1.0.0" + version = Version.parse(version_str) + rh = artificial_release_history + rh.unreleased = {} # Wipe out unreleased + + feat_commit_obj = artificial_release_history.released[version]["elements"]["feature"][0] + feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url(feat_commit_obj.commit.hexsha) + feat_description = str.join('\n', feat_commit_obj.descriptions) + + fix_commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] + fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url(fix_commit_obj.commit.hexsha) + fix_description = str.join('\n', fix_commit_obj.descriptions) + + expected_changelog = str.join("\n", [ + "# CHANGELOG", + f"## v{version_str} ({TODAY_DATE_STR})", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "### Fix", + f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", + "", + ]) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) - rh = ReleaseHistory.from_git_history( - repo=repo, translator=VersionTranslator(), commit_parser=default_angular_parser - ) context = make_changelog_context( - hvcs_client=Github(remote_url=repo.remote().url), release_history=rh + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release_history=rh, ) context.bind_to_environment(env) - actual_content = env.from_string(default_changelog_template).render() - assert actual_content == EXPECTED_CONTENT + actual_changelog = env.from_string(default_changelog_template).render() + assert expected_changelog == actual_changelog -def test_default_changelog_template_using_tag_format( - repo_with_git_flow_and_release_channels_angular_commits_using_tag_format, - default_angular_parser, +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +def test_default_changelog_template_w_unreleased_changes( + default_changelog_template: str, + hvcs_client: type[HvcsBase], + example_git_https_url: str, + artificial_release_history: ReleaseHistory, ): - repo = repo_with_git_flow_and_release_channels_angular_commits_using_tag_format + version_str = "1.0.0" + version = Version.parse(version_str) + + feat_commit_obj = artificial_release_history.released[version]["elements"]["feature"][0] + feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url(feat_commit_obj.commit.hexsha) + feat_description = str.join('\n', feat_commit_obj.descriptions) + + fix_commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] + fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url(fix_commit_obj.commit.hexsha) + fix_description = str.join('\n', fix_commit_obj.descriptions) + + expected_changelog = str.join("\n", [ + "# CHANGELOG", + "## Unreleased", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + f"## v{version_str} ({TODAY_DATE_STR})", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "### Fix", + f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", + "", + ]) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) - rh = ReleaseHistory.from_git_history( - repo=repo, - translator=VersionTranslator(tag_format="vpy{version}"), - commit_parser=default_angular_parser, - ) context = make_changelog_context( - hvcs_client=Github(remote_url=repo.remote().url), release_history=rh + hvcs_client=hvcs_client(remote_url=example_git_https_url), + release_history=artificial_release_history, ) context.bind_to_environment(env) - - actual_content = env.from_string(default_changelog_template).render() - assert actual_content == EXPECTED_CONTENT + actual_changelog = env.from_string(default_changelog_template).render() + assert expected_changelog == actual_changelog From 6dfaa0318334b5fa5dc72064d3c4bfd9eec25a4c Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 10 Feb 2024 00:05:04 -0500 Subject: [PATCH 12/22] test(fixtures): refactor for better chronological ordering for test success --- tests/scenario/test_template_render.py | 33 ++++++++++++++++---------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/tests/scenario/test_template_render.py b/tests/scenario/test_template_render.py index af116a74f..63aef9514 100644 --- a/tests/scenario/test_template_render.py +++ b/tests/scenario/test_template_render.py @@ -1,11 +1,19 @@ +from __future__ import annotations + import itertools import os from pathlib import Path +from typing import TYPE_CHECKING import pytest from semantic_release.changelog.template import environment, recursive_render +if TYPE_CHECKING: + from tests.conftest import ExProjectDir + +# FIX me + NORMAL_TEMPLATE_SRC = """--- content: - a string @@ -40,6 +48,7 @@ def _strip_trailing_j2(path: Path) -> Path: @pytest.fixture def normal_template(example_project_template_dir): template = example_project_template_dir / "normal.yaml.j2" + template.parent.mkdir(parents=True, exist_ok=True) template.write_text(NORMAL_TEMPLATE_SRC) return template @@ -48,14 +57,14 @@ def normal_template(example_project_template_dir): def long_directory_path(example_project_template_dir): # NOTE: fixture enables using Path rather than # constant string, so no issue with / vs \ on Windows - d = example_project_template_dir / "long" / "dir" / "path" - os.makedirs(str(d.resolve()), exist_ok=True) - return d + folder = example_project_template_dir / "long" / "dir" / "path" + return folder @pytest.fixture def deeply_nested_file(long_directory_path): file = long_directory_path / "buried.txt" + file.parent.mkdir(parents=True, exist_ok=True) file.write_text(PLAINTEXT_FILE_CONTENT) return file @@ -63,20 +72,21 @@ def deeply_nested_file(long_directory_path): @pytest.fixture def hidden_file(example_project_template_dir): file = example_project_template_dir / ".hidden" + file.parent.mkdir(parents=True, exist_ok=True) file.write_text("I shouldn't be present") return file @pytest.fixture -def directory_path_with_hidden_subfolder(example_project_template_dir): - d = example_project_template_dir / "path" / ".subfolder" / "hidden" - os.makedirs(str(d.resolve()), exist_ok=True) - return d +def directory_path_with_hidden_subfolder(example_project_template_dir: Path) -> Path: + folder = example_project_template_dir / "path" / ".subfolder" / "hidden" + return folder @pytest.fixture def excluded_file(directory_path_with_hidden_subfolder): file = directory_path_with_hidden_subfolder / "excluded.txt" + file.parent.mkdir(parents=True, exist_ok=True) file.write_text("I shouldn't be present") return file @@ -132,15 +142,14 @@ def test_recursive_render( @pytest.fixture -def dotfolder_template_dir(example_project_dir: Path): - newpath = example_project_dir / ".templates/.psr-templates" - newpath.mkdir(parents=True, exist_ok=True) - return newpath +def dotfolder_template_dir(example_project_dir: ExProjectDir) -> Path: + return example_project_dir / ".templates/.psr-templates" @pytest.fixture -def dotfolder_template(dotfolder_template_dir: Path): +def dotfolder_template(init_example_project: None, dotfolder_template_dir: Path) -> Path: tmpl = dotfolder_template_dir / "template.txt" + tmpl.parent.mkdir(parents=True, exist_ok=True) tmpl.write_text("I am a template") return tmpl From ab955ebd2c6843920d3cfd93d7a36a62f5ff7dbb Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 10 Feb 2024 00:09:33 -0500 Subject: [PATCH 13/22] test(changelog): enforce common single newline after generated docs --- tests/command_line/test_changelog.py | 2 +- tests/fixtures/git_repo.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index bcff9d2ae..4197ac002 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -231,7 +231,7 @@ def test_custom_release_notes_template( resp = cli_runner.invoke(main, [changelog.name or "changelog", "--post-to-release-tag", tag]) expected_release_notes = runtime_context_with_tags.template_environment.from_string( EXAMPLE_RELEASE_NOTES_TEMPLATE - ).render(version=version, release=release) + ).render(version=version, release=release) + '\n' # Assert assert resp.exit_code == 0, ( diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 208a63e86..4c5eba4fc 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -360,7 +360,7 @@ def _mimic_semantic_release_default_changelog( str.join("\n", [ entry for entry in version_entries ]) - ]) + ]).rstrip() + '\n' if dest_file is not None: dest_file.write_text(changelog_content) From 637d15901d59f93faf33075572f29abdc52f9ea8 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sat, 10 Feb 2024 00:10:27 -0500 Subject: [PATCH 14/22] fix(changelog): make sure default templates render ending in 1 newline --- semantic_release/cli/commands/changelog.py | 4 ++-- semantic_release/cli/commands/version.py | 2 +- semantic_release/cli/common.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/semantic_release/cli/commands/changelog.py b/semantic_release/cli/commands/changelog.py index 8623219dd..785f0a149 100644 --- a/semantic_release/cli/commands/changelog.py +++ b/semantic_release/cli/commands/changelog.py @@ -67,7 +67,7 @@ def changelog(ctx: click.Context, release_tag: str | None = None) -> None: ) else: changelog_text = render_default_changelog_file(env) - changelog_file.write_text(changelog_text, encoding="utf-8") + changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8") else: if runtime.global_cli_options.noop: @@ -112,7 +112,7 @@ def changelog(ctx: click.Context, release_tag: str | None = None) -> None: else: try: hvcs_client.create_or_update_release( - release_tag, release_notes, prerelease=version.is_prerelease + release_tag, f"{release_notes}\n", prerelease=version.is_prerelease ) except Exception as e: log.exception(e) diff --git a/semantic_release/cli/commands/version.py b/semantic_release/cli/commands/version.py index 8f422b120..278a4d880 100644 --- a/semantic_release/cli/commands/version.py +++ b/semantic_release/cli/commands/version.py @@ -429,7 +429,7 @@ def version( # noqa: C901 ) else: changelog_text = render_default_changelog_file(env) - changelog_file.write_text(changelog_text, encoding="utf-8") + changelog_file.write_text(f"{changelog_text}\n", encoding="utf-8") updated_paths = [str(changelog_file.relative_to(repo.working_dir))] diff --git a/semantic_release/cli/common.py b/semantic_release/cli/common.py index 10b285ba9..c0ede5ac2 100644 --- a/semantic_release/cli/common.py +++ b/semantic_release/cli/common.py @@ -34,7 +34,7 @@ def render_default_changelog_file(template_environment: Environment) -> str: .read_text(encoding="utf-8") ) tmpl = template_environment.from_string(changelog_text) - return tmpl.render() + return tmpl.render().rstrip() def render_release_notes( @@ -45,4 +45,4 @@ def render_release_notes( ) -> str: return template_environment.from_string(release_notes_template).render( version=version, release=release - ) + ).rstrip() From d6f7d486606e408618d4e9138bc7c6f080e9657b Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 7 Jan 2024 00:45:11 -0500 Subject: [PATCH 15/22] style(tests): add additional typing to test args --- tests/command_line/test_changelog.py | 15 +++--- tests/command_line/test_main.py | 38 ++++++++------ tests/command_line/test_version.py | 52 ++++++++++++------- tests/scenario/test_template_render.py | 18 ++++--- .../unit/semantic_release/cli/test_config.py | 12 +++-- 5 files changed, 82 insertions(+), 53 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 4197ac002..abece8368 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -32,6 +32,9 @@ from tests.fixtures.example_project import ExProjectDir, UseReleaseNotesTemplateFn +changelog_subcmd = changelog.name or changelog.__name__ + + @pytest.mark.parametrize( "repo,tag", [ @@ -86,7 +89,7 @@ def test_changelog_noop_is_noop( return_value=session, ), requests_mock.Mocker(session=session) as mocker: result = cli_runner.invoke( - main, ["--noop", changelog.name or "changelog", *args] + main, ["--noop", changelog_subcmd, *args] ) assert result.exit_code == 0 @@ -123,7 +126,7 @@ def test_changelog_content_regenerated( # Remove the changelog and then check that we can regenerate it os.remove(str(example_changelog_md.resolve())) - result = cli_runner.invoke(main, [changelog.name or "changelog"]) + result = cli_runner.invoke(main, [changelog_subcmd]) assert result.exit_code == 0 # Check that the changelog file was re-created @@ -150,7 +153,7 @@ def test_changelog_release_tag_not_in_history( remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) - result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) + result = cli_runner.invoke(main, [changelog_subcmd, *args]) assert result.exit_code == 2 assert "not in release history" in result.stderr.lower() @@ -189,7 +192,7 @@ def test_changelog_post_to_release( ) as mocker, monkeypatch.context() as m: m.delenv("GITHUB_REPOSITORY", raising=False) m.delenv("CI_PROJECT_NAMESPACE", raising=False) - result = cli_runner.invoke(main, [changelog.name or "changelog", *args]) + result = cli_runner.invoke(main, [changelog_subcmd, *args]) assert result.exit_code == 0 @@ -228,7 +231,7 @@ def test_custom_release_notes_template( release = release_history.released[version] # Act - resp = cli_runner.invoke(main, [changelog.name or "changelog", "--post-to-release-tag", tag]) + resp = cli_runner.invoke(main, [changelog_subcmd, "--post-to-release-tag", tag]) expected_release_notes = runtime_context_with_tags.template_environment.from_string( EXAMPLE_RELEASE_NOTES_TEMPLATE ).render(version=version, release=release) + '\n' @@ -236,7 +239,7 @@ def test_custom_release_notes_template( # Assert assert resp.exit_code == 0, ( "Unexpected failure in command " - f"'semantic-release {changelog.name} --post-to-release-tag {tag}': " + f"'semantic-release {changelog_subcmd} --post-to-release-tag {tag}': " + resp.stderr ) assert post_mocker.call_count == 1 and post_mocker.last_request is not None diff --git a/tests/command_line/test_main.py b/tests/command_line/test_main.py index 625fd4b7d..4b47facd8 100644 --- a/tests/command_line/test_main.py +++ b/tests/command_line/test_main.py @@ -1,5 +1,8 @@ +from __future__ import annotations + import json import os +from pathlib import Path from textwrap import dedent from typing import TYPE_CHECKING @@ -10,24 +13,29 @@ from semantic_release.cli import main if TYPE_CHECKING: + from pathlib import Path + from click.testing import CliRunner + from git import Repo from tests.fixtures.example_project import UpdatePyprojectTomlFn -def test_main_prints_version_and_exits(cli_runner): +def test_main_prints_version_and_exits(cli_runner: CliRunner): result = cli_runner.invoke(main, ["--version"]) assert result.exit_code == 0 assert result.output == f"semantic-release, version {__version__}\n" @pytest.mark.parametrize("args", [[], ["--help"]]) -def test_main_prints_help_text(cli_runner, args): +def test_main_prints_help_text(cli_runner: CliRunner, args: list[str]): result = cli_runner.invoke(main, args) assert result.exit_code == 0 -def test_not_a_release_branch_exit_code(repo_with_git_flow_angular_commits, cli_runner): +def test_not_a_release_branch_exit_code( + repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner +): # Run anything that doesn't trigger the help text repo_with_git_flow_angular_commits.git.checkout("-b", "branch-does-not-exist") result = cli_runner.invoke(main, ["version", "--no-commit"]) @@ -35,7 +43,7 @@ def test_not_a_release_branch_exit_code(repo_with_git_flow_angular_commits, cli_ def test_not_a_release_branch_exit_code_with_strict( - repo_with_git_flow_angular_commits, cli_runner + repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner ): # Run anything that doesn't trigger the help text repo_with_git_flow_angular_commits.git.checkout("-b", "branch-does-not-exist") @@ -44,7 +52,7 @@ def test_not_a_release_branch_exit_code_with_strict( def test_not_a_release_branch_detached_head_exit_code( - repo_with_git_flow_angular_commits, cli_runner + repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner ): expected_err_msg = ( "Detached HEAD state cannot match any release groups; no release will be made" @@ -60,7 +68,7 @@ def test_not_a_release_branch_detached_head_exit_code( @pytest.fixture -def toml_file_with_no_configuration_for_psr(tmp_path): +def toml_file_with_no_configuration_for_psr(tmp_path: Path) -> Path: path = tmp_path / "config.toml" path.write_text( dedent( @@ -76,7 +84,7 @@ def toml_file_with_no_configuration_for_psr(tmp_path): @pytest.fixture -def json_file_with_no_configuration_for_psr(tmp_path): +def json_file_with_no_configuration_for_psr(tmp_path: Path) -> Path: path = tmp_path / "config.json" path.write_text(json.dumps({"foo": [1, 2, 3]})) @@ -85,8 +93,8 @@ def json_file_with_no_configuration_for_psr(tmp_path): @pytest.mark.usefixtures("repo_with_git_flow_angular_commits") def test_default_config_is_used_when_none_in_toml_config_file( - cli_runner, - toml_file_with_no_configuration_for_psr, + cli_runner: CliRunner, + toml_file_with_no_configuration_for_psr: Path, ): result = cli_runner.invoke( main, @@ -98,8 +106,8 @@ def test_default_config_is_used_when_none_in_toml_config_file( @pytest.mark.usefixtures("repo_with_git_flow_angular_commits") def test_default_config_is_used_when_none_in_json_config_file( - cli_runner, - json_file_with_no_configuration_for_psr, + cli_runner: CliRunner, + json_file_with_no_configuration_for_psr: Path, ): result = cli_runner.invoke( main, @@ -111,7 +119,7 @@ def test_default_config_is_used_when_none_in_json_config_file( @pytest.mark.usefixtures("repo_with_git_flow_angular_commits") def test_errors_when_config_file_does_not_exist_and_passed_explicitly( - cli_runner, + cli_runner: CliRunner, ): result = cli_runner.invoke( main, @@ -124,7 +132,7 @@ def test_errors_when_config_file_does_not_exist_and_passed_explicitly( @pytest.mark.usefixtures("repo_with_no_tags_angular_commits") def test_errors_when_config_file_invalid_configuration( - cli_runner: "CliRunner", update_pyproject_toml: "UpdatePyprojectTomlFn" + cli_runner: CliRunner, update_pyproject_toml: UpdatePyprojectTomlFn ): update_pyproject_toml("tool.semantic_release.remote.type", "invalidType") result = cli_runner.invoke(main, ["--config", "pyproject.toml", "version"]) @@ -136,8 +144,8 @@ def test_errors_when_config_file_invalid_configuration( def test_uses_default_config_when_no_config_file_found( - tmp_path, - cli_runner, + tmp_path: Path, + cli_runner: CliRunner, ): # We have to initialise an empty git repository, as the example projects # all have pyproject.toml configs which would be used by default diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 824511178..0499e1b70 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -38,6 +38,9 @@ ) +version_subcmd = version.name or version.__name__ + + @pytest.mark.parametrize( "repo", [ @@ -49,7 +52,12 @@ lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], ) -def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_runner): +def test_version_noop_is_noop( + tmp_path_factory: pytest.TempPathFactory, + example_project_dir: ExProjectDir, + repo: Repo, + cli_runner: CliRunner, +): # Make a commit to ensure we have something to release # otherwise the "no release will be made" logic will kick in first new_file = example_project_dir / "temp.txt" @@ -65,7 +73,7 @@ def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_r head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) - result = cli_runner.invoke(main, ["--noop", version.name]) + result = cli_runner.invoke(main, ["--noop", version_subcmd]) tags_after = sorted(repo.tags, key=lambda tag: tag.name) head_after = repo.head.commit @@ -216,7 +224,12 @@ def test_version_noop_is_noop(tmp_path_factory, example_project_dir, repo, cli_r ], ) def test_version_print( - repo, cli_args, expected_stdout, example_project_dir, tmp_path_factory, cli_runner + repo: Repo, + cli_args: list[str], + expected_stdout: str, + example_project_dir: ExProjectDir, + tmp_path_factory: pytest.TempPathFactory, + cli_runner: CliRunner, ): # Make a commit to ensure we have something to release # otherwise the "no release will be made" logic will kick in first @@ -232,7 +245,7 @@ def test_version_print( head_before = repo.head.commit tags_before = sorted(repo.tags, key=lambda tag: tag.name) - result = cli_runner.invoke(main, [version.name, *cli_args, "--print"]) + result = cli_runner.invoke(main, [version_subcmd, *cli_args, "--print"]) tags_after = sorted(repo.tags, key=lambda tag: tag.name) head_after = repo.head.commit @@ -258,11 +271,11 @@ def test_version_print( lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), ], ) -def test_version_already_released_no_push(repo, cli_runner): +def test_version_already_released_no_push(repo: Repo, cli_runner: CliRunner): # In these tests, unless arguments are supplied then the latest version # has already been released, so we expect an exit code of 2 with the message # to indicate that no release will be made - result = cli_runner.invoke(main, ["--strict", version.name, "--no-push"]) + result = cli_runner.invoke(main, ["--strict", version_subcmd, "--no-push"]) assert result.exit_code == 2 assert "no release will be made" in result.stderr.lower() @@ -410,7 +423,7 @@ def test_version_no_push_force_level( tags_before = sorted(repo.tags, key=lambda tag: tag.name) result = cli_runner.invoke( - main, [version.name or "version", *cli_args, "--no-push"] + main, [version_subcmd or "version", *cli_args, "--no-push"] ) tags_after = sorted(repo.tags, key=lambda tag: tag.name) @@ -481,17 +494,16 @@ def test_version_no_push_force_level( ], ) def test_version_build_metadata_triggers_new_version(repo: Repo, cli_runner: CliRunner): - version_cmd_name = version.name or "version" # Verify we get "no version to release" without build metadata no_metadata_result = cli_runner.invoke( - main, ["--strict", version_cmd_name, "--no-push"] + main, ["--strict", version_subcmd, "--no-push"] ) assert no_metadata_result.exit_code == 2 assert "no release will be made" in no_metadata_result.stderr.lower() metadata_suffix = "build.abc-12345" result = cli_runner.invoke( - main, [version_cmd_name, "--no-push", "--build-metadata", metadata_suffix] + main, [version_subcmd, "--no-push", "--build-metadata", metadata_suffix] ) assert result.exit_code == 0 assert repo.git.tag(l=f"*{metadata_suffix}") @@ -500,7 +512,7 @@ def test_version_build_metadata_triggers_new_version(repo: Repo, cli_runner: Cli def test_version_prints_current_version_if_no_new_version( repo_with_git_flow_angular_commits: Repo, cli_runner: CliRunner ): - result = cli_runner.invoke(main, [version.name or "version", "--no-push"]) + result = cli_runner.invoke(main, [version_subcmd or "version", "--no-push"]) assert result.exit_code == 0 assert "no release will be made" in result.stderr.lower() assert result.stdout == "1.2.0-alpha.2\n" @@ -527,7 +539,7 @@ def test_version_runs_build_command( ): # ACT: run & force a new version that will trigger the build command result = cli_runner.invoke( - main, [version.name or "version", "--patch", "--no-push"] + main, [version_subcmd or "version", "--patch", "--no-push"] ) assert result.exit_code == 0 @@ -543,7 +555,7 @@ def test_version_skips_build_command_with_skip_build( "subprocess.run", return_value=CompletedProcess(args=(), returncode=0) ) as patched_subprocess_run: result = cli_runner.invoke( - main, [version.name, "--patch", "--no-push", "--skip-build"] + main, [version_subcmd, "--patch", "--no-push", "--skip-build"] ) # force a new version assert result.exit_code == 0 @@ -555,7 +567,7 @@ def test_version_writes_github_actions_output( ): mock_output_file = tmp_path / "action.out" monkeypatch.setenv("GITHUB_OUTPUT", str(mock_output_file.resolve())) - result = cli_runner.invoke(main, [version.name, "--patch", "--no-push"]) + result = cli_runner.invoke(main, [version_subcmd, "--patch", "--no-push"]) assert result.exit_code == 0 action_outputs = actions_output_to_dict( @@ -570,7 +582,7 @@ def test_version_writes_github_actions_output( def test_version_exit_code_when_strict(repo_with_git_flow_angular_commits, cli_runner): - result = cli_runner.invoke(main, ["--strict", version.name, "--no-push"]) + result = cli_runner.invoke(main, ["--strict", version_subcmd, "--no-push"]) assert result.exit_code != 0 @@ -578,7 +590,7 @@ def test_version_exit_code_when_not_strict( repo_with_git_flow_angular_commits, cli_runner ): # Testing "no release will be made" - result = cli_runner.invoke(main, [version.name, "--no-push"]) + result = cli_runner.invoke(main, [version_subcmd, "--no-push"]) assert result.exit_code == 0 @@ -599,7 +611,7 @@ def test_custom_release_notes_template( # Act resp = cli_runner.invoke( - main, [version.name or "version", "--skip-build", "--vcs-release"] + main, [version_subcmd, "--skip-build", "--vcs-release"] ) release_history = get_release_history_from_context(runtime_context_with_no_tags) tag = runtime_context_with_no_tags.repo.tags[-1].name @@ -620,7 +632,7 @@ def test_custom_release_notes_template( assert mocked_git_push.call_count == 2 # 1 for commit, 1 for tag assert resp.exit_code == 0, ( "Unexpected failure in command " - f"'semantic-release {version.name} --skip-build --vcs-release': " + resp.stderr + f"'semantic-release {version_subcmd} --skip-build --vcs-release': " + resp.stderr ) assert post_mocker.call_count == 1 assert post_mocker.last_request is not None @@ -640,7 +652,7 @@ def test_version_tag_only_push( head_before = runtime_context_with_no_tags.repo.head.commit # Act - args = [version.name, "--tag", "--no-commit", "--skip-build", "--no-vcs-release"] + args = [version_subcmd, "--tag", "--no-commit", "--skip-build", "--no-vcs-release"] resp = cli_runner.invoke(main, args) tag_after = runtime_context_with_no_tags.repo.tags[-1].name @@ -682,7 +694,7 @@ def test_version_only_update_files_no_git_actions( tags_before = runtime_context_with_tags.repo.tags # Act - args = [version.name, "--minor", "--no-tag", "--no-commit", "--skip-build"] + args = [version_subcmd, "--minor", "--no-tag", "--no-commit", "--skip-build"] resp = cli_runner.invoke(main, args) tags_after = runtime_context_with_tags.repo.tags diff --git a/tests/scenario/test_template_render.py b/tests/scenario/test_template_render.py index 63aef9514..9e7960baf 100644 --- a/tests/scenario/test_template_render.py +++ b/tests/scenario/test_template_render.py @@ -10,9 +10,8 @@ from semantic_release.changelog.template import environment, recursive_render if TYPE_CHECKING: - from tests.conftest import ExProjectDir + from tests.fixtures.example_project import ExProjectDir -# FIX me NORMAL_TEMPLATE_SRC = """--- content: @@ -46,7 +45,7 @@ def _strip_trailing_j2(path: Path) -> Path: @pytest.fixture -def normal_template(example_project_template_dir): +def normal_template(example_project_template_dir: Path) -> Path: template = example_project_template_dir / "normal.yaml.j2" template.parent.mkdir(parents=True, exist_ok=True) template.write_text(NORMAL_TEMPLATE_SRC) @@ -54,7 +53,7 @@ def normal_template(example_project_template_dir): @pytest.fixture -def long_directory_path(example_project_template_dir): +def long_directory_path(example_project_template_dir: Path) -> Path: # NOTE: fixture enables using Path rather than # constant string, so no issue with / vs \ on Windows folder = example_project_template_dir / "long" / "dir" / "path" @@ -62,7 +61,7 @@ def long_directory_path(example_project_template_dir): @pytest.fixture -def deeply_nested_file(long_directory_path): +def deeply_nested_file(long_directory_path: Path) -> Path: file = long_directory_path / "buried.txt" file.parent.mkdir(parents=True, exist_ok=True) file.write_text(PLAINTEXT_FILE_CONTENT) @@ -70,7 +69,7 @@ def deeply_nested_file(long_directory_path): @pytest.fixture -def hidden_file(example_project_template_dir): +def hidden_file(example_project_template_dir: Path) -> Path: file = example_project_template_dir / ".hidden" file.parent.mkdir(parents=True, exist_ok=True) file.write_text("I shouldn't be present") @@ -84,7 +83,7 @@ def directory_path_with_hidden_subfolder(example_project_template_dir: Path) -> @pytest.fixture -def excluded_file(directory_path_with_hidden_subfolder): +def excluded_file(directory_path_with_hidden_subfolder: Path) -> Path: file = directory_path_with_hidden_subfolder / "excluded.txt" file.parent.mkdir(parents=True, exist_ok=True) file.write_text("I shouldn't be present") @@ -155,7 +154,10 @@ def dotfolder_template(init_example_project: None, dotfolder_template_dir: Path) def test_recursive_render_with_top_level_dotfolder( - example_project_dir, dotfolder_template, dotfolder_template_dir + init_example_project: None, + example_project_dir: ExProjectDir, + dotfolder_template: Path, + dotfolder_template_dir: Path, ): preexisting_paths = set(example_project_dir.rglob("**/*")) env = environment(template_dir=dotfolder_template_dir.resolve()) diff --git a/tests/unit/semantic_release/cli/test_config.py b/tests/unit/semantic_release/cli/test_config.py index 8956c259c..72447c6d5 100644 --- a/tests/unit/semantic_release/cli/test_config.py +++ b/tests/unit/semantic_release/cli/test_config.py @@ -27,6 +27,10 @@ from pathlib import Path from typing import Any + from git import Repo + + from tests.fixtures.example_project import ExProjectDir + @pytest.mark.parametrize( "remote_config, expected_token", @@ -115,7 +119,7 @@ def test_invalid_commit_parser_value(commit_parser: str): assert "commit_parser" in str(excinfo.value) -def test_default_toml_config_valid(example_project_dir): +def test_default_toml_config_valid(example_project_dir: ExProjectDir): default_config_file = example_project_dir / "default.toml" default_config_file.write_text( @@ -142,9 +146,9 @@ def test_default_toml_config_valid(example_project_dir): ) def test_commit_author_configurable( example_pyproject_toml: Path, - repo_with_no_tags_angular_commits, - mock_env, - expected_author, + repo_with_no_tags_angular_commits: Repo, + mock_env: dict[str, str], + expected_author: str, ): content = tomlkit.loads(example_pyproject_toml.read_text(encoding="utf-8")).unwrap() From d6e15fe41a40e244e216f44f73302ae2284d60c0 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Sun, 7 Jan 2024 15:01:51 -0500 Subject: [PATCH 16/22] fix(changelog-generation): fix incorrect release timezone determination --- semantic_release/changelog/release_history.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/semantic_release/changelog/release_history.py b/semantic_release/changelog/release_history.py index 483d2117e..813d25b6f 100644 --- a/semantic_release/changelog/release_history.py +++ b/semantic_release/changelog/release_history.py @@ -81,7 +81,7 @@ def from_git_history( if isinstance(tag.object, TagObject): tagger = tag.object.tagger committer = tag.object.tagger.committer() - _tz = timezone(timedelta(seconds=tag.object.tagger_tz_offset)) + _tz = timezone(timedelta(seconds=-1 * tag.object.tagger_tz_offset)) tagged_date = datetime.fromtimestamp( tag.object.tagged_date, tz=_tz ) @@ -89,7 +89,7 @@ def from_git_history( # For some reason, sometimes tag.object is a Commit tagger = tag.object.author committer = tag.object.author - _tz = timezone(timedelta(seconds=tag.object.author_tz_offset)) + _tz = timezone(timedelta(seconds=-1 * tag.object.author_tz_offset)) tagged_date = datetime.fromtimestamp( tag.object.committed_date, tz=_tz ) From db4eb8e74027e745fc31108fe08f03beb3fcc45d Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 21:45:35 -0500 Subject: [PATCH 17/22] test(changelog): increase changelog rigor to all commit types --- tests/command_line/test_changelog.py | 62 ++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index abece8368..6b892af60 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -19,6 +19,33 @@ EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, ) +from tests.fixtures.repos import ( + repo_with_no_tags_angular_commits, + repo_with_no_tags_emoji_commits, + repo_with_no_tags_scipy_commits, + repo_with_no_tags_tag_commits, + repo_with_single_branch_angular_commits, + repo_with_single_branch_emoji_commits, + repo_with_single_branch_scipy_commits, + repo_with_single_branch_tag_commits, + repo_with_single_branch_and_prereleases_angular_commits, + repo_with_single_branch_and_prereleases_emoji_commits, + repo_with_single_branch_and_prereleases_scipy_commits, + repo_with_single_branch_and_prereleases_tag_commits, + repo_w_github_flow_w_feature_release_channel_angular_commits, + repo_w_github_flow_w_feature_release_channel_emoji_commits, + repo_w_github_flow_w_feature_release_channel_scipy_commits, + repo_w_github_flow_w_feature_release_channel_tag_commits, + repo_with_git_flow_angular_commits, + repo_with_git_flow_emoji_commits, + repo_with_git_flow_scipy_commits, + repo_with_git_flow_tag_commits, + repo_with_git_flow_and_release_channels_angular_commits, + repo_with_git_flow_and_release_channels_emoji_commits, + repo_with_git_flow_and_release_channels_scipy_commits, + repo_with_git_flow_and_release_channels_tag_commits, + repo_with_git_flow_and_release_channels_angular_commits_using_tag_format, +) from tests.util import flatten_dircmp, get_release_history_from_context, remove_dir_tree if TYPE_CHECKING: @@ -107,13 +134,34 @@ def test_changelog_noop_is_noop( @pytest.mark.parametrize( "repo", [ - lazy_fixture("repo_with_no_tags_angular_commits"), - lazy_fixture("repo_with_single_branch_angular_commits"), - lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), - lazy_fixture("repo_w_github_flow_w_feature_release_channel_angular_commits"), - lazy_fixture("repo_with_git_flow_angular_commits"), - lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), - lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits_using_tag_format"), + lazy_fixture(repo_fixture) + for repo_fixture in [ + repo_with_no_tags_angular_commits.__name__, + repo_with_no_tags_emoji_commits.__name__, + repo_with_no_tags_scipy_commits.__name__, + repo_with_no_tags_tag_commits.__name__, + repo_with_single_branch_angular_commits.__name__, + repo_with_single_branch_emoji_commits.__name__, + repo_with_single_branch_scipy_commits.__name__, + repo_with_single_branch_tag_commits.__name__, + repo_with_single_branch_and_prereleases_angular_commits.__name__, + repo_with_single_branch_and_prereleases_emoji_commits.__name__, + repo_with_single_branch_and_prereleases_scipy_commits.__name__, + repo_with_single_branch_and_prereleases_tag_commits.__name__, + repo_w_github_flow_w_feature_release_channel_angular_commits.__name__, + repo_w_github_flow_w_feature_release_channel_emoji_commits.__name__, + repo_w_github_flow_w_feature_release_channel_scipy_commits.__name__, + repo_w_github_flow_w_feature_release_channel_tag_commits.__name__, + repo_with_git_flow_angular_commits.__name__, + repo_with_git_flow_emoji_commits.__name__, + repo_with_git_flow_scipy_commits.__name__, + repo_with_git_flow_tag_commits.__name__, + repo_with_git_flow_and_release_channels_angular_commits.__name__, + repo_with_git_flow_and_release_channels_emoji_commits.__name__, + repo_with_git_flow_and_release_channels_scipy_commits.__name__, + repo_with_git_flow_and_release_channels_tag_commits.__name__, + repo_with_git_flow_and_release_channels_angular_commits_using_tag_format.__name__, + ] ], ) def test_changelog_content_regenerated( From 195f2601a9785afb9d23868ff9656cac4620facd Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 21:46:24 -0500 Subject: [PATCH 18/22] style(test-changelog): change to direct fixture reference & improve clarity --- tests/command_line/test_changelog.py | 110 ++++++++++++++++----------- tests/const.py | 2 + 2 files changed, 66 insertions(+), 46 deletions(-) diff --git a/tests/command_line/test_changelog.py b/tests/command_line/test_changelog.py index 6b892af60..35acd3cd2 100644 --- a/tests/command_line/test_changelog.py +++ b/tests/command_line/test_changelog.py @@ -18,33 +18,34 @@ EXAMPLE_RELEASE_NOTES_TEMPLATE, EXAMPLE_REPO_NAME, EXAMPLE_REPO_OWNER, + SUCCESS_EXIT_CODE, ) from tests.fixtures.repos import ( - repo_with_no_tags_angular_commits, - repo_with_no_tags_emoji_commits, - repo_with_no_tags_scipy_commits, - repo_with_no_tags_tag_commits, - repo_with_single_branch_angular_commits, - repo_with_single_branch_emoji_commits, - repo_with_single_branch_scipy_commits, - repo_with_single_branch_tag_commits, - repo_with_single_branch_and_prereleases_angular_commits, - repo_with_single_branch_and_prereleases_emoji_commits, - repo_with_single_branch_and_prereleases_scipy_commits, - repo_with_single_branch_and_prereleases_tag_commits, repo_w_github_flow_w_feature_release_channel_angular_commits, repo_w_github_flow_w_feature_release_channel_emoji_commits, repo_w_github_flow_w_feature_release_channel_scipy_commits, repo_w_github_flow_w_feature_release_channel_tag_commits, - repo_with_git_flow_angular_commits, - repo_with_git_flow_emoji_commits, - repo_with_git_flow_scipy_commits, - repo_with_git_flow_tag_commits, repo_with_git_flow_and_release_channels_angular_commits, + repo_with_git_flow_and_release_channels_angular_commits_using_tag_format, repo_with_git_flow_and_release_channels_emoji_commits, repo_with_git_flow_and_release_channels_scipy_commits, repo_with_git_flow_and_release_channels_tag_commits, - repo_with_git_flow_and_release_channels_angular_commits_using_tag_format, + repo_with_git_flow_angular_commits, + repo_with_git_flow_emoji_commits, + repo_with_git_flow_scipy_commits, + repo_with_git_flow_tag_commits, + repo_with_no_tags_angular_commits, + repo_with_no_tags_emoji_commits, + repo_with_no_tags_scipy_commits, + repo_with_no_tags_tag_commits, + repo_with_single_branch_and_prereleases_angular_commits, + repo_with_single_branch_and_prereleases_emoji_commits, + repo_with_single_branch_and_prereleases_scipy_commits, + repo_with_single_branch_and_prereleases_tag_commits, + repo_with_single_branch_angular_commits, + repo_with_single_branch_emoji_commits, + repo_with_single_branch_scipy_commits, + repo_with_single_branch_tag_commits, ) from tests.util import flatten_dircmp, get_release_history_from_context, remove_dir_tree @@ -59,27 +60,31 @@ from tests.fixtures.example_project import ExProjectDir, UseReleaseNotesTemplateFn -changelog_subcmd = changelog.name or changelog.__name__ +changelog_subcmd = changelog.name or changelog.__name__ # type: ignore @pytest.mark.parametrize( "repo,tag", [ - (lazy_fixture("repo_with_no_tags_angular_commits"), None), - (lazy_fixture("repo_with_single_branch_angular_commits"), "v0.1.1"), + (lazy_fixture(repo_with_no_tags_angular_commits.__name__), None), + (lazy_fixture(repo_with_single_branch_angular_commits.__name__), "v0.1.1"), ( - lazy_fixture("repo_with_single_branch_and_prereleases_angular_commits"), + lazy_fixture( + repo_with_single_branch_and_prereleases_angular_commits.__name__ + ), "v0.2.0", ), ( lazy_fixture( - "repo_w_github_flow_w_feature_release_channel_angular_commits" + repo_w_github_flow_w_feature_release_channel_angular_commits.__name__ ), "v0.2.0", ), - (lazy_fixture("repo_with_git_flow_angular_commits"), "v1.0.0"), + (lazy_fixture(repo_with_git_flow_angular_commits.__name__), "v1.0.0"), ( - lazy_fixture("repo_with_git_flow_and_release_channels_angular_commits"), + lazy_fixture( + repo_with_git_flow_and_release_channels_angular_commits.__name__ + ), "v1.1.0-alpha.3", ), ], @@ -115,11 +120,9 @@ def test_changelog_noop_is_noop( "semantic_release.hvcs.github.build_requests_session", return_value=session, ), requests_mock.Mocker(session=session) as mocker: - result = cli_runner.invoke( - main, ["--noop", changelog_subcmd, *args] - ) + result = cli_runner.invoke(main, ["--noop", changelog_subcmd, *args]) - assert result.exit_code == 0 + assert SUCCESS_EXIT_CODE == result.exit_code # noqa: SIM300 dcmp = filecmp.dircmp(str(example_project_dir.resolve()), tempdir) @@ -175,7 +178,7 @@ def test_changelog_content_regenerated( os.remove(str(example_changelog_md.resolve())) result = cli_runner.invoke(main, [changelog_subcmd]) - assert result.exit_code == 0 + assert SUCCESS_EXIT_CODE == result.exit_code # noqa: SIM300 # Check that the changelog file was re-created assert example_changelog_md.exists() @@ -187,7 +190,9 @@ def test_changelog_content_regenerated( # Just need to test that it works for "a" project, not all -@pytest.mark.usefixtures("repo_with_single_branch_and_prereleases_angular_commits") +@pytest.mark.usefixtures( + repo_with_single_branch_and_prereleases_angular_commits.__name__ +) @pytest.mark.parametrize( "args", [("--post-to-release-tag", "v1.99.91910000000000000000000000000")] ) @@ -197,16 +202,20 @@ def test_changelog_release_tag_not_in_history( example_project_dir: ExProjectDir, cli_runner: CliRunner, ): + expected_err_code = 2 tempdir = tmp_path_factory.mktemp("test_changelog") remove_dir_tree(tempdir.resolve(), force=True) shutil.copytree(src=str(example_project_dir.resolve()), dst=tempdir) result = cli_runner.invoke(main, [changelog_subcmd, *args]) - assert result.exit_code == 2 + + assert expected_err_code == result.exit_code assert "not in release history" in result.stderr.lower() -@pytest.mark.usefixtures("repo_with_single_branch_and_prereleases_angular_commits") +@pytest.mark.usefixtures( + repo_with_single_branch_and_prereleases_angular_commits.__name__ +) @pytest.mark.parametrize("args", [("--post-to-release-tag", "v0.1.0")]) def test_changelog_post_to_release( args: list[str], @@ -232,6 +241,14 @@ def test_changelog_post_to_release( session.mount("http://", mock_adapter) session.mount("https://", mock_adapter) + expected_request_url = ( + "https://{api_url}/repos/{owner}/{repo_name}/releases".format( + api_url=Github.DEFAULT_API_DOMAIN, + owner=EXAMPLE_REPO_OWNER, + repo_name=EXAMPLE_REPO_NAME, + ) + ) + # Patch out env vars that affect changelog URLs but only get set in e.g. # Github actions with mock.patch( @@ -242,17 +259,12 @@ def test_changelog_post_to_release( m.delenv("CI_PROJECT_NAMESPACE", raising=False) result = cli_runner.invoke(main, [changelog_subcmd, *args]) - assert result.exit_code == 0 + assert SUCCESS_EXIT_CODE == result.exit_code # noqa: SIM300 assert mocker.called - assert mock_adapter.called and mock_adapter.last_request is not None - assert mock_adapter.last_request.url == ( - "https://{api_url}/repos/{owner}/{repo_name}/releases".format( - api_url=Github.DEFAULT_API_DOMAIN, - owner=EXAMPLE_REPO_OWNER, - repo_name=EXAMPLE_REPO_NAME, - ) - ) + assert mock_adapter.called + assert mock_adapter.last_request is not None + assert expected_request_url == mock_adapter.last_request.url def test_custom_release_notes_template( @@ -268,6 +280,8 @@ def test_custom_release_notes_template( runtime_context_with_tags = retrieve_runtime_context( repo_with_single_branch_and_prereleases_angular_commits ) + expected_call_count = 1 + # Arrange release_history = get_release_history_from_context(runtime_context_with_tags) tag = runtime_context_with_tags.repo.tags[-1].name @@ -280,15 +294,19 @@ def test_custom_release_notes_template( # Act resp = cli_runner.invoke(main, [changelog_subcmd, "--post-to-release-tag", tag]) - expected_release_notes = runtime_context_with_tags.template_environment.from_string( - EXAMPLE_RELEASE_NOTES_TEMPLATE - ).render(version=version, release=release) + '\n' + expected_release_notes = ( + runtime_context_with_tags.template_environment.from_string( + EXAMPLE_RELEASE_NOTES_TEMPLATE + ).render(version=version, release=release) + + "\n" + ) # Assert - assert resp.exit_code == 0, ( + assert SUCCESS_EXIT_CODE == resp.exit_code, ( # noqa: SIM300 "Unexpected failure in command " f"'semantic-release {changelog_subcmd} --post-to-release-tag {tag}': " + resp.stderr ) - assert post_mocker.call_count == 1 and post_mocker.last_request is not None + assert expected_call_count == post_mocker.call_count + assert post_mocker.last_request is not None assert expected_release_notes == post_mocker.last_request.json()["body"] diff --git a/tests/const.py b/tests/const.py index 7108f892a..9801b13c7 100644 --- a/tests/const.py +++ b/tests/const.py @@ -8,6 +8,8 @@ EXAMPLE_REPO_NAME = "example_repo" EXAMPLE_HVCS_DOMAIN = "example.com" +SUCCESS_EXIT_CODE = 0 + TODAY_DATE_STR = datetime.now().strftime("%Y-%m-%d") """Date formatted as how it would appear in the changelog (Must match local timezone)""" From 5b1c96bf868f12e3b181618b0ceccf17c7843031 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 22:20:03 -0500 Subject: [PATCH 19/22] style: resolve linter & formatting across codebase --- semantic_release/changelog/release_history.py | 8 +- semantic_release/cli/common.py | 8 +- semantic_release/commit_parser/util.py | 2 +- tests/command_line/test_version.py | 33 +++---- tests/fixtures/git_repo.py | 40 ++++----- tests/scenario/test_release_history.py | 53 +++++++---- tests/scenario/test_template_render.py | 13 +-- .../changelog/test_default_changelog.py | 88 +++++++++++-------- .../changelog/test_release_notes.py | 25 +++--- 9 files changed, 159 insertions(+), 111 deletions(-) diff --git a/semantic_release/changelog/release_history.py b/semantic_release/changelog/release_history.py index 813d25b6f..f34fc3e51 100644 --- a/semantic_release/changelog/release_history.py +++ b/semantic_release/changelog/release_history.py @@ -81,7 +81,9 @@ def from_git_history( if isinstance(tag.object, TagObject): tagger = tag.object.tagger committer = tag.object.tagger.committer() - _tz = timezone(timedelta(seconds=-1 * tag.object.tagger_tz_offset)) + _tz = timezone( + timedelta(seconds=-1 * tag.object.tagger_tz_offset) + ) tagged_date = datetime.fromtimestamp( tag.object.tagged_date, tz=_tz ) @@ -89,7 +91,9 @@ def from_git_history( # For some reason, sometimes tag.object is a Commit tagger = tag.object.author committer = tag.object.author - _tz = timezone(timedelta(seconds=-1 * tag.object.author_tz_offset)) + _tz = timezone( + timedelta(seconds=-1 * tag.object.author_tz_offset) + ) tagged_date = datetime.fromtimestamp( tag.object.committed_date, tz=_tz ) diff --git a/semantic_release/cli/common.py b/semantic_release/cli/common.py index c0ede5ac2..d6dbc98ee 100644 --- a/semantic_release/cli/common.py +++ b/semantic_release/cli/common.py @@ -43,6 +43,8 @@ def render_release_notes( version: Version, release: Release, ) -> str: - return template_environment.from_string(release_notes_template).render( - version=version, release=release - ).rstrip() + return ( + template_environment.from_string(release_notes_template) + .render(version=version, release=release) + .rstrip() + ) diff --git a/semantic_release/commit_parser/util.py b/semantic_release/commit_parser/util.py index 8bffae7fe..6ad7660aa 100644 --- a/semantic_release/commit_parser/util.py +++ b/semantic_release/commit_parser/util.py @@ -6,7 +6,7 @@ def parse_paragraphs(text: str) -> list[str]: - """ + r""" This will take a text block and return a list containing each paragraph with single line breaks collapsed into spaces. diff --git a/tests/command_line/test_version.py b/tests/command_line/test_version.py index 0499e1b70..8aeb739c7 100644 --- a/tests/command_line/test_version.py +++ b/tests/command_line/test_version.py @@ -439,11 +439,13 @@ def test_version_no_push_force_level( differing_files = sorted(flatten_dircmp(dcmp)) # Changelog already reflects changes this should introduce - assert differing_files == sorted([ - "CHANGELOG.md", - "pyproject.toml", - f"src/{EXAMPLE_PROJECT_NAME}/_version.py", - ]) + assert differing_files == sorted( + [ + "CHANGELOG.md", + "pyproject.toml", + f"src/{EXAMPLE_PROJECT_NAME}/_version.py", + ] + ) # Compare pyproject.toml new_pyproject_toml = tomlkit.loads( @@ -610,9 +612,7 @@ def test_custom_release_notes_template( ) # Act - resp = cli_runner.invoke( - main, [version_subcmd, "--skip-build", "--vcs-release"] - ) + resp = cli_runner.invoke(main, [version_subcmd, "--skip-build", "--vcs-release"]) release_history = get_release_history_from_context(runtime_context_with_no_tags) tag = runtime_context_with_no_tags.repo.tags[-1].name @@ -632,7 +632,8 @@ def test_custom_release_notes_template( assert mocked_git_push.call_count == 2 # 1 for commit, 1 for tag assert resp.exit_code == 0, ( "Unexpected failure in command " - f"'semantic-release {version_subcmd} --skip-build --vcs-release': " + resp.stderr + f"'semantic-release {version_subcmd} --skip-build --vcs-release': " + + resp.stderr ) assert post_mocker.call_count == 1 assert post_mocker.last_request is not None @@ -715,12 +716,14 @@ def test_version_only_update_files_no_git_actions( differing_files = sorted(flatten_dircmp(dcmp)) # Files that should receive version change - expected_changed_files = sorted([ - # CHANGELOG.md is not included as no modification to Git History - # (no commit or tag) has been made - "pyproject.toml", - f"src/{EXAMPLE_PROJECT_NAME}/_version.py", - ]) + expected_changed_files = sorted( + [ + # CHANGELOG.md is not included as no modification to Git History + # (no commit or tag) has been made + "pyproject.toml", + f"src/{EXAMPLE_PROJECT_NAME}/_version.py", + ] + ) assert expected_changed_files == differing_files # Compare pyproject.toml diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index 4c5eba4fc..d10b870d8 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -326,21 +326,21 @@ def _build_configured_base_repo( @pytest.fixture(scope="session") def simulate_default_changelog_creation() -> SimulateDefaultChangelogCreationFn: def build_version_entry(version: VersionStr, version_def: RepoVersionDef) -> str: - version_entry = [] - if version == "Unreleased": - version_entry.append(f"## {version}\n") - else: - version_entry.append( - # TODO: artificial newline in front due to template when no Unreleased changes exist - f"\n## v{version} ({TODAY_DATE_STR})\n" - ) + version_entry = [] + if version == "Unreleased": + version_entry.append(f"## {version}\n") + else: + version_entry.append( + # TODO: artificial newline in front due to template when no Unreleased changes exist + f"\n## v{version} ({TODAY_DATE_STR})\n" + ) - for section_def in version_def["changelog_sections"]: - version_entry.append(f"### {section_def['section']}\n") - for i in section_def["i_commits"]: - version_entry.append(f"* {version_def['commits'][i]}\n") + for section_def in version_def["changelog_sections"]: + version_entry.append(f"### {section_def['section']}\n") + for i in section_def["i_commits"]: + version_entry.append(f"* {version_def['commits'][i]}\n") - return str.join("\n", version_entry) + return str.join("\n", version_entry) def _mimic_semantic_release_default_changelog( repo_definition: RepoDefinition, @@ -351,16 +351,12 @@ def _mimic_semantic_release_default_changelog( for version, version_def in repo_definition.items(): # prepend entries to force reverse ordering - version_entries.insert( - 0, build_version_entry(version, version_def) - ) + version_entries.insert(0, build_version_entry(version, version_def)) - changelog_content = str.join("\n" * 3, [ - header, - str.join("\n", [ - entry for entry in version_entries - ]) - ]).rstrip() + '\n' + changelog_content = ( + str.join("\n" * 3, [header, str.join("\n", list(version_entries))]).rstrip() + + "\n" + ) if dest_file is not None: dest_file.write_text(changelog_content) diff --git a/tests/scenario/test_release_history.py b/tests/scenario/test_release_history.py index ce73e8ffb..0c51ced8f 100644 --- a/tests/scenario/test_release_history.py +++ b/tests/scenario/test_release_history.py @@ -235,12 +235,19 @@ def test_release_history( for k in expected_release_history.released: expected = expected_release_history.released[k] actual = released[k]["elements"] - actual_released_messages = str.join("\n\n", sorted([ - str(res.commit.message) for results in actual.values() for res in results - ])) - expected_released_messages = str.join("\n\n", sorted([ - msg for bucket in expected.values() for msg in bucket - ])) + actual_released_messages = str.join( + "\n\n", + sorted( + [ + str(res.commit.message) + for results in actual.values() + for res in results + ] + ), + ) + expected_released_messages = str.join( + "\n\n", sorted([msg for bucket in expected.values() for msg in bucket]) + ) assert expected_released_messages == actual_released_messages for commit_message in ANGULAR_COMMITS_MINOR: @@ -252,18 +259,30 @@ def test_release_history( repo, translator, default_angular_parser ) - actual_unreleased_messages = str.join("\n\n", sorted([ - str(res.commit.message) for results in new_unreleased.values() for res in results - ])) + actual_unreleased_messages = str.join( + "\n\n", + sorted( + [ + str(res.commit.message) + for results in new_unreleased.values() + for res in results + ] + ), + ) - expected_unreleased_messages = str.join("\n\n", sorted([ - msg - for bucket in [ - ANGULAR_COMMITS_MINOR[::-1], - *expected_release_history.unreleased.values(), - ] - for msg in bucket - ])) + expected_unreleased_messages = str.join( + "\n\n", + sorted( + [ + msg + for bucket in [ + ANGULAR_COMMITS_MINOR[::-1], + *expected_release_history.unreleased.values(), + ] + for msg in bucket + ] + ), + ) assert expected_unreleased_messages == actual_unreleased_messages assert ( diff --git a/tests/scenario/test_template_render.py b/tests/scenario/test_template_render.py index 9e7960baf..b3d1f1598 100644 --- a/tests/scenario/test_template_render.py +++ b/tests/scenario/test_template_render.py @@ -2,7 +2,6 @@ import itertools import os -from pathlib import Path from typing import TYPE_CHECKING import pytest @@ -10,6 +9,8 @@ from semantic_release.changelog.template import environment, recursive_render if TYPE_CHECKING: + from pathlib import Path + from tests.fixtures.example_project import ExProjectDir @@ -56,8 +57,7 @@ def normal_template(example_project_template_dir: Path) -> Path: def long_directory_path(example_project_template_dir: Path) -> Path: # NOTE: fixture enables using Path rather than # constant string, so no issue with / vs \ on Windows - folder = example_project_template_dir / "long" / "dir" / "path" - return folder + return example_project_template_dir / "long" / "dir" / "path" @pytest.fixture @@ -78,8 +78,7 @@ def hidden_file(example_project_template_dir: Path) -> Path: @pytest.fixture def directory_path_with_hidden_subfolder(example_project_template_dir: Path) -> Path: - folder = example_project_template_dir / "path" / ".subfolder" / "hidden" - return folder + return example_project_template_dir / "path" / ".subfolder" / "hidden" @pytest.fixture @@ -146,7 +145,9 @@ def dotfolder_template_dir(example_project_dir: ExProjectDir) -> Path: @pytest.fixture -def dotfolder_template(init_example_project: None, dotfolder_template_dir: Path) -> Path: +def dotfolder_template( + init_example_project: None, dotfolder_template_dir: Path +) -> Path: tmpl = dotfolder_template_dir / "template.txt" tmpl.parent.mkdir(parents=True, exist_ok=True) tmpl.write_text("I am a template") diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index cc138b0de..0f8b3c097 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -86,9 +86,9 @@ def artificial_release_history(commit_author: Actor): elements={ "feature": [feat_commit_parsed], "fix": [fix_commit_parsed], - } + }, ) - } + }, ) @@ -104,23 +104,32 @@ def test_default_changelog_template( rh = artificial_release_history rh.unreleased = {} # Wipe out unreleased - feat_commit_obj = artificial_release_history.released[version]["elements"]["feature"][0] - feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url(feat_commit_obj.commit.hexsha) - feat_description = str.join('\n', feat_commit_obj.descriptions) + feat_commit_obj = artificial_release_history.released[version]["elements"][ + "feature" + ][0] + feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url( + feat_commit_obj.commit.hexsha + ) + feat_description = str.join("\n", feat_commit_obj.descriptions) fix_commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] - fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url(fix_commit_obj.commit.hexsha) - fix_description = str.join('\n', fix_commit_obj.descriptions) - - expected_changelog = str.join("\n", [ - "# CHANGELOG", - f"## v{version_str} ({TODAY_DATE_STR})", - "### Feature", - f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", - "### Fix", - f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", - "", - ]) + fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url( + fix_commit_obj.commit.hexsha + ) + fix_description = str.join("\n", fix_commit_obj.descriptions) + + expected_changelog = str.join( + "\n", + [ + "# CHANGELOG", + f"## v{version_str} ({TODAY_DATE_STR})", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "### Fix", + f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", + "", + ], + ) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) context = make_changelog_context( hvcs_client=hvcs_client(remote_url=example_git_https_url), @@ -141,26 +150,35 @@ def test_default_changelog_template_w_unreleased_changes( version_str = "1.0.0" version = Version.parse(version_str) - feat_commit_obj = artificial_release_history.released[version]["elements"]["feature"][0] - feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url(feat_commit_obj.commit.hexsha) - feat_description = str.join('\n', feat_commit_obj.descriptions) + feat_commit_obj = artificial_release_history.released[version]["elements"][ + "feature" + ][0] + feat_commit_url = hvcs_client(example_git_https_url).commit_hash_url( + feat_commit_obj.commit.hexsha + ) + feat_description = str.join("\n", feat_commit_obj.descriptions) fix_commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] - fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url(fix_commit_obj.commit.hexsha) - fix_description = str.join('\n', fix_commit_obj.descriptions) - - expected_changelog = str.join("\n", [ - "# CHANGELOG", - "## Unreleased", - "### Feature", - f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", - f"## v{version_str} ({TODAY_DATE_STR})", - "### Feature", - f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", - "### Fix", - f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", - "", - ]) + fix_commit_url = hvcs_client(example_git_https_url).commit_hash_url( + fix_commit_obj.commit.hexsha + ) + fix_description = str.join("\n", fix_commit_obj.descriptions) + + expected_changelog = str.join( + "\n", + [ + "# CHANGELOG", + "## Unreleased", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + f"## v{version_str} ({TODAY_DATE_STR})", + "### Feature", + f"* {feat_description} ([`{feat_commit_obj.commit.hexsha[:7]}`]({feat_commit_url}))", + "### Fix", + f"* {fix_description} ([`{fix_commit_obj.commit.hexsha[:7]}`]({fix_commit_url}))", + "", + ], + ) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) context = make_changelog_context( hvcs_client=hvcs_client(remote_url=example_git_https_url), diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index cd2adf0e9..d4a2958f7 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -57,9 +57,9 @@ def artificial_release_history(commit_author: Actor): tagged_date=datetime.utcnow(), elements={ "fix": [fix_commit_parsed], - } + }, ) - } + }, ) @@ -87,14 +87,19 @@ def test_default_release_notes_template( version_str = "1.1.0-alpha.3" version = Version.parse(version_str) commit_obj = artificial_release_history.released[version]["elements"]["fix"][0] - commit_url = hvcs_client(example_git_https_url).commit_hash_url(commit_obj.commit.hexsha) - commit_description = str.join('\n', commit_obj.descriptions) - expected_content = str.join("\n", [ - f"# v{version_str} ({TODAY_DATE_STR})", - "## Fix", - f"* {commit_description} ([`{commit_obj.commit.hexsha[:7]}`]({commit_url}))", - "", - ]) + commit_url = hvcs_client(example_git_https_url).commit_hash_url( + commit_obj.commit.hexsha + ) + commit_description = str.join("\n", commit_obj.descriptions) + expected_content = str.join( + "\n", + [ + f"# v{version_str} ({TODAY_DATE_STR})", + "## Fix", + f"* {commit_description} ([`{commit_obj.commit.hexsha[:7]}`]({commit_url}))", + "", + ], + ) env = environment(trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True) context = make_changelog_context( hvcs_client=hvcs_client(remote_url=example_git_https_url), From 230e72bb73958633928fe418198247c57609819f Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 21:47:16 -0500 Subject: [PATCH 20/22] test: add bitbucket to hvcs parameter list & bitbucket to configs --- tests/fixtures/example_project.py | 15 ++++++++++++++- tests/fixtures/git_repo.py | 3 +++ .../changelog/test_default_changelog.py | 6 +++--- .../changelog/test_release_notes.py | 4 ++-- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/tests/fixtures/example_project.py b/tests/fixtures/example_project.py index 7cc28872d..eabd66c6f 100644 --- a/tests/fixtures/example_project.py +++ b/tests/fixtures/example_project.py @@ -14,7 +14,7 @@ ScipyCommitParser, TagCommitParser, ) -from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab from tests.const import ( EXAMPLE_CHANGELOG_MD_CONTENT, @@ -352,3 +352,16 @@ def _use_gitea_hvcs(domain: str | None = None) -> type[HvcsBase]: return Gitea return _use_gitea_hvcs + + +@pytest.fixture(scope="session") +def use_bitbucket_hvcs(update_pyproject_toml: UpdatePyprojectTomlFn) -> UseHvcsFn: + """Modify the configuration file to use BitBucket as the HVCS.""" + + def _use_bitbucket_hvcs(domain: str | None = None) -> type[HvcsBase]: + update_pyproject_toml("tool.semantic_release.remote.type", "bitbucket") + if domain is not None: + update_pyproject_toml("tool.semantic_release.remote.domain", domain) + return Bitbucket + + return _use_bitbucket_hvcs diff --git a/tests/fixtures/git_repo.py b/tests/fixtures/git_repo.py index d10b870d8..ae39a57f1 100644 --- a/tests/fixtures/git_repo.py +++ b/tests/fixtures/git_repo.py @@ -252,6 +252,7 @@ def build_configured_base_repo( use_github_hvcs: UseHvcsFn, use_gitlab_hvcs: UseHvcsFn, use_gitea_hvcs: UseHvcsFn, + use_bitbucket_hvcs: UseHvcsFn, use_angular_parser: UseParserFn, use_emoji_parser: UseParserFn, use_scipy_parser: UseParserFn, @@ -301,6 +302,8 @@ def _build_configured_base_repo( hvcs_class = use_gitlab_hvcs(hvcs_domain) elif hvcs_client_name == "gitea": hvcs_class = use_gitea_hvcs(hvcs_domain) + elif hvcs_client_name == "bitbucket": + hvcs_class = use_bitbucket_hvcs(hvcs_domain) else: raise ValueError(f"Unknown HVCS client name: {hvcs_client_name}") diff --git a/tests/unit/semantic_release/changelog/test_default_changelog.py b/tests/unit/semantic_release/changelog/test_default_changelog.py index 0f8b3c097..f5dc9fbc3 100644 --- a/tests/unit/semantic_release/changelog/test_default_changelog.py +++ b/tests/unit/semantic_release/changelog/test_default_changelog.py @@ -16,7 +16,7 @@ from semantic_release.changelog.template import environment from semantic_release.commit_parser import ParsedCommit from semantic_release.enums import LevelBump -from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab from semantic_release.version.translator import Version from tests.const import TODAY_DATE_STR @@ -92,7 +92,7 @@ def artificial_release_history(commit_author: Actor): ) -@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_changelog_template( default_changelog_template: str, hvcs_client: type[HvcsBase], @@ -140,7 +140,7 @@ def test_default_changelog_template( assert expected_changelog == actual_changelog -@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_changelog_template_w_unreleased_changes( default_changelog_template: str, hvcs_client: type[HvcsBase], diff --git a/tests/unit/semantic_release/changelog/test_release_notes.py b/tests/unit/semantic_release/changelog/test_release_notes.py index d4a2958f7..285eea1b1 100644 --- a/tests/unit/semantic_release/changelog/test_release_notes.py +++ b/tests/unit/semantic_release/changelog/test_release_notes.py @@ -17,7 +17,7 @@ from semantic_release.changelog.template import environment from semantic_release.commit_parser import ParsedCommit from semantic_release.enums import LevelBump -from semantic_release.hvcs import Gitea, Github, Gitlab +from semantic_release.hvcs import Bitbucket, Gitea, Github, Gitlab from semantic_release.version import Version from tests.const import TODAY_DATE_STR @@ -72,7 +72,7 @@ def release_notes_template() -> str: return version_notes_template.read_text(encoding="utf-8") -@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea]) +@pytest.mark.parametrize("hvcs_client", [Github, Gitlab, Gitea, Bitbucket]) def test_default_release_notes_template( example_git_https_url: str, hvcs_client: type[HvcsBase], From 46d3c35bf553e9826daabfe4431fbf605a02d4ff Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 22:29:47 -0500 Subject: [PATCH 21/22] test(fixtures): adjust scipy changelog expectations related to parse errors --- tests/fixtures/repos/git_flow/repo_w_3_release_channels.py | 2 +- tests/fixtures/repos/github_flow/repo_w_release_channels.py | 2 +- tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py | 2 +- tests/fixtures/repos/trunk_based_dev/repo_w_tags.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py index a10b57de3..6077450bd 100644 --- a/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_3_release_channels.py @@ -39,7 +39,7 @@ def get_commits_for_git_flow_repo_w_3_release_channels() -> GetRepoDefinitionFn: "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ diff --git a/tests/fixtures/repos/github_flow/repo_w_release_channels.py b/tests/fixtures/repos/github_flow/repo_w_release_channels.py index 5c84162c7..5e5e7c38e 100644 --- a/tests/fixtures/repos/github_flow/repo_w_release_channels.py +++ b/tests/fixtures/repos/github_flow/repo_w_release_channels.py @@ -39,7 +39,7 @@ def get_commits_for_github_flow_repo_w_feature_release_channel() -> GetRepoDefin "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 5ee445a1d..31dc41cef 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -52,7 +52,7 @@ def get_commits_for_trunk_only_repo_w_no_tags() -> GetRepoDefinitionFn: "scipy": [ {"section": "Feature", "i_commits": [2]}, {"section": "Fix", "i_commits": [1, 3]}, - {"section": "None", "i_commits": [0]}, + {"section": "Unknown", "i_commits": [0]}, ], "tag": [ {"section": "Feature", "i_commits": [2]}, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py index 4b933bd41..0669e684d 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_tags.py @@ -39,7 +39,7 @@ def get_commits_for_trunk_only_repo_w_tags() -> GetRepoDefinitionFn: "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ From a18322981818d7b700dffa50f20d26b43980e2f1 Mon Sep 17 00:00:00 2001 From: codejedi365 Date: Fri, 1 Mar 2024 22:39:48 -0500 Subject: [PATCH 22/22] test(fixtures): correct the ordering of commits in changelog expectations --- tests/fixtures/repos/git_flow/repo_w_2_release_channels.py | 2 +- tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py | 6 +++--- tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py index 4e03ebda4..7e47a6955 100644 --- a/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py +++ b/tests/fixtures/repos/git_flow/repo_w_2_release_channels.py @@ -39,7 +39,7 @@ def get_commits_for_git_flow_repo_with_2_release_channels() -> GetRepoDefinition "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [ diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py index 31dc41cef..06ff258ae 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_no_tags.py @@ -45,18 +45,18 @@ def get_commits_for_trunk_only_repo_w_no_tags() -> GetRepoDefinitionFn: {"section": "Unknown", "i_commits": [0]}, ], "emoji": [ - {"section": ":bug:", "i_commits": [1, 3]}, + {"section": ":bug:", "i_commits": [3, 1]}, {"section": ":sparkles:", "i_commits": [2]}, {"section": "Other", "i_commits": [0]}, ], "scipy": [ {"section": "Feature", "i_commits": [2]}, - {"section": "Fix", "i_commits": [1, 3]}, + {"section": "Fix", "i_commits": [3, 1]}, {"section": "Unknown", "i_commits": [0]}, ], "tag": [ {"section": "Feature", "i_commits": [2]}, - {"section": "Fix", "i_commits": [1, 3]}, + {"section": "Fix", "i_commits": [3, 1]}, {"section": "Unknown", "i_commits": [0]}, ], }, diff --git a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py index 496f15940..b013dcee5 100644 --- a/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py +++ b/tests/fixtures/repos/trunk_based_dev/repo_w_prereleases.py @@ -39,7 +39,7 @@ def get_commits_for_trunk_only_repo_w_prerelease_tags() -> GetRepoDefinitionFn: "changelog_sections": { "angular": [{"section": "Unknown", "i_commits": [0]}], "emoji": [{"section": "Other", "i_commits": [0]}], - "scipy": [{"section": "None", "i_commits": [0]}], + "scipy": [{"section": "Unknown", "i_commits": [0]}], "tag": [{"section": "Unknown", "i_commits": [0]}], }, "commits": [