Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add a GitHub Action for generating new clients using the hermetic build scripts #10488

Merged
Merged
Show file tree
Hide file tree
Changes from 74 commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
8cf7df0
chore: add new-client for hermetic build
diegomarquezp Mar 5, 2024
74a3873
enable yaml insertion
diegomarquezp Mar 6, 2024
94119ae
normalize yaml format
diegomarquezp Mar 6, 2024
7b19853
add requirements file
diegomarquezp Mar 6, 2024
b8787c5
add gh action
diegomarquezp Mar 6, 2024
1bfd884
add pull_request event for testing
diegomarquezp Mar 6, 2024
d13364e
add requirements.txt
diegomarquezp Mar 6, 2024
affb741
use requirements with hashes
diegomarquezp Mar 6, 2024
72d54dc
add requirement hashes
diegomarquezp Mar 6, 2024
6483d7c
fix requirements.in
diegomarquezp Mar 6, 2024
309e4e0
remove testing event
diegomarquezp Mar 6, 2024
e62f1e6
fix requirement hashes
diegomarquezp Mar 6, 2024
6a5172d
fix generation script call
diegomarquezp Mar 6, 2024
ca356c1
remove usage of googleapis-gen-url
diegomarquezp Mar 6, 2024
db13151
fix gapic entry generation
diegomarquezp Mar 6, 2024
1a32e29
add debug print statement
diegomarquezp Mar 6, 2024
9f8e6f1
remove mistakenly added chat library
diegomarquezp Mar 6, 2024
46620a3
suppress debug output
diegomarquezp Mar 6, 2024
124b4fd
fix pr message, fix pr label
diegomarquezp Mar 7, 2024
c0a1b54
fix pr description message
diegomarquezp Mar 7, 2024
c7429c0
improve pr descriptoin
diegomarquezp Mar 7, 2024
142fc18
fix comment
diegomarquezp Mar 7, 2024
aef76f2
add readme
diegomarquezp Mar 7, 2024
eeabd19
add advanced options
diegomarquezp Mar 7, 2024
5ef7f96
fix syntax
diegomarquezp Mar 7, 2024
918875a
clarify type of workflow
diegomarquezp Mar 7, 2024
f71f2b4
fix advanced options
diegomarquezp Mar 7, 2024
78fda36
checkout latest fix in main branch
diegomarquezp Mar 7, 2024
330f888
Merge remote-tracking branch 'origin/main' into new-library-hermetic-…
diegomarquezp Mar 7, 2024
b964c4a
remove transport form workflow options
diegomarquezp Mar 7, 2024
910e026
change destination_name to library_name, remove cloud_api
diegomarquezp Mar 7, 2024
fedd456
remove library existence check
diegomarquezp Mar 7, 2024
d4a4909
change product_docs to product_documentation
diegomarquezp Mar 7, 2024
d623ed5
Revert "change product_docs to product_documentation"
diegomarquezp Mar 7, 2024
26e4e32
add api_reference
diegomarquezp Mar 7, 2024
df167fb
add codeowner_team
diegomarquezp Mar 7, 2024
94fa787
add excluded_dependencies
diegomarquezp Mar 7, 2024
4417f44
add excluded_poms
diegomarquezp Mar 7, 2024
1eb08df
add googleapis_commitish
diegomarquezp Mar 7, 2024
f10fbd6
add mutually exclusive logic for group_id and distribution_name
diegomarquezp Mar 7, 2024
8f489ab
add issue_tracker
diegomarquezp Mar 7, 2024
da92cf1
add extra_versioned_modules
diegomarquezp Mar 7, 2024
81d9274
remove untracked file
diegomarquezp Mar 7, 2024
9afaf9f
improve logic for inferring yaml variables
diegomarquezp Mar 7, 2024
03908a7
remove debug file
diegomarquezp Mar 8, 2024
28da276
compute all proto_path verions
diegomarquezp Mar 8, 2024
ce4125a
update googleapis_commitish for testing
diegomarquezp Mar 8, 2024
66872b3
add cleanup of googleapis folder
diegomarquezp Mar 8, 2024
12f7f95
add xtrace
diegomarquezp Mar 8, 2024
9675bab
update dependencies
diegomarquezp Mar 8, 2024
c4f426d
update dependencies ii
diegomarquezp Mar 8, 2024
6e326f5
remove owlbot label
diegomarquezp Mar 11, 2024
b544a21
add owlbot run label
diegomarquezp Mar 11, 2024
b0bfbe6
fix library_name
diegomarquezp Mar 11, 2024
b92c8d7
Merge remote-tracking branch 'origin/main' into new-library-hermetic-…
diegomarquezp Mar 11, 2024
d118665
restore label
diegomarquezp Mar 11, 2024
76fdeb5
Merge remote-tracking branch 'origin/main' into new-library-hermetic-…
diegomarquezp Mar 11, 2024
42165b5
reorganize required params
diegomarquezp Mar 11, 2024
8b49863
check if library is releasable
diegomarquezp Mar 11, 2024
19b5fac
sync generation_config.yaml
diegomarquezp Mar 11, 2024
fe525b5
temporarily use latest commitish
diegomarquezp Mar 11, 2024
94fea6f
Revert "temporarily use latest commitish"
diegomarquezp Mar 11, 2024
0ff665c
add python script to parse arguments
diegomarquezp Mar 11, 2024
4b9f9f2
fix argument generation
diegomarquezp Mar 11, 2024
cac37a7
fix api-shortname parameter
diegomarquezp Mar 11, 2024
45d929c
restart tests
diegomarquezp Mar 11, 2024
b4ecb26
Merge remote-tracking branch 'origin/main' into new-library-hermetic-…
diegomarquezp Mar 12, 2024
0389090
fix documentation and comments
diegomarquezp Mar 12, 2024
3c977e8
add `library_name` explanation
diegomarquezp Mar 12, 2024
c7e9404
clarify api_shortname
diegomarquezp Mar 12, 2024
fa469d0
rename to add-new-client-config
diegomarquezp Mar 13, 2024
d1eafd0
remove redundant library_name assignment
diegomarquezp Mar 13, 2024
c3afa11
support for versioned proto_paths only
diegomarquezp Mar 13, 2024
aae9d07
instructions for multiple proto_paths
diegomarquezp Mar 13, 2024
d3b2e57
restore config yaml
diegomarquezp Mar 13, 2024
62b2c7f
restore generation config
diegomarquezp Mar 18, 2024
4c8bcfb
Merge remote-tracking branch 'origin/main' into new-library-hermetic-…
diegomarquezp Mar 18, 2024
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
157 changes: 157 additions & 0 deletions .github/workflows/generate_new_client_hermetic_build.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
name: Generate new GAPIC client library (Hermetic Build)
on:
workflow_dispatch:
# some inputs are ommited due to limit of 10 input arguments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is an existing limitation of Github actions, but IMO this is a indication that Github action may not be a good fit for new client library generation. Because we are clearly omitting some parameters here, they may not be "required" to generate a new library, but repo_metadata.json would be missing some info without those parameters.
I think we should find a different way sooner than later, but it would be out of scope for this PR. cc: @suztomo

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

