Skip to content

Commit 363e35e

Browse files
diegomarquezpsuztomoblakeli0JoeWang1127
authored
feat: enable generation with postprocessing of multiple service versions (#2342)
* use local synthtool and owlbot * remove unused files * remove more unused files * remove cache files in owlbot * use java 11 for it * remove kokoro files * use glob in owlbot entrypoint * remove unused files * do not do post-process IT on mac * concise entrypoint logic * cleanup i * cleanup ii * cleanup iii * cleanup iv * remove templates * remove protos folder * remove synthtool * connect image to owlbot entrypoint * simplify synthtool docker run command * install synthtool locally * install synthtool only once * use virtualenvs to run python scripts * install pyenv in action * remove jar from history * download google-java-format * fix pyenv init * attempt to fix pyenv installation in gh action * fix manual pyenv installation * install pyenv in profile * install pyenv in bashrc as well * use bash shell explicitly in gh action * install pyenv in same step as IT * do not restart shell * set pyenv path manually * install pyenv in its own step * propagate environment variables to other steps * fix global env var setup * remove wrong env settings * explain usage of pyenv in README * simplify pyenv setup * add comment to owlbot entrypoint * rename destination_path to preprocessed_libraries_path * infer scripts_root in postprocess_library.sh * use temporary folder for preprocess step * use owlbot files from workspace * get rid of output_folder argument * use common temp dir to clone synthtool into * lock synthtool to a specific commitish * fix file transfer * fix owl-bot-staging unpacking * remove unnecessary workspace variable * rename workspace to postprocessing_target * remove owlbot sha logic * remove repository_root variable * cleanup * correct pyenv comment * clean temp sources folder on each run * safety checks for get_proto_path_from_preprocessed_sources * fix integration test * disable compute and asset/v1p2beta1 temporarily they have changes in googleapis that have not been reflected yet in google-cloud-java * fix unit tests * correct comment * do not install docker for macos * fix owlbot files check * fix license headers * remove unnecessary owlbot_sha * add explanation on why are there no macos + postprocess ITs * use `fmt:format` instead of google formatter * clean templates * remove more unnecessary elements * add README entry explaining owlbot maintenance * remove unnecessary java format version * wip: create generate_composed_library.sh * initial implementation of generate_composed_library.sh * partial implementation of scripts * wip: fixes after main changes * partial implementation of scripts ii * correct arg parsing * fixes in python utils * fix generate_library call * correct argument preparation * add gapic generatior version arg check * call generate_library successfully * fix postprocessing step in generate_composed * IT working for all libraries * add unit tests * fix comments in generate_composed_lib * remove commented code * prepare tests without postprocessing * restore test functions * fix rename utility for building owlbot folder * correct linter problems * install realpath on macos * install utils also in unit tests * include utilities.sh in showcase scripts * comment py_util * Update library_generation/generate_composed_library.sh Co-authored-by: Tomo Suzuki <[email protected]> * add comment explaining usage of coreutils in macos workflow * explain that entrypoint.sh can be used for HW libraries * use real world example for generate_composed_library.sh * improve versions.txt explanation in generate_composed_library * add return type in python utils * fix versions file inference * use ggj 2.29 to fix ITs temporarily * disable asset due to licence year mismatch * improve commment in generate_composed_library * restore latest generator * remove wrong WORKSPACE comment * improve composed_library input comment * Update library_generation/utilities.py Co-authored-by: Blake Li <[email protected]> * remove postprocessing logic in generate_library * add generated_composed_library.py * use python script in IT * iterative fixes * ignore python cache * iterative fixes ii * Update library_generation/generate_composed_library.py Co-authored-by: Joe Wang <[email protected]> * Update library_generation/utilities.py Co-authored-by: Joe Wang <[email protected]> * Update library_generation/generate_composed_library.py Co-authored-by: Tomo Suzuki <[email protected]> * use underscores in configuration yaml * initial model for gapic config yaml * add requirements file * introduce yaml parsing logic * move parse logic to GenerationConfig * adapt composed_library script * fixes i * set IT to dummy mode * pass BUILD parse utils to production utils * fixes ii - constructor calls and composed_library arguments * fix destination path for partial generations * adapt IT to process individual libraries * use proper versions in configuration yaml * add rest of the libraries to integration test * add library_type to config yaml * reference to parent directory in compare_poms * handle script failures * use library-specific googleapis_commitish * fix protobuf version * install python reqs in Github Action * fix python version path logic * add python unit tests * remove obsolete py_util tests * add python unit tests in workflow * correct type hinting for Library * fix comments * set enable_postprocessing input to main.py to boolean * add explanation on library_type * remove old proto_path_list * fix comments in IT * remove WORKSPACE file usage * add IT configuration yaml for java-bigtable * fix config yaml for bigtable * finish tests for HW bigtable * install python in gh action, lint fix * update ggj to 2.32.0 * fix showcase test * remove commented line * use owlbot cli sha from config yaml * use python version from config yaml * use synthtool commitish from config yaml * add repository_path option * make destination_path required * add typing * use python version from config yaml * correct workflow indentation * fix workflow syntax * use repository_path when postprocessing * correct runs-on in workflow * use full path to repo in workflow * add debug output in workflow * decompose steps to compute python version * checout code in workflow * fix function name in workflow * use full path to obtain python version from yaml * use correct path in python version workflow * use set-output to share python version * fix set-output call * fix output setting in workflow * correct python version letter case * remove pyenv setup * fix repository_path * fix speech proto_path * do not wipe out google-cloud-java in IT * ensure correct version of python compares the poms * fix diff check * add return type for GenerationCOnfig.from_yaml * add missing python unit tests * use default values for enable_postprocessing * use is_monorepo var, constant for google-coud-java * fixes for local run * compute monorepo variable heuristically * update generation configs * remove python version * rename Library to LibraryConfig * rename GAPIC to GapicConfig * remove quotes from grpc version * move ClientInputs to model folder * parse BUILD file using ClientInputs * add unit tests for ClientInputs * fix CLientInputs typo * fix synthtool version * disable compute test * fix unit test * fix compute test * fix unit tests * remove BUILD parsing shell utilities * update monorepo special treatment comment * fix typo in shebang --------- Co-authored-by: Tomo Suzuki <[email protected]> Co-authored-by: Blake Li <[email protected]> Co-authored-by: Joe Wang <[email protected]>
1 parent a34d897 commit 363e35e

32 files changed

+1107
-618
lines changed

.github/workflows/verify_library_generation.yaml

+38-5
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
cache: maven
2626
- uses: actions/setup-python@v4
2727
with:
28-
python-version: '3.11'
28+
python-version: 3.11
2929
- name: install pyenv
3030
shell: bash
3131
run: |
@@ -36,10 +36,23 @@ jobs:
3636
export PATH="$PYENV_ROOT/bin:$PATH"
3737
echo "PYENV_ROOT=${PYENV_ROOT}" >> $GITHUB_ENV
3838
echo "PATH=${PATH}" >> $GITHUB_ENV
39-
# init pyenv
40-
eval "$(pyenv init --path)"
41-
eval "$(pyenv init -)"
39+
4240
set +ex
41+
- name: install python dependencies
42+
shell: bash
43+
run: |
44+
set -ex
45+
pushd library_generation
46+
pip install -r requirements.in
47+
popd
48+
49+
- name: install utils (macos)
50+
if: matrix.os == 'macos-12'
51+
shell: bash
52+
run: |
53+
brew update --preinstall
54+
# we need the `realpath` command to be available
55+
brew install coreutils
4356
- name: install docker (ubuntu)
4457
if: matrix.os == 'ubuntu-22.04'
4558
shell: bash
@@ -69,10 +82,30 @@ jobs:
6982
runs-on: ${{ matrix.os }}
7083
steps:
7184
- uses: actions/checkout@v3
72-
- name: Run unit tests
85+
- name: install utils (macos)
86+
if: matrix.os == 'macos-12'
87+
shell: bash
88+
run: |
89+
brew update --preinstall
90+
brew install coreutils
91+
- uses: actions/setup-python@v4
92+
with:
93+
python-version: 3.11
94+
- name: install python dependencies
95+
shell: bash
96+
run: |
97+
set -ex
98+
pushd library_generation
99+
pip install -r requirements.in
100+
popd
101+
- name: Run shell unit tests
73102
run: |
74103
set -x
75104
library_generation/test/generate_library_unit_tests.sh
105+
- name: Run python unit tests
106+
run: |
107+
set -x
108+
python -m unittest library_generation/test/unit_tests.py
76109
lint:
77110
runs-on: ubuntu-22.04
78111
steps:

library_generation/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__pycache__/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
"""
2+
This script allows generation of libraries that are composed of more than one
3+
service version. It is achieved by calling `generate_library.sh` without
4+
postprocessing for all service versions and then calling
5+
postprocess_library.sh at the end, once all libraries are ready.
6+
7+
Prerequisites
8+
- Needs a folder named `output` in current working directory. This folder
9+
is automatically detected by `generate_library.sh` and this script ensures it
10+
contains the necessary folders and files, specifically:
11+
- A "google" folder found in the googleapis/googleapis repository
12+
- A "grafeas" folder found in the googleapis/googleapis repository
13+
Note: googleapis repo is found in https://github.com/googleapis/googleapis.
14+
"""
15+
16+
import click
17+
import utilities as util
18+
import os
19+
import sys
20+
import subprocess
21+
import json
22+
from model.GenerationConfig import GenerationConfig
23+
from model.LibraryConfig import LibraryConfig
24+
from model.ClientInputs import parse as parse_build_file
25+
26+
script_dir = os.path.dirname(os.path.realpath(__file__))
27+
28+
"""
29+
Main function in charge of generating libraries composed of more than one
30+
service or service version.
31+
Arguments
32+
- config: a GenerationConfig object representing a parsed configuration
33+
yaml
34+
- library: a LibraryConfig object contained inside config, passed here for
35+
convenience and to prevent all libraries to be processed
36+
- enable_postprocessing: true if postprocessing should be done on the generated
37+
libraries
38+
- repository_path: path to the repository where the generated files will be
39+
sent. If not specified, it will default to the one defined in the configuration yaml
40+
and will be downloaded. The versions file will be inferred from this folder
41+
"""
42+
def generate_composed_library(
43+
config: GenerationConfig,
44+
library: LibraryConfig,
45+
repository_path: str,
46+
enable_postprocessing: bool = True,
47+
) -> None:
48+
output_folder = util.sh_util('get_output_folder')
49+
50+
print(f'output_folder: {output_folder}')
51+
print('library: ', library)
52+
os.makedirs(output_folder, exist_ok=True)
53+
54+
googleapis_commitish = config.googleapis_commitish
55+
if library.googleapis_commitish is not None:
56+
googleapis_commitish = library.googleapis_commitish
57+
print('using library-specific googleapis commitish: ' + googleapis_commitish)
58+
else:
59+
print('using common googleapis_commitish')
60+
61+
print('removing old googleapis folders and files')
62+
util.delete_if_exists(f'{output_folder}/google')
63+
util.delete_if_exists(f'{output_folder}/grafeas')
64+
65+
print('downloading googleapis')
66+
util.sh_util(f'download_googleapis_files_and_folders "{output_folder}" "{googleapis_commitish}"')
67+
68+
is_monorepo = len(config.libraries) > 1
69+
70+
base_arguments = []
71+
base_arguments += util.create_argument('gapic_generator_version', config)
72+
base_arguments += util.create_argument('grpc_version', config)
73+
base_arguments += util.create_argument('protobuf_version', config)
74+
75+
library_name = f'java-{library.api_shortname}'
76+
library_path = None
77+
78+
versions_file = ''
79+
if is_monorepo:
80+
print('this is a monorepo library')
81+
destination_path = config.destination_path + '/' + library_name
82+
library_folder = destination_path.split('/')[-1]
83+
if repository_path is None:
84+
print(f'sparse_cloning monorepo with {library_name}')
85+
repository_path = f'{output_folder}/{config.destination_path}'
86+
clone_out = util.sh_util(f'sparse_clone "https://github.com/googleapis/{MONOREPO_NAME}.git" "{library_folder} google-cloud-pom-parent google-cloud-jar-parent versions.txt .github"', cwd=output_folder)
87+
print(clone_out)
88+
library_path = f'{repository_path}/{library_name}'
89+
versions_file = f'{repository_path}/versions.txt'
90+
else:
91+
print('this is a HW library')
92+
destination_path = library_name
93+
if repository_path is None:
94+
repository_path = f'{output_folder}/{destination_path}'
95+
util.delete_if_exists(f'{output_folder}/{destination_path}')
96+
clone_out = util.sh_util(f'git clone "https://github.com/googleapis/{destination_path}.git"', cwd=output_folder)
97+
print(clone_out)
98+
library_path = f'{repository_path}'
99+
versions_file = f'{repository_path}/versions.txt'
100+
101+
owlbot_cli_source_folder = util.sh_util('mktemp -d')
102+
for gapic in library.gapic_configs:
103+
104+
effective_arguments = list(base_arguments)
105+
effective_arguments += util.create_argument('proto_path', gapic)
106+
107+
build_file_folder = f'{output_folder}/{gapic.proto_path}'
108+
print(f'build_file_folder: {build_file_folder}')
109+
client_inputs = parse_build_file(build_file_folder, gapic.proto_path)
110+
effective_arguments += [
111+
'--proto_only', client_inputs.proto_only,
112+
'--gapic_additional_protos', client_inputs.additional_protos,
113+
'--transport', client_inputs.transport,
114+
'--rest_numeric_enums', client_inputs.rest_numeric_enum,
115+
'--gapic_yaml', client_inputs.gapic_yaml,
116+
'--service_config', client_inputs.service_config,
117+
'--service_yaml', client_inputs.service_yaml,
118+
'--include_samples', client_inputs.include_samples,
119+
]
120+
service_version = gapic.proto_path.split('/')[-1]
121+
temp_destination_path = f'java-{library.api_shortname}-{service_version}'
122+
effective_arguments += [ '--destination_path', temp_destination_path ]
123+
print('arguments: ')
124+
print(effective_arguments)
125+
print(f'Generating library from {gapic.proto_path} to {destination_path}...')
126+
util.run_process_and_print_output(['bash', '-x', f'{script_dir}/generate_library.sh',
127+
*effective_arguments], 'Library generation')
128+
129+
130+
if enable_postprocessing:
131+
util.sh_util(f'build_owlbot_cli_source_folder "{library_path}"'
132+
+ f' "{owlbot_cli_source_folder}" "{output_folder}/{temp_destination_path}"'
133+
+ f' "{gapic.proto_path}"',
134+
cwd=output_folder)
135+
136+
if enable_postprocessing:
137+
# call postprocess library
138+
util.run_process_and_print_output([f'{script_dir}/postprocess_library.sh',
139+
f'{library_path}', '', versions_file, owlbot_cli_source_folder,
140+
config.owlbot_cli_image, config.synthtool_commitish, str(is_monorepo).lower()], 'Library postprocessing')
141+

library_generation/generate_library.sh

+8-42
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,10 @@ case $key in
6060
include_samples="$2"
6161
shift
6262
;;
63-
--enable_postprocessing)
64-
enable_postprocessing="$2"
65-
shift
66-
;;
6763
--os_architecture)
6864
os_architecture="$2"
6965
shift
7066
;;
71-
--versions_file)
72-
versions_file="$2"
73-
shift
74-
;;
7567
*)
7668
echo "Invalid option: [$1]"
7769
exit 1
@@ -85,6 +77,11 @@ script_dir=$(dirname "$(readlink -f "$0")")
8577
source "${script_dir}"/utilities.sh
8678
output_folder="$(get_output_folder)"
8779

