Skip to content

Commit

Permalink
feat: generate selected libraries (#2598)
Browse files Browse the repository at this point in the history
In this PR:
- Generate a selected list of libraries based on `library_name`

This PR is a preparation for improve nightly generation.

Note that we need to change the [new library generation
workflow](https://github.com/googleapis/google-cloud-java/blob/main/.github/workflows/generate_new_client_hermetic_build.yaml)
in google-cloud-java to accommodate this change because
`library_generation/generate_repo.py` has replaced
`--target-library-api-shortname` with `--target-library-names`.
  • Loading branch information
JoeWang1127 authored Mar 26, 2024
1 parent e3d35d1 commit 739ddbb
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 23 deletions.
71 changes: 53 additions & 18 deletions library_generation/generate_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import click
import library_generation.utilities as util
import click
import os
from library_generation.generate_composed_library import generate_composed_library
from library_generation.model.generation_config import GenerationConfig
from library_generation.model.generation_config import from_yaml
from library_generation.model.library_config import LibraryConfig
from library_generation.utils.monorepo_postprocessor import monorepo_postprocessing


Expand All @@ -39,13 +41,22 @@ def main(ctx):
""",
)
@click.option(
"--target-library-api-shortname",
"--target-library-names",
required=False,
default=None,
type=str,
help="""
If specified, only the `library` whose api_shortname equals to
target-library-api-shortname will be generated.
A list of libraries will be generated.
If specified, only the `library` whose library_name is in
target-library-names will be generated.
If not specified, all libraries in the configuration yaml will be generated.
The input string will be parsed to a list of string with comma as the
separator.
For example, apigeeconnect,alloydb-connectors will be parsed as a
list of two strings, apigeeconnect and alloydb-connectors.
""",
)
@click.option(
Expand All @@ -61,39 +72,46 @@ def main(ctx):
)
def generate(
generation_config_yaml: str,
target_library_api_shortname: str,
target_library_names: str,
repository_path: str,
):
generate_from_yaml(
generation_config_yaml=generation_config_yaml,
repository_path=repository_path,
target_library_api_shortname=target_library_api_shortname,
target_library_names=target_library_names.split(",")
if target_library_names is not None
else target_library_names,
)


def generate_from_yaml(
generation_config_yaml: str,
repository_path: str,
target_library_api_shortname: str = None,
target_library_names: list[str] = None,
) -> None:
"""
Parses a config yaml and generates libraries via
generate_composed_library.py
:param generation_config_yaml: Path to generation_config.yaml that contains
the metadata about library generation
:param repository_path: If specified, the generated files will be sent to
this location. If not specified, the repository will be generated to the
current working directory.
:param target_library_names: a list of libraries to be generated.
If specified, only the library whose library_name is in
target-library-names will be generated.
If specified with an empty list, then no library will be generated.
If not specified, all libraries in the configuration yaml will be generated.
"""
# convert paths to absolute paths so they can be correctly referenced in
# convert paths to absolute paths, so they can be correctly referenced in
# downstream scripts
generation_config_yaml = os.path.abspath(generation_config_yaml)
repository_path = os.path.abspath(repository_path)

config = from_yaml(generation_config_yaml)
target_libraries = config.libraries
if target_library_api_shortname is not None:
target_libraries = [
library
for library in config.libraries
if library.api_shortname == target_library_api_shortname
]

target_libraries = get_target_libraries(
config=config, target_library_names=target_library_names
)
repo_config = util.prepare_repo(
gen_config=config, library_config=target_libraries, repo_path=repository_path
)
Expand All @@ -118,5 +136,22 @@ def generate_from_yaml(
)


if __name__ == "__main__":
main()
def get_target_libraries(
config: GenerationConfig, target_library_names: list[str] = None
) -> list[LibraryConfig]:
"""
Returns LibraryConfig objects whose library_name is in target_library_names.
:param config: a GenerationConfig object.
:param target_library_names: library_name of target libraries.
If not specified, all libraries in the given config will be returned.
:return: LibraryConfig objects.
"""
if target_library_names is None:
return config.libraries
target_libraries = set(target_library_names)
return [
library
for library in config.libraries
if library.get_library_name() in target_libraries
]
63 changes: 63 additions & 0 deletions library_generation/test/generate_repo_unit_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import unittest

from library_generation.generate_repo import get_target_libraries
from library_generation.model.generation_config import GenerationConfig
from library_generation.model.library_config import LibraryConfig


class GenerateRepoTest(unittest.TestCase):
def test_get_target_library_returns_selected_libraries(self):
one_library = GenerateRepoTest.__get_an_empty_library_config()
one_library.api_shortname = "one_library"
another_library = GenerateRepoTest.__get_an_empty_library_config()
another_library.api_shortname = "another_library"
config = GenerateRepoTest.__get_an_empty_generation_config()
config.libraries.extend([one_library, another_library])
target_libraries = get_target_libraries(config, ["another_library"])
self.assertEqual([another_library], target_libraries)

def test_get_target_library_given_null_returns_all_libraries(self):
one_library = GenerateRepoTest.__get_an_empty_library_config()
one_library.api_shortname = "one_library"
another_library = GenerateRepoTest.__get_an_empty_library_config()
another_library.api_shortname = "another_library"
config = GenerateRepoTest.__get_an_empty_generation_config()
config.libraries.extend([one_library, another_library])
target_libraries = get_target_libraries(config)
self.assertEqual([one_library, another_library], target_libraries)

@staticmethod
def __get_an_empty_generation_config() -> GenerationConfig:
return GenerationConfig(
gapic_generator_version="",
googleapis_commitish="",
synthtool_commitish="",
owlbot_cli_image="",
template_excludes=[],
path_to_yaml="",
libraries=[],
)

@staticmethod
def __get_an_empty_library_config() -> LibraryConfig:
return LibraryConfig(
api_shortname="",
name_pretty="",
api_description="",
product_documentation="",
gapic_configs=[],
)
6 changes: 1 addition & 5 deletions library_generation/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,7 @@ def prepare_repo(
os.makedirs(output_folder, exist_ok=True)
libraries = {}
for library in library_config:
library_name = (
f"{language}-{library.library_name}"
if library.library_name
else f"{language}-{library.api_shortname}"
)
library_name = f"{language}-{library.get_library_name()}"
library_path = (
f"{repo_path}/{library_name}" if gen_config.is_monorepo else f"{repo_path}"
)
Expand Down

0 comments on commit 739ddbb

Please sign in to comment.