Skip to content

Commit 3089ed5

Browse files
authored
Merge pull request #1511 from googleapis/feat/migrate-generator-no-dart
feat: migrate generator (excluding dart) from google-cloud-rust to internal/sidekick
2 parents 60554f6 + ebc27b6 commit 3089ed5

File tree

258 files changed

+40429
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

258 files changed

+40429
-0
lines changed

all_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ func TestHeaders(t *testing.T) {
7272
return nil
7373
case strings.HasSuffix(path, ".excalidraw"):
7474
return nil
75+
case strings.HasPrefix(path, "internal/sidekick"):
76+
// TODO(https://github.com/googleapis/librarian/issues/1510): fix
77+
// tests for sidekick and remove this case statement.
78+
return nil
7579
case slices.Contains(noHeaderRequiredFiles, path):
7680
return nil
7781
default:
@@ -154,6 +158,11 @@ func TestExportedSymbolsHaveDocs(t *testing.T) {
154158
strings.HasSuffix(path, "_test.go") || strings.HasSuffix(path, ".pb.go") {
155159
return nil
156160
}
161+
// TODO(https://github.com/googleapis/librarian/issues/1510): fix docs
162+
// for sidekick and remove
163+
if strings.HasPrefix(path, "internal/sidekick") {
164+
return nil
165+
}
157166

158167
fset := token.NewFileSet()
159168
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)

internal/sidekick/README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# sidekick: a tool to generate and maintain Google Cloud SDKs
2+
3+
`sidekick` automates (or will soon automate) most activities around generating
4+
and maintaining SDKs for Google Cloud.
5+
6+
## Example Run with Protobuf
7+
8+
You need to have `protoc` installed in your path. You can find useful links
9+
below.
10+
11+
This will generate the client library for [Secret Manager] in the
12+
`generator/testdata/rust/openapi/golden` directory. In future releases most
13+
options should be already configured in a `.sidekick.toml` file.
14+
15+
```bash
16+
cd generator
17+
go run cmd/sidekick/main.go generate -project-root=.. \
18+
-specification-format protobuf \
19+
-specification-source generator/testdata/googleapis/google/cloud/secretmanager/v1 \
20+
-service-config generator/testdata/googleapis/google/cloud/secretmanager/v1/secretmanager_v1.yaml \
21+
-source-option googleapis-root=generator/testdata/googleapis \
22+
-language rust \
23+
-output generator/testdata/rust/protobuf/golden/secretmanager \
24+
-codec-option package-name-override=secretmanager-golden-protobuf \
25+
-codec-option package:wkt=package=types,path=types,source=google.protobuf \
26+
-codec-option package:gax=package=gax,path=gax,feature=unstable-sdk-client \
27+
-codec-option package:iam=package=iam-v1-golden-protobuf,path=generator/testdata/rust/protobuf/golden/iam/v1,source=google.iam.v1
28+
```
29+
30+
## Example Run with OpenAPI
31+
32+
This will generate the client library for [Secret Manager] in the
33+
`generator/testdata/rust/openapi/golden` directory. In future releases most
34+
options should be already configured in a `.sidekick.toml` file.
35+
36+
```bash
37+
cd generator
38+
go run cmd/sidekick/main.go generate -project-root=.. \
39+
-specification-format openapi \
40+
-specification-source generator/testdata/openapi/secretmanager_openapi_v1.json \
41+
-service-config generator/testdata/googleapis/google/cloud/secretmanager/v1/secretmanager_v1.yaml \
42+
-language rust \
43+
-output generator/testdata/rust/openapi/golden \
44+
-codec-option package-name-override=secretmanager-golden-openapi \
45+
-codec-option package:wkt=package=types,path=types,source=google.protobuf \
46+
-codec-option package:gax=package=gax,path=gax,feature=unstable-sdk-client
47+
```
48+
49+
## Testing
50+
51+
From the repo root: `go -C generator/ test ./...`
52+
53+
Or from `generator/`: `go test ./...`
54+
55+
## Prerequisites
56+
57+
### Installing `protoc`: the Protobuf Compiler
58+
59+
The generator and its unit tests use `protoc`, the Protobuf compiler. Ensure you
60+
have `protoc >= v23.0` installed and it is found via your `$PATH`.
61+
62+
```bash
63+
protoc --version
64+
```
65+
66+
If not, follow the steps in [Protocol Buffer Compiler Installation] to download
67+
a suitable version.
68+
69+
### Install goimports
70+
71+
```shell
72+
go install golang.org/x/tools/cmd/goimports@latest
73+
```
74+
75+
[protocol buffer compiler installation]: https://protobuf.dev/installation/
76+
[secret manager]: https://cloud.google.com/secret-manager/
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"fmt"
19+
"os"
20+
21+
"github.com/googleapis/google-cloud-rust/generator/internal/sidekick"
22+
)
23+
24+
func main() {
25+
if err := sidekick.Run(os.Args[1:]); err != nil {
26+
fmt.Fprintf(os.Stderr, "%v\n", err)
27+
os.Exit(1)
28+
}
29+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2024 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package main
16+
17+
import (
18+
"flag"
19+
"os"
20+
"testing"
21+
)
22+
23+
func TestMain(m *testing.M) {
24+
flag.Parse()
25+
os.Exit(m.Run())
26+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# How-To Guide: Implementing a new Codec
2+
3+
This guide is intended for contributors to the `sidekick` project. It will walk
4+
you through the steps necessary to add a new `codec`. If you are just looking
5+
for instructions on how to use the `sidekick` consult the top-level README or
6+
the specific guide for your SDK.
7+
8+
## What is a `codec`
9+
10+
In the context of `sidekick`: a golang package that outputs the syntax tree for
11+
one specific "API". The most common case is to generate a client library (a
12+
GAPIC) for a specific SDK. As usual with software, the same abstraction can be
13+
used to output other things, like a text representation of the API syntax tree,
14+
or a CLI, or maybe a partial client library that is wrapped by a veneer.
15+
16+
The basic data flows is:
17+
18+
- Sidekick parses the source specification of an API (Protobuf or OpenAPIv3) and
19+
converts this into a language-neutral, source-neutral abstract syntax tree. We
20+
often call this syntax tree "the model".
21+
- Sidekick then calls the `Generate()` function in a codec, and passes the model
22+
(as a `api.API` data structure, and some configuration options).
23+
- The codec annotates the syntax tree. Each node in the tree has a `Codec`
24+
field. The codec is expected to put its own data structure in that node.
25+
- The code then loads a number of mustache templates and calls these templates
26+
to output the `api.API` data structure as described by these templates.
27+
28+
## What are the contents of an `api.API`?
29+
30+
The elements to be generated are in `api.API.Messages`, `api.API.Enums` and
31+
`api.API.Services`. Note that `Services` is a list, and typically there may be
32+
more than one service (in the "gRPC service" sense in the same `api.API`
33+
invocation).
34+
35+
Sidekick requires that all the elements in an `api.API` are in the same
36+
namespace. That is, all the elements in the generation lists must have the same
37+
`.Package` value in their field, and none may have the same `.ID` field.
38+
39+
This is trivial for OpenAPI. For Protobuf this implies all the root messages,
40+
root enums, and services must be part of the same package. If you need to
41+
generate multiple packages that will require different calls to `Generate()`.
42+
43+
To support mixins, `api.API` may reference messages, enums and and services.
44+
These are found in the `api.API.State.*ByID` maps.
45+
46+
## How does sidekick gets its configuration?
47+
48+
In a SDK repository the client libraries are generated in separate directories.
49+
Each directory contains a `.sidekick.toml` file. This file describes where to
50+
find the source for the client library, and may include some extra configuration
51+
for the codec. The top-level directory contains a `.sidekick.toml` configuration
52+
file which applies to all the generated client libraries.
53+
54+
## How is `api.API` built?
55+
56+
The `.sidekick.toml` file describes what parser to use for that directory, and
57+
where to find the source. The most common parser is `protobuf`. This parser
58+
typically receives a directory as the source, the parser uses all the `.proto`
59+
files in that directory (without recursion). It is also possible to list
60+
specific files, or to exclude some files.
61+
62+
The parser calls `protoc`, reads the resulting proto descriptors and the service
63+
config YAML, and then converts that into a `api.API` instance. This is the input
64+
into your codec.
65+
66+
## Implementing a `codec`
67+
68+
First, add a module, something like this would do:
69+
70+
```shell
71+
ls -l generator/internal/codec_sample
72+
```
73+
74+
The main entry point in that module is the `Generate()` function, found in
75+
`generate.go`:
76+
77+
```shell
78+
cat generator/internal/codec_sample/generate.go
79+
```
80+
81+
This module has a single (and relatively simple) template file:
82+
83+
```shell
84+
cat generator/internal/codec_sample/templates/readme/README.md.mustache
85+
```
86+
87+
The codec is invoked from a single point in sidekick:
88+
89+
```shell
90+
git grep -A 2 -B 2 codec_sample.Generate
91+
```
92+
93+
## Basic integration tests
94+
95+
A simple integration test for this module is found in:
96+
97+
```shell
98+
cat generator/internal/sidekick/sidekick_sample_test.go
99+
```
100+
101+
## Unit tests
102+
103+
You can write tests for the codec as usual. There are some helpers to initialize
104+
the `api.API` data structure. Look at the other codecs for examples.
105+
106+
## Annotations
107+
108+
Eventually your codec will need to add annotations to the `api.API` structure. A
109+
simple annotation may be a boolean indicating if an API has any services. If you
110+
needed such an annotation you would write your own `annotate()` function:
111+
112+
```go
113+
type modelAnnotation {
114+
HasServices bool
115+
}
116+
117+
func annotate(model *api.API) {
118+
model.Codec = &modelAnnotation{
119+
HasServices: len(model.Services) > 0
120+
}
121+
}
122+
```
123+
124+
You would need to call this function from your `Generate()` function. Note that
125+
only the `.Codec` field is modified, and that the type is of your own choosing.
126+
127+
In a more interesting codec you would iterate over the `model` data structure
128+
and set the `.Codec` field with annotations about methods, messages, enums, and
129+
so forth. Look at the other codecs for examples. The most common annotations are
130+
the names of the generated elements, as these often differ in non-trivial ways
131+
from the source name.

internal/sidekick/go.mod

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
module github.com/googleapis/google-cloud-rust/generator
2+
3+
go 1.23.6
4+
5+
require (
6+
cloud.google.com/go/iam v1.5.2
7+
cloud.google.com/go/longrunning v0.6.7
8+
github.com/cbroglie/mustache v1.4.0
9+
github.com/ghodss/yaml v1.0.0
10+
github.com/google/go-cmp v0.7.0
11+
github.com/iancoleman/strcase v0.3.0
12+
github.com/pb33f/libopenapi v0.23.0
13+
github.com/pelletier/go-toml/v2 v2.2.4
14+
github.com/walle/targz v0.0.0-20140417120357-57fe4206da5a
15+
github.com/yuin/goldmark v1.7.12
16+
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb
17+
google.golang.org/genproto/googleapis/api v0.0.0-20250414145226-207652e42e2e
18+
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e
19+
google.golang.org/protobuf v1.36.6
20+
gopkg.in/yaml.v3 v3.0.1
21+
)
22+
23+
require (
24+
github.com/bahlo/generic-list-go v0.2.0 // indirect
25+
github.com/buger/jsonparser v1.1.1 // indirect
26+
github.com/kr/pretty v0.3.1 // indirect
27+
github.com/mailru/easyjson v0.9.0 // indirect
28+
github.com/rogpeppe/go-internal v1.13.1 // indirect
29+
github.com/speakeasy-api/jsonpath v0.6.2 // indirect
30+
github.com/wk8/go-ordered-map/v2 v2.1.9-0.20240815153524-6ea36470d1bd // indirect
31+
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
32+
golang.org/x/net v0.39.0 // indirect
33+
golang.org/x/sys v0.32.0 // indirect
34+
golang.org/x/text v0.24.0 // indirect
35+
google.golang.org/grpc v1.71.1 // indirect
36+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
37+
gopkg.in/yaml.v2 v2.4.0 // indirect
38+
)

0 commit comments

Comments
 (0)