diff --git a/.generator/cli.py b/.generator/cli.py index 0f5078204c44..e10269da3405 100644 --- a/.generator/cli.py +++ b/.generator/cli.py @@ -323,6 +323,32 @@ def _get_library_id(request_data: Dict) -> str: return library_id +def _get_package_name(library_id: str) -> str: + """Extracts the folder name from the library id by removing any -preview suffix.""" + if library_id.endswith("-preview"): + return library_id[:-8] + return library_id + + +def _get_library_path(library_id: str, is_mono_repo: bool) -> str: + """Gets the relative path of the library in the repository. + + Args: + library_id(str): The library id. + is_mono_repo(bool): True if the current repository is a mono-repo. + + Returns: + str: The relative path to the library. + """ + if not is_mono_repo: + return "." + if library_id.endswith("-preview"): + package_name = _get_package_name(library_id) + return f"preview-packages/{package_name}" + return f"packages/{library_id}" + + + def _run_post_processor(output: str, library_id: str, is_mono_repo: bool): """Runs the synthtool post-processor on the output directory. @@ -575,7 +601,7 @@ def _get_repo_metadata_file_path(base: str, library_id: str, is_mono_repo: bool) Returns: str: The absolute path to the .repo-metadata.json file. """ - path_to_library = f"packages/{library_id}" if is_mono_repo else "." + path_to_library = _get_library_path(library_id, is_mono_repo) return f"{base}/{path_to_library}/.repo-metadata.json" @@ -1530,8 +1556,9 @@ def _process_changelog( entry_parts.append(f"* {change[subject_key]} {commit_link}") new_entry_text = "\n".join(entry_parts) + package_name = _get_package_name(library_id) anchor_pattern = re.compile( - rf"(\[1\]: https://pypi\.org/project/{library_id}/#history)", + rf"(\[1\]: https://pypi\.org/project/{package_name}/#history)", re.MULTILINE, ) replacement_text = f"\\g<1>\n\n{new_entry_text}" @@ -1570,11 +1597,12 @@ def _update_changelog_for_library( tag_format(str): The format of the git tag. """ if is_mono_repo: - relative_path = f"packages/{library_id}/CHANGELOG.md" - docs_relative_path = f"packages/{library_id}/docs/CHANGELOG.md" + path_to_library = _get_library_path(library_id, is_mono_repo) + relative_path = f"{path_to_library}/CHANGELOG.md" + docs_relative_path = f"{path_to_library}/docs/CHANGELOG.md" else: relative_path = "CHANGELOG.md" - docs_relative_path = f"docs/CHANGELOG.md" + docs_relative_path = "docs/CHANGELOG.md" changelog_src = f"{repo}/{relative_path}" changelog_dest = f"{output}/{relative_path}" @@ -1670,7 +1698,7 @@ def handle_release_stage( f"{library_id} version: {previous_version}\n" ) - path_to_library = f"packages/{library_id}" if is_mono_repo else "." + path_to_library = _get_library_path(library_id, is_mono_repo) _update_version_for_library(repo, output, path_to_library, version) if library_changes is not None: diff --git a/.generator/test_cli.py b/.generator/test_cli.py index 4ccd9a3b1359..ffbfc96feebb 100644 --- a/.generator/test_cli.py +++ b/.generator/test_cli.py @@ -48,6 +48,8 @@ _get_api_generator_options, _get_library_dist_name, _get_library_id, + _get_package_name, + _get_library_path, _get_libraries_to_prepare_for_release, _get_new_library_config, _get_previous_version, @@ -1437,6 +1439,38 @@ def test_process_changelog_success(): assert result == expected_result +def test_process_changelog_success_preview(): + """Tests that a preview package updates its changelog correctly when the anchor has the standard package name.""" + current_date = datetime.now().strftime("%Y-%m-%d") + mock_content = """# Changelog\n[PyPI History][1]\n[1]: https://pypi.org/project/google-cloud-language/#history\n +## [1.2.2](https://github.com/googleapis/google-cloud-python/compare/google-cloud-language-v1.2.1...google-cloud-language-v1.2.2) (2025-06-11)""" + expected_result = f"""# Changelog\n[PyPI History][1]\n[1]: https://pypi.org/project/google-cloud-language/#history\n +## [1.2.3](https://github.com/googleapis/google-cloud-python/compare/google-cloud-language-v1.2.2...google-cloud-language-v1.2.3) ({current_date})\n\n +### Documentation\n +* fix typo in BranchRule comment ([9461532e7d19c8d71709ec3b502e5d81340fb661](https://github.com/googleapis/google-cloud-python/commit/9461532e7d19c8d71709ec3b502e5d81340fb661))\n\n +### Features\n +* add new UpdateRepository API ([9461532e7d19c8d71709ec3b502e5d81340fb661](https://github.com/googleapis/google-cloud-python/commit/9461532e7d19c8d71709ec3b502e5d81340fb661))\n\n +### Bug Fixes\n +* some fix ([1231532e7d19c8d71709ec3b502e5d81340fb661](https://github.com/googleapis/google-cloud-python/commit/1231532e7d19c8d71709ec3b502e5d81340fb661)) +* another fix ([1241532e7d19c8d71709ec3b502e5d81340fb661](https://github.com/googleapis/google-cloud-python/commit/1241532e7d19c8d71709ec3b502e5d81340fb661))\n +## [1.2.2](https://github.com/googleapis/google-cloud-python/compare/google-cloud-language-v1.2.1...google-cloud-language-v1.2.2) (2025-06-11)""" + version = "1.2.3" + previous_version = "1.2.2" + library_id = "google-cloud-language-preview" + tag_format = "google-cloud-language-v{version}" + + result = _process_changelog( + mock_content, + _MOCK_LIBRARY_CHANGES, + version, + previous_version, + library_id, + "googleapis/google-cloud-python", + tag_format, + ) + assert result == expected_result + + def test_process_changelog_failure(): """Tests that value error is raised if the changelog anchor string cannot be found""" with pytest.raises(ValueError): @@ -2045,3 +2079,53 @@ def test_get_repo_name_from_repo_metadata_missing_repo(mocker): mocker.patch("cli._read_json_file", return_value={}) with pytest.raises(ValueError): _get_repo_name_from_repo_metadata("base", "library_id", False) + + +def test_get_package_name(): + assert _get_package_name("google-cloud-language") == "google-cloud-language" + assert _get_package_name("google-cloud-language-preview") == "google-cloud-language" + assert _get_package_name("google-cloud-language-preview-preview") == "google-cloud-language-preview" + + +@pytest.mark.parametrize( + "library_id, is_mono_repo, expected_path", + [ + ("google-cloud-language", True, "packages/google-cloud-language"), + ("google-cloud-language-preview", True, "preview-packages/google-cloud-language"), + ("google-cloud-language", False, "."), + ("google-cloud-language-preview", False, "."), + ], +) +def test_get_library_path(library_id, is_mono_repo, expected_path): + assert _get_library_path(library_id, is_mono_repo) == expected_path + + +def test_update_changelog_for_library_preview_package(mocker): + """Tests that _update_changelog_for_library writes to preview-packages directory for preview libraries.""" + mock_content = """# Changelog + +[PyPI History][1] + +[1]: https://pypi.org/project/google-cloud-language/#history +""" + mock_read = mocker.patch("cli._read_text_file", return_value=mock_content) + mock_write = mocker.patch("cli._write_text_file") + mock_path_exists = mocker.patch("cli.os.path.lexists", return_value=True) + _update_changelog_for_library( + "repo", + "output", + _MOCK_LIBRARY_CHANGES, + "1.2.3", + "1.2.2", + "google-cloud-language-preview", + True, + "{id}-v{version}", + ) + + assert mock_write.call_count == 2 + mock_write.assert_any_call( + "output/preview-packages/google-cloud-language/CHANGELOG.md", mocker.ANY + ) + mock_write.assert_any_call( + "output/preview-packages/google-cloud-language/docs/CHANGELOG.md", mocker.ANY + )