From 9bb3e950ac5b1e0b4232908029b34ed353e4ec02 Mon Sep 17 00:00:00 2001 From: Noah Dietz Date: Thu, 14 May 2026 22:51:49 +0000 Subject: [PATCH 1/4] chore(.generator): preview release support --- .generator/cli.py | 37 ++++++++++++++++++++++---- .generator/test_cli.py | 52 +++++++++++++++++++++++++++++++++++++ ci/run_conditional_tests.sh | 1 + 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/.generator/cli.py b/.generator/cli.py index 0f5078204c44..0c77f5502e9a 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_actual_library_id(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"): + actual_id = _get_actual_library_id(library_id) + return f"preview-packages/{actual_id}" + 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" @@ -1570,11 +1596,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 +1697,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..bceef28e1434 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_actual_library_id, + _get_library_path, _get_libraries_to_prepare_for_release, _get_new_library_config, _get_previous_version, @@ -2045,3 +2047,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_actual_library_id(): + assert _get_actual_library_id("google-cloud-language") == "google-cloud-language" + assert _get_actual_library_id("google-cloud-language-preview") == "google-cloud-language" + assert _get_actual_library_id("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-preview/#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 + ) diff --git a/ci/run_conditional_tests.sh b/ci/run_conditional_tests.sh index 9b8eaee52e5b..b4df9cb923f4 100755 --- a/ci/run_conditional_tests.sh +++ b/ci/run_conditional_tests.sh @@ -80,6 +80,7 @@ set -e subdirs=( packages + preview-packages ) RETVAL=0 From 05c4298b9f40c39053bd7f08a53c6a0526e54327 Mon Sep 17 00:00:00 2001 From: Noah Dietz Date: Fri, 15 May 2026 19:21:43 +0000 Subject: [PATCH 2/4] chore(.generator): use pkg name in changelog --- .generator/cli.py | 3 ++- .generator/test_cli.py | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/.generator/cli.py b/.generator/cli.py index 0c77f5502e9a..3ef0440930c3 100644 --- a/.generator/cli.py +++ b/.generator/cli.py @@ -1556,8 +1556,9 @@ def _process_changelog( entry_parts.append(f"* {change[subject_key]} {commit_link}") new_entry_text = "\n".join(entry_parts) + actual_library_id = _get_actual_library_id(library_id) anchor_pattern = re.compile( - rf"(\[1\]: https://pypi\.org/project/{library_id}/#history)", + rf"(\[1\]: https://pypi\.org/project/{actual_library_id}/#history)", re.MULTILINE, ) replacement_text = f"\\g<1>\n\n{new_entry_text}" diff --git a/.generator/test_cli.py b/.generator/test_cli.py index bceef28e1434..2941416843a4 100644 --- a/.generator/test_cli.py +++ b/.generator/test_cli.py @@ -1439,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): @@ -2074,7 +2106,7 @@ def test_update_changelog_for_library_preview_package(mocker): [PyPI History][1] -[1]: https://pypi.org/project/google-cloud-language-preview/#history +[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") From 7423ed81b7fa933c05b8f6a61494362e011d20f7 Mon Sep 17 00:00:00 2001 From: Noah Dietz Date: Fri, 15 May 2026 19:57:54 +0000 Subject: [PATCH 3/4] revert ci test script change --- ci/run_conditional_tests.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/run_conditional_tests.sh b/ci/run_conditional_tests.sh index b4df9cb923f4..9b8eaee52e5b 100755 --- a/ci/run_conditional_tests.sh +++ b/ci/run_conditional_tests.sh @@ -80,7 +80,6 @@ set -e subdirs=( packages - preview-packages ) RETVAL=0 From c5a6862787e5556e8d3d555e889538382c200691 Mon Sep 17 00:00:00 2001 From: Noah Dietz Date: Fri, 15 May 2026 20:07:03 +0000 Subject: [PATCH 4/4] rename helpers --- .generator/cli.py | 10 +++++----- .generator/test_cli.py | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.generator/cli.py b/.generator/cli.py index 3ef0440930c3..e10269da3405 100644 --- a/.generator/cli.py +++ b/.generator/cli.py @@ -323,7 +323,7 @@ def _get_library_id(request_data: Dict) -> str: return library_id -def _get_actual_library_id(library_id: str) -> str: +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] @@ -343,8 +343,8 @@ def _get_library_path(library_id: str, is_mono_repo: bool) -> str: if not is_mono_repo: return "." if library_id.endswith("-preview"): - actual_id = _get_actual_library_id(library_id) - return f"preview-packages/{actual_id}" + package_name = _get_package_name(library_id) + return f"preview-packages/{package_name}" return f"packages/{library_id}" @@ -1556,9 +1556,9 @@ def _process_changelog( entry_parts.append(f"* {change[subject_key]} {commit_link}") new_entry_text = "\n".join(entry_parts) - actual_library_id = _get_actual_library_id(library_id) + package_name = _get_package_name(library_id) anchor_pattern = re.compile( - rf"(\[1\]: https://pypi\.org/project/{actual_library_id}/#history)", + rf"(\[1\]: https://pypi\.org/project/{package_name}/#history)", re.MULTILINE, ) replacement_text = f"\\g<1>\n\n{new_entry_text}" diff --git a/.generator/test_cli.py b/.generator/test_cli.py index 2941416843a4..ffbfc96feebb 100644 --- a/.generator/test_cli.py +++ b/.generator/test_cli.py @@ -48,7 +48,7 @@ _get_api_generator_options, _get_library_dist_name, _get_library_id, - _get_actual_library_id, + _get_package_name, _get_library_path, _get_libraries_to_prepare_for_release, _get_new_library_config, @@ -2081,10 +2081,10 @@ def test_get_repo_name_from_repo_metadata_missing_repo(mocker): _get_repo_name_from_repo_metadata("base", "library_id", False) -def test_get_actual_library_id(): - assert _get_actual_library_id("google-cloud-language") == "google-cloud-language" - assert _get_actual_library_id("google-cloud-language-preview") == "google-cloud-language" - assert _get_actual_library_id("google-cloud-language-preview-preview") == "google-cloud-language-preview" +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(