repo_metadata.json would be missing some info without those parameters.

What are missing?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For example, these four in repo_metadata.json, we might be able to get them heuristically, but if we want to override them, there is no way other than manually adding them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed today, the long term solution is to

  1. Make the PR generation logic more generic so that it can recognize any changes to the generation config
  2. Trigger the generation process on every generation config change.
  3. Create a PR(Manually or still use this Github action) that adds a new section for the new client, which should trigger the process above.
    CC: @JoeWang1127

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the example.

manually adding them.

The best way is to automatically get the data from the service config yaml.

inputs:
api_shortname:
required: true
type: string
description: "`api_shortname`: Name for the new directory name and (default) artifact name"
name_pretty:
required: true
type: string
description: "`name_pretty`: The human-friendly name that appears in README.md"
api_description:
required: true
description: "`api_description`: Description that appears in README.md"
proto_path:
required: true
type: string
description: |
`proto_path`: Path to proto file from the root of the googleapis repository to the
directory that contains the proto files (with the version).
For example, to generate `v2` of google/cloud/library,
you must pass google/cloud/library/v2
product_docs:
required: true
type: string
description: "`product_docs`: Documentation URL that appears in README.md"
rest_docs:
required: false
type: string
description: |
`rest_docs`: If it exists, link to the REST Documentation for a service
rpc_docs:
required: false
type: string
description: |
`rpc_docs`: If it exists, link to the RPC Documentation for a service
library_name:
required: false
type: string
description: |
`library_name`: The directory name of the new library. By default it's
java-<api_shortname>
distribution_name:
required: false
type: string
description: |
`distribution_name`: Maven coordinates of the generated library. By default it's
com.google.cloud:google-cloud-<api_shortname>

