-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add script to help bootstrap new client libraries #4111
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
Merged
Merged
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
d5631cd
Add new client python script
chingor13 f8df942
Merge branch 'master' into new-client-script
chingor13 0eb718a
Add requirements.txt, convert to use attrs
chingor13 00116c0
Start new artifact versions at 0.0.0-alpha. Next release will bump ve…
chingor13 2a68d9d
Switch to pathlib
chingor13 8614901
Move main execution into main()
chingor13 4da03b5
Standardize quotes to double-quotes
chingor13 f5a869c
Update docstrings
chingor13 e20d186
Merge branch 'master' into new-client-script
chingor13 499ec21
Fix Path vs. string types when reading/writing files
chingor13 5333a89
Update synth.py template to include the java formatter
chingor13 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,303 @@ | ||
| #!/usr/bin/env python3 | ||
| # Copyright 2018 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 argparse | ||
| import os | ||
| import pathlib | ||
| import re | ||
| import sys | ||
| import subprocess | ||
|
|
||
| import attr | ||
| from jinja2 import Environment, FileSystemLoader | ||
| from lxml import etree | ||
| from releasetool.commands.start import java as releasetool | ||
| from typing import List | ||
|
|
||
| @attr.s(auto_attribs=True) | ||
| class Context: | ||
chingor13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| service: str = None | ||
| api_version: str = None | ||
| artman_config: str = None | ||
| google_cloud_artifact: str = None | ||
| google_cloud_version: releasetool.Version = None | ||
| grpc_artifact: str = None | ||
| grpc_version: releasetool.Version = None | ||
| proto_artifact: str = None | ||
| proto_version: releasetool.Version = None | ||
| root_directory: pathlib.Path = \ | ||
| pathlib.Path(os.path.realpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))) | ||
| description: str = "FIXME" | ||
| name: str = "FIXME" | ||
| versions: List[str] = None | ||
| jinja_env: Environment = None | ||
|
|
||
| def __attrs_post_init__(self): | ||
| self.google_cloud_artifact = f"google-cloud-{self.service}" | ||
| self.grpc_artifact = f"grpc-google-cloud-{self.service}-{self.api_version}" | ||
| self.proto_artifact = f"proto-google-cloud-{self.service}-{self.api_version}" | ||
| self.jinja_env = Environment( | ||
| loader=FileSystemLoader(str(self.root_directory / "utilities/templates")) | ||
| ) | ||
|
|
||
| def add_to_versions(ctx: Context) -> None: | ||
chingor13 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| """Add the new artifact's versions to the versions.txt manifest.""" | ||
| versions = [] | ||
|
|
||
| # read from versions.txt | ||
| versions_path = ctx.root_directory / "versions.txt" | ||
| with open(versions_path) as f: | ||
| for line in f: | ||
| version_line = line.strip() | ||
| if not version_line or version_line.startswith("#"): | ||
| continue | ||
|
|
||
| versions.append(releasetool.ArtifactVersions(version_line)) | ||
|
|
||
| # Add new versions unless the artifacts already exist in the versions.txt manifest | ||
| ctx.google_cloud_version = next((v for v in versions if v.module == ctx.google_cloud_artifact), None) | ||
| if not ctx.google_cloud_version: | ||
| ctx.google_cloud_version = releasetool.ArtifactVersions(f"{ctx.google_cloud_artifact}:0.0.0-alpha:0.0.1-alpha-SNAPSHOT") | ||
| versions.append(ctx.google_cloud_version) | ||
|
|
||
| ctx.proto_version = next((v for v in versions if v.module == ctx.proto_artifact), None) | ||
| if not ctx.proto_version: | ||
| ctx.proto_version = releasetool.ArtifactVersions(f"{ctx.proto_artifact}:0.0.0-alpha:0.0.1-alpha-SNAPSHOT") | ||
| versions.append(ctx.proto_version) | ||
|
|
||
| ctx.grpc_version = next((v for v in versions if v.module == ctx.grpc_artifact), None) | ||
| if not ctx.grpc_version: | ||
| ctx.grpc_version = releasetool.ArtifactVersions(f"{ctx.grpc_artifact}:0.0.0-alpha:0.0.1-alpha-SNAPSHOT") | ||
| versions.append(ctx.grpc_version) | ||
|
|
||
| # sort by name | ||
| versions.sort(key=lambda v: v.module) | ||
|
|
||
| # update versions.txt | ||
| with open(versions_path, "w") as f: | ||
| f.write("# Format:\n") | ||
| f.write("# module:released-version:current-version\n\n") | ||
| for version in versions: | ||
| f.write("{}\n".format(version)) | ||
|
|
||
| ctx.versions = versions | ||
|
|
||
| def add_module_to_pom(pom: pathlib.Path, module_name: str) -> None: | ||
| """ | ||
| Insert a new <module>name</module> into a pom.xml file. | ||
|
|
||
| This assumes that the modules are in alphabetical order and inserts it | ||
| in the correct place | ||
| """ | ||
| tree = etree.parse(str(pom)) | ||
| root = tree.getroot() | ||
| modules = root.find("{http://maven.apache.org/POM/4.0.0}modules") | ||
|
|
||
| new_module = etree.Element("{http://maven.apache.org/POM/4.0.0}module") | ||
| new_module.text = module_name | ||
|
|
||
| for i, module in enumerate(modules): | ||
| if module.text == module_name: | ||
| # print("already added to pom.xml, skipping") | ||
| return | ||
|
|
||
| if module.text > module_name: | ||
| modules.insert(i, new_module) | ||
| break | ||
|
|
||
| tree.write(str(pom), pretty_print=True, xml_declaration=True, encoding="utf-8") | ||
|
|
||
| def add_dependency_management_to_pom(pom: pathlib.Path, group: str, artifact: str, version: str) -> None: | ||
| """Adds a <dependency>...</depedency> section into a pom.xml file.""" | ||
| tree = etree.parse(str(pom)) | ||
| root = tree.getroot() | ||
| dependencies = root.find("{http://maven.apache.org/POM/4.0.0}dependencyManagement/{http://maven.apache.org/POM/4.0.0}dependencies") | ||
|
|
||
| for dependency in dependencies: | ||
| existing = dependency.find("{http://maven.apache.org/POM/4.0.0}artifactId") | ||
| if existing is not None: | ||
| if existing.text == artifact: | ||
| print("already added dependency to pom.xml, skipping") | ||
| return | ||
|
|
||
| new_dependency = etree.Element("{http://maven.apache.org/POM/4.0.0}dependency") | ||
| group_element = etree.Element("{http://maven.apache.org/POM/4.0.0}groupId") | ||
| group_element.text = group | ||
| new_dependency.append(group_element) | ||
| artifact_element = etree.Element("{http://maven.apache.org/POM/4.0.0}artifactId") | ||
| artifact_element.text = artifact | ||
| new_dependency.append(artifact_element) | ||
| version_element = etree.Element("{http://maven.apache.org/POM/4.0.0}version") | ||
| version_element.text = version | ||
| new_dependency.append(version_element) | ||
| comment = etree.Comment("{x-version-update:" + artifact + ":current}") | ||
| new_dependency.append(comment) | ||
| dependencies.append(new_dependency) | ||
|
|
||
| tree.write(str(pom), pretty_print=True, xml_declaration=True, encoding="utf-8") | ||
|
|
||
|
|
||
| def write_synthfile(ctx: Context) -> None: | ||
| """Creates a synth.py file from a template.""" | ||
| template = ctx.jinja_env.get_template("synth.py") | ||
| synth = template.stream( | ||
| version=ctx.api_version, | ||
| service=ctx.service, | ||
| config_path=ctx.artman_config, | ||
| ) | ||
| path = ctx.root_directory / "google-cloud-clients" / ctx.google_cloud_artifact / "synth.py" | ||
| directory = os.path.dirname(path) | ||
| if not os.path.isdir(directory): | ||
| os.makedirs(directory) | ||
| synth.dump(str(path)) | ||
|
|
||
| def write_pom(template: str, path: str, ctx: Context, version: str) -> None: | ||
| """Creates a pom.xml file from a template.""" | ||
| template = ctx.jinja_env.get_template(template) | ||
| pom = template.stream( | ||
| api_version=ctx.api_version, | ||
| description=ctx.description, | ||
| name=ctx.name, | ||
| service=ctx.service, | ||
| version=version | ||
| ) | ||
| directory = os.path.dirname(path) | ||
| if not os.path.isdir(directory): | ||
| os.makedirs(directory) | ||
| pom.dump(str(path)) | ||
|
|
||
| def run_synthtool(ctx: Context) -> None: | ||
| """Runs synthtool for the initial client generation.""" | ||
| subprocess.run( | ||
| [sys.executable, "synth.py"], | ||
| check=True, | ||
| cwd=ctx.root_directory / "google-cloud-clients" / ctx.google_cloud_artifact | ||
| ) | ||
|
|
||
| def update_stub_packages(ctx: Context) -> None: | ||
| """Updates the pom.xml documentation section to scrub certain packages.""" | ||
| # open google-cloud-clients/pom.xml and fix the Stub packages list | ||
| pom = ctx.root_directory / "google-cloud-clients/pom.xml" | ||
| tree = etree.parse(str(pom)) | ||
|
|
||
| grpc_artifacts = [v.module for v in ctx.versions if v.module.startswith("grpc-")] | ||
| stub_classes = [] | ||
| for artifact in grpc_artifacts: | ||
| m = re.match("grpc-google-cloud-(.*)-(v.*)", artifact) | ||
| stub_classes.append(f"com.google.cloud.{m[1]}.{m[2]}.stub") | ||
|
|
||
| for group in tree.findall(".//{http://maven.apache.org/POM/4.0.0}group"): | ||
| if group.find("{http://maven.apache.org/POM/4.0.0}title").text == "Stub packages": | ||
| group.find("{http://maven.apache.org/POM/4.0.0}packages").text = ":".join(stub_classes) | ||
|
|
||
| tree.write(str(pom), pretty_print=True, xml_declaration=True, encoding="utf-8") | ||
|
|
||
| def write_readme(ctx: Context) -> None: | ||
| """Creates a README.md from a template.""" | ||
| template = ctx.jinja_env.get_template("README.md") | ||
| pom = template.stream( | ||
| api_version=ctx.api_version, | ||
| description=ctx.description, | ||
| name=ctx.name, | ||
| service=ctx.service, | ||
| version=ctx.google_cloud_version | ||
| ) | ||
| path = ctx.root_directory / "google-cloud-clients" / ctx.google_cloud_artifact / "README.md" | ||
| directory = os.path.dirname(path) | ||
| if not os.path.isdir(directory): | ||
| os.makedirs(directory) | ||
| pom.dump(str(path)) | ||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser(description="Create a new client") | ||
| parser.add_argument("-v", required=True, help="API version (i.e. v1)") | ||
| parser.add_argument("-c", required=True, help="Path to config in googleapis/googleapis") | ||
| parser.add_argument("-s", required=True, help="Service name") | ||
| args = parser.parse_args() | ||
|
|
||
| ctx = Context( | ||
| api_version=args.v, | ||
| service=args.s, | ||
| artman_config=args.c | ||
| ) | ||
|
|
||
| add_to_versions(ctx) | ||
| write_synthfile(ctx) | ||
| run_synthtool(ctx) | ||
| write_pom( | ||
| ctx=ctx, | ||
| template="cloud_pom.xml", | ||
| path=ctx.root_directory / "google-cloud-clients" / ctx.google_cloud_artifact / "pom.xml", | ||
| version=ctx.google_cloud_version.current | ||
| ) | ||
| add_module_to_pom( | ||
| pom=ctx.root_directory / "google-cloud-clients/pom.xml", | ||
| module_name="google-cloud-iamcredentials" | ||
| ) | ||
| add_dependency_management_to_pom( | ||
| pom=ctx.root_directory / "google-api-grpc/pom.xml", | ||
| group="com.google.api.grpc", | ||
| artifact=ctx.proto_artifact, | ||
| version=str(ctx.proto_version.current) | ||
| ) | ||
| add_dependency_management_to_pom( | ||
| pom=ctx.root_directory / "google-api-grpc/pom.xml", | ||
| group="com.google.api.grpc", | ||
| artifact=ctx.grpc_artifact, | ||
| version=str(ctx.grpc_version.current) | ||
| ) | ||
| add_dependency_management_to_pom( | ||
| pom=ctx.root_directory / "google-cloud-bom/pom.xml", | ||
| group="com.google.api.grpc", | ||
| artifact=ctx.proto_artifact, | ||
| version=str(ctx.proto_version.current) | ||
| ) | ||
| add_dependency_management_to_pom( | ||
| pom=ctx.root_directory / "google-cloud-bom/pom.xml", | ||
| group="com.google.api.grpc", | ||
| artifact=ctx.grpc_artifact, | ||
| version=str(ctx.grpc_version.current) | ||
| ) | ||
| add_dependency_management_to_pom( | ||
| pom=ctx.root_directory / "google-cloud-bom/pom.xml", | ||
| group="com.google.cloud", | ||
| artifact=ctx.google_cloud_artifact, | ||
| version=str(ctx.google_cloud_version.current) | ||
| ) | ||
| write_pom( | ||
| ctx=ctx, | ||
| template="proto_pom.xml", | ||
| path=ctx.root_directory / "google-api-grpc" / ctx.proto_artifact / "pom.xml", | ||
| version=ctx.proto_version.current | ||
| ) | ||
| write_pom( | ||
| ctx=ctx, | ||
| template="grpc_pom.xml", | ||
| path=ctx.root_directory / "google-api-grpc" / ctx.grpc_artifact / "pom.xml", | ||
| version=ctx.grpc_version.current | ||
| ) | ||
| add_module_to_pom( | ||
| pom=ctx.root_directory / "google-api-grpc/pom.xml", | ||
| module_name=ctx.grpc_artifact | ||
| ) | ||
| add_module_to_pom( | ||
| pom=ctx.root_directory / "google-api-grpc/pom.xml", | ||
| module_name=ctx.proto_artifact | ||
| ) | ||
| update_stub_packages(ctx) | ||
| write_readme(ctx) | ||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| attr | ||
| gcp-releasetool | ||
| jinja2 | ||
| lxml~=4.2 | ||
| typing~=3.6 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.