Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .generator/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,10 @@ RUN git clone --depth 1 https://github.com/googleapis/synthtool.git /tmp/synthto
bazel_env/bin/python3.9 -m pip install /tmp/synthtool nox && \
rm -rf /tmp/synthtool

# Install build which is used to get the metadata of package config files.
COPY .generator/requirements.in .
RUN python3.9 -m pip install -r requirements.in

# Copy the CLI script into the container.
COPY .generator/cli.py .
RUN chmod a+rx ./cli.py
Expand Down
44 changes: 44 additions & 0 deletions .generator/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
from pathlib import Path
from typing import Dict, List

import build.util

try:
import synthtool
from synthtool.languages import python_mono_repo
Expand Down Expand Up @@ -546,12 +548,54 @@ def _verify_library_namespace(library_id: str, repo: str):
)


def _get_library_dist_name(library_id: str, repo: str) -> str:
"""
Gets the package name by programmatically building the metadata.

Args:
library_id: id of the library.
repo: This directory will contain all directories that make up a
library, the .librarian folder, and any global file declared in
the config.yaml.

Returns:
str: The library name string if found, otherwise None.
"""
library_path = f"{repo}/packages/{library_id}"
metadata = build.util.project_wheel_metadata(library_path)
return metadata.get("name")


def _verify_library_dist_name(library_id: str, repo: str):
"""Verifies the library distribution name against its config files.

This function ensures that:
1. At least one of `setup.py` or `pyproject.toml` exists and is valid.
2. Any existing config file's 'name' property matches the `library_id`.

Args:
library_id: id of the library.
repo: This directory will contain all directories that make up a
library, the .librarian folder, and any global file declared in
the config.yaml.

Raises:
ValueError: If a name in an existing config file does not match the `library_id`.
"""
dist_name = _get_library_dist_name(library_id, repo)
if dist_name != library_id:
raise ValueError(
f"The distribution name `{dist_name}` does not match the folder `{library_id}`."
)


def handle_build(librarian: str = LIBRARIAN_DIR, repo: str = REPO_DIR):
"""The main coordinator for validating client library generation."""
try:
request_data = _read_json_file(f"{librarian}/{BUILD_REQUEST_FILE}")
library_id = _get_library_id(request_data)
_verify_library_namespace(library_id, repo)
_verify_library_dist_name(library_id, repo)
_run_nox_sessions(library_id, repo)
except Exception as e:
raise ValueError("Build failed.") from e
Expand Down
1 change: 1 addition & 0 deletions .generator/requirements-test.in
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ pytest-cov
pytest-mock
gcp-synthtool @ git+https://github.com/googleapis/synthtool@5aa438a342707842d11fbbb302c6277fbf9e4655
starlark-pyo3>=2025.1
build
1 change: 1 addition & 0 deletions .generator/requirements.in
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
25 changes: 24 additions & 1 deletion .generator/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
_copy_files_needed_for_post_processing,
_create_main_version_header,
_determine_bazel_rule,
_get_library_dist_name,
_determine_library_namespace,
_get_library_id,
_get_libraries_to_prepare_for_release,
Expand All @@ -52,6 +53,7 @@
_update_changelog_for_library,
_update_global_changelog,
_update_version_for_library,
_verify_library_dist_name,
_verify_library_namespace,
_write_json_file,
_write_text_file,
Expand Down Expand Up @@ -525,7 +527,8 @@ def test_handle_build_success(caplog, mocker, mock_build_request_file):
caplog.set_level(logging.INFO)

mocker.patch("cli._run_nox_sessions")
mocker.patch("cli._verify_library_namespace", return_value=True)
mocker.patch("cli._verify_library_namespace")
mocker.patch("cli._verify_library_dist_name")
handle_build()

assert "'build' command executed." in caplog.text
Expand Down Expand Up @@ -922,6 +925,26 @@ def test_determine_library_namespace_fails_not_subpath():
_determine_library_namespace(gapic_parent_path, pkg_root_path)


def test_get_library_dist_name_success(mocker):
mock_metadata = {"name": "my-lib", "version": "1.0.0"}
mocker.patch("build.util.project_wheel_metadata", return_value=mock_metadata)
assert _get_library_dist_name("my-lib", "repo") == "my-lib"


def test_verify_library_dist_name_setup_success(mocker):
"""Tests success when a library distribution name in setup.py is valid."""
mock_setup_file = mocker.patch("cli._get_library_dist_name", return_value="my-lib")
_verify_library_dist_name("my-lib", "repo")
mock_setup_file.assert_called_once_with("my-lib", "repo")


def test_verify_library_dist_name_fail(mocker):
"""Tests failure when a library-id does not match the libary distribution name."""
mocker.patch("cli._get_library_dist_name", return_value="invalid-lib")
with pytest.raises(ValueError):
_verify_library_dist_name("my-lib", "repo")


def test_verify_library_namespace_success_valid(mocker, mock_path_class):
"""Tests success when a single valid namespace is found."""
# 1. Get the mock instance from the mock class's return_value
Expand Down
Loading