jobs:
generate:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.9'
cache: 'pip' # caching pip dependencies
- name: Install add-new-client-config.py dependencies
run: pip install --require-hashes -r generation/new_client_hermetic_build/requirements.txt
- name: Add entry to generation_config.yaml
diegomarquezp marked this conversation as resolved.
Show resolved Hide resolved
id: config_generation
run: |
set -x
arguments=$(python generation/new_client_hermetic_build/generate-arguments.py)
echo "::set-output name=new_library_args::${arguments}"
echo "${arguments}" \
| xargs python generation/new_client_hermetic_build/add-new-client-config.py add-new-library
env:
API_SHORTNAME: ${{ github.event.inputs.api_shortname }}
NAME_PRETTY: ${{ github.event.inputs.name_pretty }}
PROTO_PATH: ${{ github.event.inputs.proto_path }}
PRODUCT_DOCS: ${{ github.event.inputs.product_docs }}
REST_DOCS: ${{ github.event.inputs.rest_docs }}
RPC_DOCS: ${{ github.event.inputs.rpc_docs }}
API_DESCRIPTION: ${{ github.event.inputs.api_description }}
LIBRARY_NAME: ${{ github.event.inputs.library_name }}
DISTRIBUTION_NAME: ${{ github.event.inputs.distribution_name }}
- name: setup docker environment
shell: bash
run: |
set -x
# we create a volume pointing to `pwd` (google-cloud-java) that will
# be referenced by the container and its children
if [[ $(docker volume inspect repo-google-cloud-java) != '[]' ]]; then
docker volume rm repo-google-cloud-java
fi
docker volume create --name "repo-google-cloud-java" --opt "type=none" --opt "device=$(pwd)" --opt "o=bind"
- name: generate from configuration
id: generation
shell: bash
run: |
set -x
repo_volumes="-v repo-google-cloud-java:/workspace/google-cloud-java"
echo "::set-output name=repo_volumes::${repo_volumes}"
docker run --rm \
${repo_volumes} \
-v /tmp:/tmp \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "RUNNING_IN_DOCKER=true" \
-e "REPO_BINDING_VOLUMES=${repo_volumes}" \
gcr.io/cloud-devrel-public-resources/java-library-generation:latest \
python /src/generate_repo.py generate \
--generation-config-yaml=/workspace/google-cloud-java/generation_config.yaml \
--repository-path=/workspace/google-cloud-java \
--target-library-api-shortname=${API_SHORTNAME}
env:
API_SHORTNAME: ${{ github.event.inputs.api_shortname }}
- name: Push to branch and create PR
run: |
set -x
[ -z "`git config user.email`" ] && git config --global user.email "cloud-java-bot@google.com"
[ -z "`git config user.name`" ] && git config --global user.name "cloud-java-bot"

# create and push to branch in origin
# random_id allows multiple runs of this workflow
random_id=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 5; echo)
branch_name="new-library/${{ github.event.inputs.api_shortname }}-${random_id}"
git checkout -b "${branch_name}"
git add --all
commit_message="feat: [${API_SHORTNAME}] new module for ${API_SHORTNAME}"
git commit -m "${commit_message}"
git remote add monorepo https://cloud-java-bot:${GH_TOKEN}@github.com/${{ github.repository }}.git
git fetch -q --unshallow monorepo
git push -f monorepo "${branch_name}"

# create PR
pr_body="Generated by @${USERNAME} via [generate_new_client_hermetic_build.yaml](https://github.com/googleapis/google-cloud-java/actions/workflows/generate_new_client_hermetic_build.yaml)

Command used:

\`\`\`
python generation/new_client_hermetic_build/add-new-client-config.py add-new-client ${GENERATION_ARGUMENTS}

docker run --rm \\
${DOCKER_VOLUMES} \\
-v /tmp:/tmp \\
-v /var/run/docker.sock:/var/run/docker.sock \\
-e \"RUNNING_IN_DOCKER=true\" \\
-e \"REPO_BINDING_VOLUMES=${DOCKER_VOLUMES}\" \\
gcr.io/cloud-devrel-public-resources/java-library-generation:latest \\
python /src/generate_repo.py generate \\
--generation-config-yaml=/workspace/google-cloud-java/generation_config.yaml \\
--repository-path=/workspace/google-cloud-java \\
--target-library-api-shortname=${API_SHORTNAME}

\`\`\`"
gh pr create --title "${commit_message}" --label "owlbot:run" --head "${branch_name}" --body "${pr_body}"
env:
USERNAME: ${{ github.actor }}
API_SHORTNAME: ${{ github.event.inputs.api_shortname }}
GENERATION_ARGUMENTS: ${{ steps.config_generation.outputs.new_library_args }}
DOCKER_VOLUMES: ${{ steps.generation.outputs.repo_volumes }}
GH_TOKEN: ${{ secrets.CLOUD_JAVA_BOT_TOKEN }}

171 changes: 171 additions & 0 deletions generation/new_client_hermetic_build/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
# New client generation (GitHub Action)
This new generation workflow enables generation of new libraries by
1. appending a new library to our [generation_config.yaml](https://github.com/googleapis/google-cloud-java/blob/c7429c0eec419c01d4e2fe14d063b9335efb810b/generation_config.yaml).
2. running the hermetic build scripts docker image and
generate the newly added library.
3. create a PR with the changes.


## Components
### `new_library_hermetic_build/add-new-client-config.py`
This script takes 10 arguments that map to items in the newly added library that
goes in `generation_config.yaml`. A new entry will be added to `libraries` with
the necessary generation configuration.

### `.github/workflows/generate_new_client_hermetic_build.yaml`
This workflow orchestrates the `add-new-client-config.py` script and also uses our docker
image
([gcr.io/cloud-devrel-public-resources/java-library-generation](https://pantheon.corp.google.com/gcr/images/cloud-devrel-public-resources/global/java-library-generation))
to generate a new library. It also creates the pull request.


## Execute the Github Action

In order to run the
[Github Action](https://github.com/googleapis/google-cloud-java/actions/workflows/generate_new_client_hermetic_build.yaml)
, 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](https://github.com/googleapis/googleapis/blob/master/google/cloud/alloydb/v1/alloydb_v1.yaml) file as an example.

### API short name (`api_shortname`)

As a convenience for the subsequent commands, we need an identifier for the
library, called `api_shortname`.
This identifier will be used by default to generate the following:
* `--distribution-name`
* `--library_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 OR to pass a unique
> `library_name`.
> See [Advanced Options](#advanced-options).

### Proto path (`proto_path`)

This is the path from the internal `google3/third_party/googleapis/stable` root to the
directory that contains the proto definitions for a specific version.
For example: `google/datastore/v2`. Root-level proto paths like
`google/datastore` are not supported.
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/v1alpha`.

We will publish a single module for a service that includes the specified version
(in the example, `v1alpha`). Any future version must be manually added to
the configuration yaml (`google-cloud-java/generation_config.yaml`)

#### More than one `proto_path`

If you need another `proto_path` in the library, you must manually add it
to `google-cloud-java/generation_config.yaml` after generating the new client.

### Name pretty (`name_pretty`)

The corresponding value in the Cloud Drop page is `title`.

Example: `AlloyDB API`

### Product Docs (`product_docs`)

The corresponding value in the Cloud Drop page is `documentation_uri`.
The value must starts with "https://".

Example: `https://cloud.google.com/alloydb/docs`

### REST Docs (`rest_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 the value exists, add it as a flag to the python command below (see [Advanced
Options](#advanced-options]):
`--rest-docs="https://cloud.google.com/alloydb/docs/reference/rest" \`

### RPC Docs (`rpc_docs`)

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 the value exists, add it as a flag to the python command below (see [Advanced
Options](#advanced-options]):
`--rpc-docs="https://cloud.google.com/speech-to-text/docs/reference/rpc" \`

### API description (`api_description`)

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.
```

### Distribution Name (`distribution_name`)

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.

### Library Name (`library_name`)

This variable indicates the output folder of the library. For example you can
have two libraries with `alloydb` (AlloyDB and AlloyDB Connectors)
as `api_shortname`. In order to avoid both
libraries going to the default `java-alloydb` folder, we can override this
behavior by specifying a value like `alloydb-connectors` so the AlloyDB
Connectors goes to `java-alloydb-connectors`.

## Advanced Options

In case the steps above don't show you how to specify the desired options, you can
run the `add-new-client-config.py` script in your local evironment. The advanced options
not shown in the section above **cannot be specified in the Github Action**,
JoeWang1127 marked this conversation as resolved.
Show resolved Hide resolved
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_hermetic_build/add-new-client-config.py generate --help`.

After you run the script, you will see that the `generation_config.yaml` file
was modified (or the script exited because the library already existed)

The last step you need is to `cd` into the root of `google-cloud-java` and run
```
docker volume create --name "repo-google-cloud-java" --opt "type=none" --opt "device=$(pwd)" --opt "o=bind"
repo_volumes="-v repo-google-cloud-java:/workspace/google-cloud-java"
docker run --rm \
${repo_volumes} \
-v /tmp:/tmp \
-v /var/run/docker.sock:/var/run/docker.sock \
-e "RUNNING_IN_DOCKER=true" \
-e "REPO_BINDING_VOLUMES=${repo_volumes}" \
gcr.io/cloud-devrel-public-resources/java-library-generation:latest \
python /src/generate_repo.py generate \
--generation-config-yaml=/workspace/google-cloud-java/generation_config.yaml \
--repository-path=/workspace/google-cloud-java \
--target-library-api-shortname=<your api_shortname>

```

This docker container will run the generation scripts and generate the library
in your repo. You can create a PR explaining what commands you used (the docker
command is not as informative as the `add-new-client-config.py` call, so make sure at least
the add-new-client-config.py arguments were listed).
Loading