80+
if [ -z "${gapic_generator_version}" ]; then
81+
echo 'missing required argument --gapic_generator_version'
82+
exit 1
83+
fi
84+
8885
if [ -z "${protobuf_version}" ]; then
8986
protobuf_version=$(get_protobuf_version "${gapic_generator_version}")
9087
fi
@@ -125,10 +122,6 @@ if [ -z "${include_samples}" ]; then
125122
include_samples="true"
126123
fi
127124

128-
if [ -z "$enable_postprocessing" ]; then
129-
enable_postprocessing="true"
130-
fi
131-
132125
if [ -z "${os_architecture}" ]; then
133126
os_architecture=$(detect_os_architecture)
134127
fi
@@ -305,34 +298,7 @@ popd # output_folder
305298
pushd "${temp_destination_path}"
306299
rm -rf java_gapic_srcjar java_gapic_srcjar_raw.srcjar.zip java_grpc.jar java_proto.jar temp-codegen.srcjar
307300
popd # destination path
308-
##################### Section 5 #####################
309-
# post-processing
310-
#####################################################
311-
if [ "${enable_postprocessing}" != "true" ];
312-
then
313-
echo "post processing is disabled"
314-
cp -r ${temp_destination_path}/* "${output_folder}/${destination_path}"
315-
rm -rdf "${temp_destination_path}"
316-
exit 0
317-
fi
318-
if [ -z "${versions_file}" ];then
319-
echo "no versions.txt argument provided. Please provide one in order to enable post-processing"
320-
exit 1
321-
fi
322-
workspace="${output_folder}/workspace"
323-
if [ -d "${workspace}" ]; then
324-
rm -rdf "${workspace}"
325-
fi
326-
327-
mkdir -p "${workspace}"
328-
329-
# if destination_path is not empty, it will be used as a starting workspace for
330-
# postprocessing
331-
if [[ $(find "${output_folder}/${destination_path}" -mindepth 1 -maxdepth 1 -type d,f | wc -l) -gt 0 ]];then
332-
workspace="${output_folder}/${destination_path}"
333-
fi
334-
335-
bash -x "${script_dir}/postprocess_library.sh" "${workspace}" \
336-
"${temp_destination_path}" \
337-
"${versions_file}"
338301

302+
cp -r ${temp_destination_path}/* "${output_folder}/${destination_path}"
303+
rm -rdf "${temp_destination_path}"
304+
exit 0

library_generation/main.py

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
"""
2+
Parses a config yaml and generates libraries via generate_composed_library.py
3+
"""
4+
5+
import click
6+
from generate_composed_library import generate_composed_library
7+
from typing import Dict
8+
from model.GenerationConfig import GenerationConfig
9+
from collections.abc import Sequence
10+
from absl import app
11+
12+
@click.group(invoke_without_command=False)
13+
@click.pass_context
14+
@click.version_option(message="%(version)s")
15+
def main(ctx):
16+
pass
17+
18+
@main.command()
19+
@click.option(
20+
"--generation-config-yaml",
21+
required=True,
22+
type=str,
23+
help="""
24+
Path to generation_config.yaml that contains the metadata about library generation
25+
"""
26+
)
27+
@click.option(
28+
"--enable-postprocessing",
29+
required=False,
30+
default=True,
31+
type=bool,
32+
help="""
33+
Path to repository where generated files will be merged into, via owlbot copy-code.
34+
Specifying this option enables postprocessing
35+
"""
36+
)
37+
@click.option(
38+
"--target-library-api-shortname",
39+
required=False,
40+
type=str,
41+
help="""
42+
If specified, only the `library` with api_shortname = target-library-api-shortname will
43+
be generated. If not specified, all libraries in the configuration yaml will be generated
44+
"""
45+
)
46+
@click.option(
47+
"--repository-path",
48+
required=False,
49+
type=str,
50+
help="""
51+
If specified, the generated files will be sent to this location. If not specified, the
52+
repository will be pulled into output_folder and move the generated files there
53+
"""
54+
)
55+
def generate_from_yaml(
56+
generation_config_yaml: str,
57+
enable_postprocessing: bool,
58+
target_library_api_shortname: str,
59+
repository_path: str
60+
) -> None:
61+
config = GenerationConfig.from_yaml(generation_config_yaml)
62+
target_libraries = config.libraries
63+
if target_library_api_shortname is not None:
64+
target_libraries = [library for library in config.libraries
65+
if library.api_shortname == target_library_api_shortname]
66+
for library in target_libraries:
67+
print(f'generating library {library.api_shortname}')
68+
generate_composed_library(
69+
config, library, repository_path, enable_postprocessing
70+
)
71+
72+
73+
74+
75+
76+
if __name__ == "__main__":
77+
main()

0 commit comments

Comments
 (0)