1# New Google Client Library Generation 2 3The script allows you to create a new client library module in the 4google-cloud-java monorepo. 5 6**This tool is for repository maintainers only. Not for library users.** 7 8## Prerequisites 9 10This section is only needed for the first run of this script. If it's already 11done, go to "Run client generation script" section. 12 13 14### Environment 15 16Use Linux environment. 17 18Install Docker. 19 20### Ensure no Release Please "snapshot" pull request open 21 22Ensure google-cloud-java repository does not have [a pull request with "autorelease: snapshot" label]( 23https://github.com/googleapis/google-cloud-java/pulls?q=is%3Apr+is%3Aopen+label%3A%22autorelease%3A+snapshot%22). 24 25If you find one, merge it after approving it. 26This is a pull request to append "-SNAPSHOT" to versions in pom.xml files in the 27repostiory. It's not for an actual release. 28 29Background: This new client library generation process creates pom.xml files with 30a "-SNAPSHOT" version. To have consistency with other modules, ensure the pom.xml files in 31the repository has "-SNAPSHOT" versions too. 32 33 34### Checkout google-cloud-java repository 35 36``` 37$ git clone https://github.com/googleapis/google-cloud-java 38$ git checkout main 39$ git pull 40``` 41 42### Install pyenv 43 44Install pyenv 45 46``` 47curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer \ 48| bash 49``` 50 51Append the following lines to `$HOME/.bashrc`. 52 53``` 54export PYENV_ROOT="$HOME/.pyenv" 55command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH" 56eval "$(pyenv init -)" 57eval "$(pyenv virtualenv-init -)" 58``` 59 60Logout the shell and login again. You should be at the home directory. 61 62Assuming you have the following folder structure: 63``` 64~ (Home) 65 -> IdeaProjects/ 66 -> google-cloud-java 67 -> ... 68``` 69You can run these next commands in the home directory (or IdeaProjects). Otherwise, run it at `google-cloud-java`'s parent directory. 70 71Confirm pyenv installation succeeded: 72 73``` 74~$ pyenv 75pyenv 2.3.4 76Usage: pyenv <command> [<args>] 77 78Some useful pyenv commands are: 79 activate Activate virtual environment 80 commands List all available pyenv commands 81 deactivate Deactivate virtual environment 82... 83``` 84 85### Install Python 3.9 via pyenv 86 87``` 88~$ pyenv install 3.9.13 89Downloading Python-3.9.13.tar.xz... 90-> https://www.python.org/ftp/python/3.9.13/Python-3.9.13.tar.xz 91Installing Python-3.9.13... 92WARNING: The Python sqlite3 extension was not compiled. Missing the SQLite3 lib? 93Installed Python-3.9.13 to /usr/local/google/home/suztomo/.pyenv/versions/3.9.13 94``` 95 96### Install Python v3.9.13 locally 97 98Run this command 99 100``` 101$ pyenv local 3.9.13 102``` 103 104Confirm `python3.9` is installed: 105``` 106$ which python3.9 107/usr/local/google/home/suztomo/.pyenv/shims/python3.9 108``` 109 110### Install Python packages 111 112At the root of google-cloud-java repository clone, run: 113 114``` 115$ python3.9 -m pip install -r generation/new_client/requirements.txt 116``` 117 118### Install GitHub CLI (Optional) 119 120Install the GitHub CLI and login, if needed (may help with `Common Errors` below): 121 122``` 123$ sudo apt-get install gh 124$ gh auth login 125``` 126 127## Run client generation script 128 129You will run new-client.py script with the following parameters. 130Collect them from the ticket before running the command. 131 132### API short name 133 134For convenience of the subsequent commands, define a variable for API short name. 135Get the value from the DevRel Services page (Example: `apikeys`): 136 137### Proto path 138 139The script takes "proto path" parameter. This is path from google3's root to the 140directory that contains versions (e.g., "v1" or "v2"). For example, if we 141have 142 143> Link to protos: http://google3/google/api/apikeys/v2/apikeys.proto 144 145then the "proto path" value we supply to the command is "google/api/apikeys". 146 147### Name pretty 148 149Pick name from the display name in the DevRel Services site. 150For example, "API Keys API" in 151https://devrel.corp.google.com/admin/products/479. 152 153### Product Docs 154 155Find product document URL in the DevRel Services site. 156For example, "https://cloud.google.com/api-keys/" in 157https://devrel.corp.google.com/admin/products/479. 158The value must starts with "https://". 159 160### API description 161 162Find the short description of the API. Usually the first sentence in the product 163document above. 164 165### Example arguments 166 167Run `new-client.py` with the arguments above: 168 169``` 170$ python3.9 generation/new_client/new-client.py generate \ 171 --api_shortname=apikeys \ 172 --proto-path=google/api/apikeys \ 173 --name-pretty="API Keys API" \ 174 --product-docs="https://cloud.google.com/api-keys/" \ 175 --api-description="API Keys lets you create and manage your API keys for your projects." 176``` 177 178The command creates changes for 179the new module in the monorepo. At the end (~ 10 minutes), it tells you to 180create a pull request in the monorepo: 181 182``` 183... 184Please create a pull request: 185 $ git checkout -b new_module_java-discoveryengine 186 $ git add . 187 $ git commit -m 'feat: [apikeys] new module for apikeys' 188 $ gh pr create --title 'feat: [apikeys] new module for apikeys' 189``` 190 191Create a pull request from the change. 192In the description, record the `python3.9 generation/new_client/new-client.py generate ...` 193command you ran above. 194 195## Advanced Options 196 197For the explanation of the available parameters, run: 198`python3.9 generation/new_client/new-client.py generate --help`. 199 200``` 201~/google-cloud-java$ python3.9 generation/new_client/new-client.py generate --help 202/usr/local/google/home/suztomo/google-cloud-java/generation/new_client 203Usage: new-client.py generate [OPTIONS] 204 205Options: 206 --api_shortname TEXT Name for the new directory name and 207 (default) artifact name [required] 208 --name-pretty TEXT The human-friendly name that appears in 209 README.md [required] 210 --product-docs TEXT Documentation URL that appears in README.md 211 [required] 212 --api-description TEXT Description that appears in README.md 213 [required] 214 --release-level [stable|preview] 215 A label that appears in repo-metadata.json. 216 The first library generation is always 217 'preview'. [default: preview] 218 --transport [grpc|http|both] A label that appears in repo-metadata.json 219 [default: grpc] 220 --language TEXT [default: java] 221 --distribution-name TEXT Maven coordinates of the generated library. 222 By default it's com.google.cloud:google- 223 cloud-<api_shortname> 224 --api-id TEXT The value of the apiid parameter used in 225 README.md It has link to https://console.clo 226 ud.google.com/flows/enableapi?apiid=<api_id> 227 --requires-billing BOOLEAN Based on this value, README.md explains 228 whether billing setup is needed or not. 229 [default: True] 230 --destination-name TEXT The directory name of the new library. By 231 default it's java-<api_shortname> 232 --proto-path TEXT Path to proto file from the root of the 233 googleapis repository to thedirectory that 234 contains the proto files (without the 235 version).For example, to generate the 236 library for 'google/maps/routing/v2', then 237 you specify this value as 238 'google/maps/routing' [required] 239 --cloud-api BOOLEAN If true, the artifact ID of the library is 240 'google-cloud-'; otherwise 'google-' 241 [default: True] 242 --group-id TEXT The group ID of the artifact when 243 distribution name is not set [default: 244 com.google.cloud] 245 --owlbot-image TEXT The owlbot container image used in 246 OwlBot.yaml [default: gcr.io/cloud-devrel- 247 public-resources/owlbot-java] 248 --library-type TEXT A label that appear in repo-metadata.json to 249 tell how the library is maintained or 250 generated [default: GAPIC_AUTO] 251 --googleapis-gen-url TEXT The URL of the repository that has generated 252 Java code from proto service definition 253 [default: 254 https://github.com/googleapis/googleapis- 255 gen.git] 256 --help Show this message and exit. 257``` 258 259Sometimes, a library generation requires special handling for 260Maven coordinates or API ID, especially when the library is not 261specific to Google Cloud. Refer to the following command example when we 262generated Google Maps Routes API Java client library. 263 264``` 265~/google-cloud-java$ python3.9 generation/new_client/new-client.py generate \ 266 --api_shortname=maps-routing \ 267 --proto-path=google/maps/routing \ 268 --name-pretty="Routes API" \ 269 --product-docs="https://developers.google.com/maps/documentation/routes" \ 270 --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." \ 271 --api-id=routes.googleapis.com \ 272 --cloud-api=false \ 273 --requires-billing=true \ 274 --distribution-name="com.google.maps:google-maps-routing" 275``` 276 277# Principles 278 279The script should finish creating a pull request even when the newly created 280module fails to compile. This gives the user flexibility to fix things in the 281created pull request. 282 283# Common Errors 284 285## Unable to clone googleapis-gen 286``` 287Creating a new module /usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/java-vmwareengine 288gcr.io/cloud-devrel-public-resources/owlbot-java:latest 289Cloning googleapis-gen... 290Username for 'https://github.com': xxxxxx 291Password for 'https://[email protected]': ****** 292remote: Support for password authentication was removed on August 13, 2021. 293remote: 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. 294fatal: Authentication failed for 'https://github.com/googleapis/googleapis-gen.git/' 295Traceback (most recent call last): 296 File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 291, in <module> 297 main() 298 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__ 299 return self.main(*args, **kwargs) 300 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1055, in main 301 rv = self.invoke(ctx) 302 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke 303 return _process_result(sub_ctx.command.invoke(sub_ctx)) 304 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke 305 return ctx.invoke(self.callback, **ctx.params) 306 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 760, in invoke 307 return __callback(*args, **kwargs) 308 File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 180, in generate 309 subprocess.check_call(["git", "clone", "-q", googleapis_gen_url, "./gen/googleapis-gen"], cwd=workdir) 310 File "/usr/local/google/home/lawrenceqiu/.pyenv/versions/3.9.13/lib/python3.9/subprocess.py", line 373, in check_call 311 raise CalledProcessError(retcode, cmd) 312subprocess.CalledProcessError: Command '['git', 'clone', '-q', 'https://github.com/googleapis/googleapis-gen.git', './gen/googleapis-gen']' returned non-zero exit status 128. 313``` 314### Solution 315Run `gh auth login` and choose to authenticate with HTTPS. You may already be authenticated with SSH. 316 317 318## Owl-bot Staging Directory Not Found 319``` 320Removing googleapis-gen... 321mv: cannot stat 'owl-bot-staging': No such file or directory 322Traceback (most recent call last): 323 File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 291, in <module> 324 main() 325 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__ 326 return self.main(*args, **kwargs) 327 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1055, in main 328 rv = self.invoke(ctx) 329 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke 330 return _process_result(sub_ctx.command.invoke(sub_ctx)) 331 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke 332 return ctx.invoke(self.callback, **ctx.params) 333 File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 760, in invoke 334 return __callback(*args, **kwargs) 335 File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 212, in generate 336 subprocess.check_call( 337 File "/usr/local/google/home/lawrenceqiu/.pyenv/versions/3.9.13/lib/python3.9/subprocess.py", line 373, in check_call 338 raise CalledProcessError(retcode, cmd) 339subprocess.CalledProcessError: Command '['mv', 'owl-bot-staging', '../']' returned non-zero exit status 1. 340``` 341 342### Solution 343The proto path is incorrect. See the `Proto Path` section to find the correct path. 344 345## Python3.9 is not Installed 346``` 347pyenv: version `3.9.13' is not installed (set by /workspace/.python-version) 348Traceback (most recent call last): 349File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 291, in <module> 350main() 351File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__ 352return self.main(*args, **kwargs) 353File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1055, in main 354rv = self.invoke(ctx) 355File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke 356return _process_result(sub_ctx.command.invoke(sub_ctx)) 357File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke 358return ctx.invoke(self.callback, **ctx.params) 359File "/usr/local/google/home/lawrenceqiu/.local/lib/python3.9/site-packages/click/core.py", line 760, in invoke 360return __callback(*args, **kwargs) 361File "/usr/local/google/home/lawrenceqiu/IdeaProjects/google-cloud-java/generation/new_client/new-client.py", line 223, in generate 362subprocess.check_call( 363File "/usr/local/google/home/lawrenceqiu/.pyenv/versions/3.9.13/lib/python3.9/subprocess.py", line 373, in check_call 364raise CalledProcessError(retcode, cmd) 365subprocess.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. 366``` 367 368### Solution 369You have run the `pyenv local 3.9.13` in the `google-cloud-java` repo. 3701. Remove the `.python-version` file in `google-cloud-java`. 3712. `cd ..` out to the parent directory and run `pyenv local 3.9.13` there 3723. `cd google-cloud-java` back into the repo and run the generation script again 373