Skip to content

Commit 99bb2b3

Browse files
authored
feat: get released version from versions.txt to render README.md (#3007)
In this PR: - get library version from `versions.txt` to render `README.md`. - set library version as an env variable to post processor. - add `distribution_name` checker in `LibraryConfig`.
1 parent d996c2d commit 99bb2b3

File tree

12 files changed

+217
-35
lines changed

12 files changed

+217
-35
lines changed

.cloudbuild/library_generation/library_generation.Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ FROM gcr.io/cloud-devrel-public-resources/python
1717

1818
SHELL [ "/bin/bash", "-c" ]
1919

20-
ARG SYNTHTOOL_COMMITTISH=e36d2f164ca698f0264fb6f79ddc4b0fa024a940
20+
ARG SYNTHTOOL_COMMITTISH=696c4bff721f5541cd75fdc97d413f8f39d2a2c1
2121
ARG OWLBOT_CLI_COMMITTISH=ac84fa5c423a0069bbce3d2d869c9730c8fdf550
2222
ARG PROTOC_VERSION=25.3
2323
ENV HOME=/home

library_generation/generate_composed_library.py

+9-5
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from library_generation.model.gapic_inputs import GapicInputs
3737
from library_generation.model.library_config import LibraryConfig
3838
from library_generation.model.gapic_inputs import parse as parse_build_file
39+
from library_generation.model.repo_config import RepoConfig
3940

4041
script_dir = os.path.dirname(os.path.realpath(__file__))
4142

@@ -44,8 +45,7 @@ def generate_composed_library(
4445
config: GenerationConfig,
4546
library_path: str,
4647
library: LibraryConfig,
47-
output_folder: str,
48-
versions_file: str,
48+
repo_config: RepoConfig,
4949
) -> None:
5050
"""
5151
Generate libraries composed of more than one service or service version
@@ -55,10 +55,10 @@ def generate_composed_library(
5555
:param library_path: the path to which the generated file goes
5656
:param library: a LibraryConfig object contained inside config, passed here
5757
for convenience and to prevent all libraries to be processed
58-
:param output_folder: the folder to where tools go
59-
:param versions_file: the file containing version of libraries
58+
:param repo_config:
6059
:return None
6160
"""
61+
output_folder = repo_config.output_folder
6262
util.pull_api_definition(
6363
config=config, library=library, output_folder=output_folder
6464
)
@@ -102,16 +102,20 @@ def generate_composed_library(
102102
cwd=output_folder,
103103
)
104104

105+
library_version = repo_config.get_library_version(
106+
artifact_id=library.get_artifact_id()
107+
)
105108
# call postprocess library
106109
util.run_process_and_print_output(
107110
[
108111
f"{script_dir}/postprocess_library.sh",
109112
f"{library_path}",
110113
"",
111-
versions_file,
114+
repo_config.versions_file,
112115
owlbot_cli_source_folder,
113116
str(config.is_monorepo()).lower(),
114117
config.libraries_bom_version,
118+
library_version,
115119
],
116120
"Library postprocessing",
117121
)

library_generation/generate_repo.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,13 @@ def generate_from_yaml(
4444
gen_config=config, library_config=target_libraries, repo_path=repository_path
4545
)
4646

47-
for library_path, library in repo_config.libraries.items():
47+
for library_path, library in repo_config.get_libraries().items():
4848
print(f"generating library {library.get_library_name()}")
4949
generate_composed_library(
5050
config=config,
5151
library_path=library_path,
5252
library=library,
53-
output_folder=repo_config.output_folder,
54-
versions_file=repo_config.versions_file,
53+
repo_config=repo_config,
5554
)
5655

5756
if not config.is_monorepo() or config.contains_common_protos():

library_generation/model/library_config.py

+32-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
from hashlib import sha256
16-
1716
from typing import Optional
1817
from library_generation.model.gapic_config import GapicConfig
1918

2019

20+
MAVEN_COORDINATE_SEPARATOR = ":"
21+
22+
2123
class LibraryConfig:
2224
"""
2325
Class that represents a library in a generation_config.yaml file
@@ -64,7 +66,6 @@ def __init__(
6466
self.excluded_dependencies = excluded_dependencies
6567
self.excluded_poms = excluded_poms
6668
self.client_documentation = client_documentation
67-
self.distribution_name = distribution_name
6869
self.googleapis_commitish = googleapis_commitish
6970
self.group_id = group_id
7071
self.issue_tracker = issue_tracker
@@ -76,6 +77,7 @@ def __init__(
7677
self.extra_versioned_modules = extra_versioned_modules
7778
self.recommended_package = recommended_package
7879
self.min_java_version = min_java_version
80+
self.distribution_name = self.__get_distribution_name(distribution_name)
7981

8082
def get_library_name(self) -> str:
8183
"""
@@ -87,6 +89,34 @@ def get_library_name(self) -> str:
8789
def get_sorted_gapic_configs(self) -> list[GapicConfig]:
8890
return sorted(self.gapic_configs)
8991

92+
def get_maven_coordinate(self) -> str:
93+
"""
94+
Returns the Maven coordinate (group_id:artifact_id) of the library
95+
"""
96+
return self.distribution_name
97+
98+
def get_artifact_id(self) -> str:
99+
"""
100+
Returns the artifact ID of the library
101+
"""
102+
return self.get_maven_coordinate().split(MAVEN_COORDINATE_SEPARATOR)[-1]
103+
104+
def __get_distribution_name(self, distribution_name: Optional[str]) -> str:
105+
LibraryConfig.__check_distribution_name(distribution_name)
106+
if distribution_name:
107+
return distribution_name
108+
cloud_prefix = "cloud-" if self.cloud_api else ""
109+
library_name = self.get_library_name()
110+
return f"{self.group_id}:google-{cloud_prefix}{library_name}"
111+
112+
@staticmethod
113+
def __check_distribution_name(distribution_name: str) -> None:
114+
if not distribution_name:
115+
return
116+
sections = distribution_name.split(MAVEN_COORDINATE_SEPARATOR)
117+
if len(sections) != 2:
118+
raise ValueError(f"{distribution_name} is not a valid distribution name.")
119+
90120
def __eq__(self, other):
91121
return (
92122
self.api_shortname == other.api_shortname

library_generation/model/repo_config.py

+38-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@
1212
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
15-
from typing import Dict
1615
from library_generation.model.library_config import LibraryConfig
1716

1817

18+
GRPC_PREFIX = "grpc-"
19+
PROTO_PREFIX = "proto-"
20+
NEW_CLIENT_VERSION = "0.0.0"
21+
22+
1923
class RepoConfig:
2024
"""
2125
Class that represents a generated repository
@@ -24,15 +28,47 @@ class RepoConfig:
2428
def __init__(
2529
self,
2630
output_folder: str,
27-
libraries: Dict[str, LibraryConfig],
31+
libraries: dict[str, LibraryConfig],
2832
versions_file: str,
2933
):
3034
"""
3135
Init a RepoConfig object
36+
3237
:param output_folder: the path to which the generated repo goes
3338
:param libraries: a mapping from library_path to LibraryConfig object
3439
:param versions_file: the path of versions.txt used in post-processing
3540
"""
3641
self.output_folder = output_folder
3742
self.libraries = libraries
3843
self.versions_file = versions_file
44+
self.library_versions = self.__parse_versions()
45+
46+
def get_libraries(self) -> dict[str, LibraryConfig]:
47+
return self.libraries
48+
49+
def get_library_version(self, artifact_id: str) -> str:
50+
"""
51+
Returns the version of a given artifact ID.
52+
If the artifact ID is not managed, i.e., a new client, returns `0.0.0`.
53+
54+
:param artifact_id: the Maven artifact ID.
55+
:return: the version of the artifact.
56+
"""
57+
return self.library_versions.get(artifact_id, NEW_CLIENT_VERSION)
58+
59+
def __parse_versions(self) -> dict[str, str]:
60+
library_versions = dict()
61+
with open(self.versions_file) as f:
62+
for line in f.readlines():
63+
sections = line.split(":")
64+
# skip comments and empty lines.
65+
if len(sections) != 3:
66+
continue
67+
artifact_id = sections[0]
68+
released_version = sections[1]
69+
if artifact_id.startswith(GRPC_PREFIX) or artifact_id.startswith(
70+
PROTO_PREFIX
71+
):
72+
continue
73+
library_versions[artifact_id] = released_version
74+
return library_versions

library_generation/owlbot/bin/entrypoint.sh

+4-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
# both to README and pom.xml files
2323
# 3: is_monorepo: whether we are postprocessing a monorepo
2424
# 4: libraries_bom_version: used to render the readme
25+
# 5: library_version: used to render the readme
2526

2627
# The scripts assumes the CWD is the folder where postprocessing is going to be
2728
# applied
@@ -31,7 +32,7 @@ scripts_root=$1
3132
versions_file=$2
3233
is_monorepo=$3
3334
libraries_bom_version=$4
34-
35+
library_version=$5
3536

3637
if [[ "${is_monorepo}" == "true" ]]; then
3738
mv owl-bot-staging/* temp
@@ -48,10 +49,12 @@ then
4849
# synthtool library considering the way owlbot.py files are written
4950
export SYNTHTOOL_TEMPLATES="${scripts_root}/owlbot/templates"
5051
export SYNTHTOOL_LIBRARIES_BOM_VERSION="${libraries_bom_version}"
52+
export SYNTHTOOL_LIBRARY_VERSION="${library_version}"
5153
# defaults to run owlbot.py
5254
python3 owlbot.py
5355
unset SYNTHTOOL_TEMPLATES
5456
unset SYNTHTOOL_LIBRARIES_BOM_VERSION
57+
unset SYNTHTOOL_LIBRARY_VERSION
5558
fi
5659
echo "...done"
5760

library_generation/owlbot/templates/java_library/README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{% set group_id = metadata['repo']['distribution_name'].split(':')|first -%}
22
{% set artifact_id = metadata['repo']['distribution_name'].split(':')|last -%}
3+
{% set version = metadata['library_version'] -%}
34
{% set repo_short = metadata['repo']['repo'].split('/')|last -%}
45

56
# Google {{ metadata['repo']['name_pretty'] }} Client for Java
@@ -71,7 +72,7 @@ If you are using Maven, add this to your pom.xml file:
7172
<dependency>
7273
<groupId>{{ group_id }}</groupId>
7374
<artifactId>{{ artifact_id }}</artifactId>
74-
<version>{{ metadata['latest_version'] }}</version>
75+
<version>{{ version }}</version>
7576
</dependency>
7677
{% endif -%}
7778
```
@@ -80,7 +81,7 @@ If you are using Maven, add this to your pom.xml file:
8081
If you are using Gradle 5.x or later, add this to your dependencies:
8182

8283
```Groovy
83-
implementation platform('com.google.cloud:libraries-bom:{{metadata['latest_bom_version']}}')
84+
implementation platform('com.google.cloud:libraries-bom:{{metadata['libraries_bom_version']}}')
8485
8586
implementation '{{ group_id }}:{{ artifact_id }}'
8687
```
@@ -89,13 +90,13 @@ implementation '{{ group_id }}:{{ artifact_id }}'
8990
If you are using Gradle without BOM, add this to your dependencies:
9091

9192
```Groovy
92-
implementation '{{ group_id }}:{{ artifact_id }}:{{ metadata['latest_version'] }}'
93+
implementation '{{ group_id }}:{{ artifact_id }}:{{ version }}'
9394
```
9495

9596
If you are using SBT, add this to your dependencies:
9697

9798
```Scala
98-
libraryDependencies += "{{ group_id }}" % "{{ artifact_id }}" % "{{ metadata['latest_version'] }}"
99+
libraryDependencies += "{{ group_id }}" % "{{ artifact_id }}" % "{{ version }}"
99100
```
100101
<!-- {x-version-update-end} -->
101102

@@ -264,7 +265,7 @@ Java is a registered trademark of Oracle and/or its affiliates.
264265
[kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/{{ repo_short }}/java11.html
265266
[stability-image]: https://img.shields.io/badge/stability-{% if metadata['repo']['release_level'] == 'stable' %}stable-green{% elif metadata['repo']['release_level'] == 'preview' %}preview-yellow{% else %}unknown-red{% endif %}
266267
[maven-version-image]: https://img.shields.io/maven-central/v/{{ group_id }}/{{ artifact_id }}.svg
267-
[maven-version-link]: https://central.sonatype.com/artifact/{{ group_id }}/{{ artifact_id }}/{{ metadata['latest_version'] }}
268+
[maven-version-link]: https://central.sonatype.com/artifact/{{ group_id }}/{{ artifact_id }}/{{ version }}
268269
[authentication]: https://github.com/googleapis/google-cloud-java#authentication
269270
[auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes
270271
[predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles

library_generation/postprocess_library.sh

+5-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
# different logic
2020
# 6 - libraries_bom_version: used by our implementation of owlbot to render the
2121
# readme
22+
# 7 - library_version: used by our implementation of owlbot to render the
23+
# readme
2224
set -exo pipefail
2325
scripts_root=$(dirname "$(readlink -f "$0")")
2426

@@ -28,6 +30,7 @@ versions_file=$3
2830
owlbot_cli_source_folder=$4
2931
is_monorepo=$5
3032
libraries_bom_version=$6
33+
library_version=$7
3134
owlbot_yaml_file_name=".OwlBot-hermetic.yaml"
3235

3336
source "${scripts_root}"/utils/utilities.sh
@@ -102,6 +105,7 @@ bash "${scripts_root}/owlbot/bin/entrypoint.sh" \
102105
"${scripts_root}" \
103106
"${versions_file}" \
104107
"${is_monorepo}" \
105-
"${libraries_bom_version}"
108+
"${libraries_bom_version}" \
109+
"${library_version}"
106110

107111
popd # postprocessing_target

library_generation/templates/owlbot.yaml.monorepo.j2

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
{% if artifact_name %}
15+
{% if artifact_id %}
1616
deep-remove-regex:
1717
- "/{{ module_name }}/grpc-google-.*/src"
1818
- "/{{ module_name }}/proto-google-.*/src"
@@ -24,11 +24,11 @@ deep-preserve-regex:
2424

2525
deep-copy-regex:
2626
- source: "/{{ proto_path }}/(v.*)/.*-java/proto-google-.*/src"
27-
dest: "/owl-bot-staging/{{ module_name }}/$1/proto-{{ artifact_name }}-$1/src"
27+
dest: "/owl-bot-staging/{{ module_name }}/$1/proto-{{ artifact_id }}-$1/src"
2828
- source: "/{{ proto_path }}/(v.*)/.*-java/grpc-google-.*/src"
29-
dest: "/owl-bot-staging/{{ module_name }}/$1/grpc-{{ artifact_name }}-$1/src"
29+
dest: "/owl-bot-staging/{{ module_name }}/$1/grpc-{{ artifact_id }}-$1/src"
3030
- source: "/{{ proto_path }}/(v.*)/.*-java/gapic-google-.*/src"
31-
dest: "/owl-bot-staging/{{ module_name }}/$1/{{ artifact_name }}/src"
31+
dest: "/owl-bot-staging/{{ module_name }}/$1/{{ artifact_id }}/src"
3232
- source: "/{{ proto_path }}/(v.*)/.*-java/samples/snippets/generated"
3333
dest: "/owl-bot-staging/{{ module_name }}/$1/samples/snippets/generated"
3434
{%- endif %}

library_generation/test/model/library_config_unit_tests.py

+56
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,59 @@ def test_get_sorted_gapic_configs_returns_correct_order(self):
6464
],
6565
library.get_sorted_gapic_configs(),
6666
)
67+
68+
def test_init_invalid_distribution_name_raise_value_error(self):
69+
self.assertRaisesRegex(
70+
ValueError,
71+
"fake-distribution-name is not a valid distribution name.",
72+
LibraryConfig,
73+
api_shortname="baremetalsolution",
74+
name_pretty="Bare Metal Solution",
75+
product_documentation="https://cloud.google.com/bare-metal/docs",
76+
api_description="example api description",
77+
gapic_configs=list(),
78+
distribution_name="fake-distribution-name",
79+
)
80+
81+
def test_get_distribution_name_cloud_api(self):
82+
library = LibraryConfig(
83+
api_shortname="baremetalsolution",
84+
name_pretty="Bare Metal Solution",
85+
product_documentation="https://cloud.google.com/bare-metal/docs",
86+
api_description="example api description",
87+
gapic_configs=list(),
88+
)
89+
self.assertEqual(
90+
"com.google.cloud:google-cloud-baremetalsolution",
91+
library.get_maven_coordinate(),
92+
)
93+
self.assertEqual("google-cloud-baremetalsolution", library.get_artifact_id())
94+
95+
def test_get_distribution_name_non_cloud_api(self):
96+
library = LibraryConfig(
97+
api_shortname="baremetalsolution",
98+
name_pretty="Bare Metal Solution",
99+
product_documentation="https://cloud.google.com/bare-metal/docs",
100+
api_description="example api description",
101+
gapic_configs=list(),
102+
cloud_api=False,
103+
group_id="com.example",
104+
)
105+
self.assertEqual(
106+
"com.example:google-baremetalsolution", library.get_maven_coordinate()
107+
)
108+
self.assertEqual("google-baremetalsolution", library.get_artifact_id())
109+
110+
def test_get_distribution_name_with_distribution_name(self):
111+
library = LibraryConfig(
112+
api_shortname="baremetalsolution",
113+
name_pretty="Bare Metal Solution",
114+
product_documentation="https://cloud.google.com/bare-metal/docs",
115+
api_description="example api description",
116+
gapic_configs=list(),
117+
distribution_name="com.example:baremetalsolution",
118+
)
119+
self.assertEqual(
120+
"com.example:baremetalsolution", library.get_maven_coordinate()
121+
)
122+
self.assertEqual("baremetalsolution", library.get_artifact_id())

0 commit comments

Comments
 (0)