The script allows you to create a new client library module in the google-cloud-java monorepo.
This tool is for repository maintainers only. Not for library users.
Your can generate a library by using the
generate_new_client.yaml Github Action.
This workflow runs new-client.py with your input arguments
and creates a pull request with the generated library.
new-client.py arguments are available in the Github Action.
Please refer to
this
section
for more arguments (it requires to setup a local environment).
The arguments currently supported by the Github Action are:
- API short Name (
api_shortname) - Proto path (
proto_path) - Name pretty (
name_pretty) - Product Docs (
product_docs) - REST Docs (
rest_docs) - RPC Docs (
rpc_docs) - API description (
api_description) - Transport (
transport) - Destination Name (
destination_name) - Distribution Name (
distribution_name)
The workflow automatically creates a PR for you. Once the workflow has run successfully, you can check the action logs and see the last line of the "Push to branch and create PR" step to find a link to the generated pull request.
Ensure google-cloud-java repository does not have a pull request with "autorelease: snapshot" label.
If you find one, merge it after approving it. This is a pull request to append "-SNAPSHOT" to versions in pom.xml files in the repostiory. It's not for an actual release.
Background: This new client library generation process creates pom.xml files with a "-SNAPSHOT" version. To have consistency with other modules, ensure the pom.xml files in the repository has "-SNAPSHOT" versions too.
Some languages require a new request when a new version of a service is created, but Java manages all versions of the service as a single package, so the client library will automatically be updated to include new versions.
You in order to run Github Action , you need to specify a few parameters. These parameters will be available in the Cloud Drop link (a YAML file) included in the buganizer request. The example in this README uses AlloyDB's Cloud Drop file as an example.
For convenience of the subsequent commands, define a variable for API short name. This value will be used by default to generate the following:
--distribution-name--destination-name
The corresponding value in the Cloud Drop page is api_short_name.
Example: alloydb
Important
api_short_name is not always unique across client libraries.
In the instance that the api_short_name is already in use by an existing client library, you will need to determine a unique name.
See example under [Advanced Options](#Example with duplicate api_short_name).
The script takes "proto path" parameter. This is the path from the internal google3/third_party/googleapis/stable root to the
directory that contains versions (e.g., "v1" or "v2").
Note that the internal google3/third_party/googleapis/stable directory is mirrored externally in https://github.com/googleapis/googleapis/blob/master/.
For example, if the buganizer ticket includes:
Link to protos:
http://...(omit).../google/cloud/alloydb/v1alpha/alloydb_v1alpha.yaml.
then the corresponding external mirrored proto is here: https://github.com/googleapis/googleapis/blob/master/google/cloud/alloydb/v1alpha/alloydb_v1alpha.yaml.
Therefore, the "proto path" value we supply to the command is google/cloud/alloydb.
We will publish a single module for a service that includes all versions in this path. Once the service has been published once, any future additional versions will automatically be generated via OwlBot.
The corresponding value in the Cloud Drop page is title.
Example: AlloyDB API
The corresponding value in the Cloud Drop page is documentation_uri.
The value must starts with "https://".
Example: https://cloud.google.com/alloydb/docs
The corresponding value in the Cloud Drop page is rest_reference_documentation_uri.
The value must starts with "https://".
Example: https://cloud.google.com/alloydb/docs/reference/rest
If they exist, add them as a flag to the python command below like:
--rest-docs="https://cloud.google.com/alloydb/docs/reference/rest" \
The corresponding value in the Cloud Drop page is proto_reference_documentation_uri.
The value must starts with "https://".
Example: https://cloud.google.com/speech-to-text/docs/reference/rpc
If they exist, add them as a flag to the python command below like:
--rpc-docs="https://cloud.google.com/speech-to-text/docs/reference/rpc" \
The corresponding value in the Cloud Drop page is documentation.summary or documentation.overview.
If both of those fields are missing, take the description from the product page above. Use the first sentence to keep it concise.
Example:
AlloyDB for PostgreSQL is an open source-compatible database service that
provides a powerful option for migrating, modernizing, or building
commercial-grade applications.
This variable represents the type of requests the library will make to
its corresponding service. It can be grpc (default), http or both. In
practice, it is mainly used to create a .repo-metadata.json file.
This information can be found in BUILD.bazel file. Look for transport parameter
in bazel rule java_gapic_library. For example, in alloydb's
external mirrored location: https://github.com/googleapis/googleapis/blob/master/google/cloud/alloydb/v1/BUILD.bazel#L89
This variable represents the folder name to be created in the root of the
monorepo. For example, if it is java-example-library, then a new folder
with the generated library will be created in
google-cloud-java/java-example/library.
This variable determines the Maven coordinates of the generated library. It
defaults to com.google.cloud:google-cloud-{api_shortname}. This mainly affect
the values in the generated pom.xml files.
In case the steps above don't show you how to specify the desired options, you can
run the new-client.py script in your local evironment. The advanced options
not shown in the section above cannot be specified in the Github Action,
hence the need for a local run (refer to the "Prerequisites
(for local environment)" section).
For the explanation of the available parameters, run:
python3.9 generation/new_client/new-client.py generate --help.
Sometimes, a library generation requires special handling for Maven coordinates or API ID, especially when the library is not specific to Google Cloud. The table below is the summary of the special cases:
| API paths | --api_shortname |
--distribution-name |
|---|---|---|
| google/shopping/* | shopping-<API short name> |
com.google.shipping:google-shopping-<API short name> |
| google/maps/* | maps-<API short name> |
com.google.maps:google-maps-<API short name> |
where <API short name> is the value from Cloud Drop file.
For example, the following command example was when we generated Google Maps Routes
API Java client library.
Notice maps as a prefix to the --api_shortname and it specifies com.google.maps group ID in
--distribution-name, while keeping --api-id with the value in Cloud Drop page.
~/google-cloud-java$ python3.9 generation/new_client/new-client.py generate \
--api_shortname=maps-routing \
--proto-path=google/maps/routing \
--name-pretty="Routes API" \
--product-docs="https://developers.google.com/maps/documentation/routes" \
--api-description="Routes API is the next generation, performance optimized version of the existing Directions API and Distance Matrix API. It helps you find the ideal route from A to Z, calculates ETAs and distances for matrices of origin and destination locations, and also offers new features." \
--api-id=routes.googleapis.com \
--cloud-api=false \
--requires-billing=true \
--distribution-name="com.google.maps:google-maps-routing"
Let's say you get a new library request where the Cloud Drop value for api_short_name is maps-routing.
You discover that maps-routing is already in use by an existing client library!
You need to determine a unique destination_name for the new library's subdirectory as well as a unique distribution_name for the artifact to be published. There is no hard and fast rule for determining a unique name, so some discussion will be necessary. Confirm the distribution_name with the service team on the buganizer ticket before proceeding, as this name is visible to customers as the Maven artifact name.
You will still use the non-unique api_short_name for the api_short_name flag. This is important because the api_short_name is used to derive links within cloud.google.com to enable the API.
Let's say that after some discussion, maps-routing-gps is selected as a suitable unique subdirectory name and com.google.maps:google-maps-routing-gps is selected as a suitable unique artifact name. You would then use the following command:
~/google-cloud-java$ python3.9 generation/new_client/new-client.py generate \
--api_shortname=maps-routing \
--proto-path=google/maps/routing \
--name-pretty="Routes API" \
--product-docs="https://developers.google.com/maps/documentation/routes" \
--api-description="Routes API is the next generation, performance optimized version of the existing Directions API and Distance Matrix API. It helps you find the ideal route from A to Z, calculates ETAs and distances for matrices of origin and destination locations, and also offers new features." \
--api-id=routes.googleapis.com \
--cloud-api=false \
--requires-billing=true \
--distribution-name="com.google.maps:google-maps-routing-gps" \
--destination-name="maps-routing-gps"
This section is only needed for the first local run of this script. If it's already done, go to the "Execute the workflow (either locally or via Github Action)" section.
Use Linux environment.
Install Docker.
$ git clone https://github.com/googleapis/google-cloud-java
$ git checkout main
$ git pull
Install pyenv
curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer \
| bash
Append the following lines to $HOME/.bashrc.
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"
Logout the shell and login again. You should be at the home directory.
Assuming you have the following folder structure:
~ (Home)
-> IdeaProjects/
-> google-cloud-java
-> ...
You can run these next commands in the home directory (or IdeaProjects). Otherwise, run it at google-cloud-java's parent directory.
Confirm pyenv installation succeeded:
~$ pyenv
pyenv 2.3.4
Usage: pyenv <command> [<args>]
Some useful pyenv commands are:
activate Activate virtual environment
commands List all available pyenv commands
deactivate Deactivate virtual environment
...
~$ pyenv install 3.9.13
Downloading Python-3.9.13.tar.xz...
-> https://www.python.org/ftp/python/3.9.13/Python-3.9.13.tar.xz
Installing Python-3.9.13...
WARNING: The Python sqlite3 extension was not compiled. Missing the SQLite3 lib?
Installed Python-3.9.13 to /usr/local/google/home/suztomo/.pyenv/versions/3.9.13
Run this command
$ pyenv local 3.9.13
Confirm python3.9 is installed:
$ which python3.9
/usr/local/google/home/suztomo/.pyenv/shims/python3.9
At the root of google-cloud-java repository clone, run:
$ python3.9 -m pip install -r generation/new_client/requirements.txt
Install the GitHub CLI and login, if needed (may help with Common Errors below):
$ sudo apt-get install gh
$ gh auth login
Run new-client.py with the arguments above:
$ python3.9 generation/new_client/new-client.py generate \
--api_shortname=alloydb \
--proto-path=google/cloud/alloydb \
--name-pretty="AlloyDB API" \
--product-docs="https://cloud.google.com/alloydb/docs" \
--api-description="AlloyDB for PostgreSQL is an open source-compatible database service that
provides a powerful option for migrating, modernizing, or building
commercial-grade applications."
The command creates changes for the new module in the monorepo. At the end (~ 10 minutes), it tells you to create a pull request in the monorepo:
...
Please create a pull request:
$ git checkout -b new_module_java-alloydb
$ git add .
$ git commit -m 'feat: [alloydb] new module for alloydb'
$ gh pr create --title 'feat: [alloydb] new module for alloydb'
Create a pull request from the change.
In the description, record the python3.9 generation/new_client/new-client.py generate ...
command you ran above.
The script should finish creating a pull request even when the newly created module fails to compile. This gives the user (release manager) some flexibility to fix things in the created pull request.
Creating a new module /usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/java-vmwareengine
gcr.io/cloud-devrel-public-resources/owlbot-java:latest
Cloning googleapis-gen...
Username for 'https://github.com': xxxxxx
Password for 'https://[email protected]': ******
remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/googleapis/googleapis-gen.git/'
Traceback (most recent call last):
File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 291, in <module>
main()
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
return self.main(*args, **kwargs)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 180, in generate
subprocess.check_call(["git", "clone", "-q", googleapis_gen_url, "./gen/googleapis-gen"], cwd=workdir)
File "/usr/local/google/home/lawrenceqiu/.pyenv/versions/3.9.13/lib/python3.9/subprocess.py", line 373, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['git', 'clone', '-q', 'https://github.com/googleapis/googleapis-gen.git', './gen/googleapis-gen']' returned non-zero exit status 128.
Run gh auth login and choose to authenticate with HTTPS. You may already be authenticated with SSH.

Removing googleapis-gen...
mv: cannot stat 'owl-bot-staging': No such file or directory
Traceback (most recent call last):
File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 291, in <module>
main()
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
return self.main(*args, **kwargs)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 212, in generate
subprocess.check_call(
File "/usr/local/google/home/lawrenceqiu/.pyenv/versions/3.9.13/lib/python3.9/subprocess.py", line 373, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['mv', 'owl-bot-staging', '../']' returned non-zero exit status 1.
The proto path is incorrect. See the Proto Path section to find the correct path.
pyenv: version `3.9.13' is not installed (set by /workspace/.python-version)
Traceback (most recent call last):
File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 291, in <module>
main()
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
return self.main(*args, **kwargs)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1055, in main
rv = self.invoke(ctx)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 760, in invoke
return __callback(*args, **kwargs)
File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 223, in generate
subprocess.check_call(
File "/usr/local/google/home/lawrenceqiu/.pyenv/versions/3.9.13/lib/python3.9/subprocess.py", line 373, in check_call
raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['docker', 'run', '--rm', '-v', '/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java:/workspace', '--user', '1023638:89939', 'gcr.io/cloud-devrel-public-resources/owlbot-java']' returned non-zero exit status 1.
You have run the pyenv local 3.9.13 in the google-cloud-java repo.
- Remove the
.python-versionfile ingoogle-cloud-java. cd ..out to the parent directory and runpyenv local 3.9.13therecd google-cloud-javaback into the repo and run the generation script again