Skip to content

Commit cfe8f40

Browse files
authored
feat(internal/librarian/golang): add README generation (#3898)
Generate a README.md file for each Go client library module. For #3617
1 parent 887ac38 commit cfe8f40

3 files changed

Lines changed: 134 additions & 0 deletions

File tree

internal/librarian/golang/generate.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,25 @@ package golang
1717

1818
import (
1919
"context"
20+
_ "embed"
2021
"fmt"
2122
"os"
2223
"path/filepath"
2324
"strings"
25+
"text/template"
2426

2527
"github.com/googleapis/librarian/internal/command"
2628
"github.com/googleapis/librarian/internal/config"
2729
"github.com/googleapis/librarian/internal/serviceconfig"
2830
)
2931

32+
var (
33+
//go:embed template/_README.md.txt
34+
readmeTmpl string
35+
36+
readmeTmplParsed = template.Must(template.New("readme").Parse(readmeTmpl))
37+
)
38+
3039
// Generate generates a Go client library.
3140
func Generate(ctx context.Context, library *config.Library, googleapisDir string) error {
3241
if len(library.APIs) == 0 {
@@ -70,6 +79,13 @@ func Generate(ctx context.Context, library *config.Library, googleapisDir string
7079
}
7180

7281
moduleRoot := filepath.Join(outdir, library.Name)
82+
absModuleRoot, err := filepath.Abs(moduleRoot)
83+
if err != nil {
84+
return err
85+
}
86+
if !strings.HasPrefix(absModuleRoot, outdir+string(filepath.Separator)) && absModuleRoot != outdir {
87+
return fmt.Errorf("invalid library name: path traversal detected")
88+
}
7389
if err := generateInternalVersionFile(moduleRoot, library.Version); err != nil {
7490
return err
7591
}
@@ -78,6 +94,13 @@ func Generate(ctx context.Context, library *config.Library, googleapisDir string
7894
return err
7995
}
8096
}
97+
api, err := serviceconfig.Find(googleapisDir, library.APIs[0].Path)
98+
if err != nil {
99+
return err
100+
}
101+
if err := generateREADME(library, api, moduleRoot); err != nil {
102+
return err
103+
}
81104
return nil
82105
}
83106

@@ -293,3 +316,22 @@ func collectProtoFiles(googleapisDir, apiPath string, nestedProtos []string) ([]
293316
}
294317
return files, nil
295318
}
319+
320+
func generateREADME(library *config.Library, api *serviceconfig.API, moduleRoot string) error {
321+
if len(library.APIs) == 0 {
322+
return fmt.Errorf("no APIs configured")
323+
}
324+
f, err := os.Create(filepath.Join(moduleRoot, "README.md"))
325+
if err != nil {
326+
return err
327+
}
328+
err = readmeTmplParsed.Execute(f, map[string]string{
329+
"Name": api.Title,
330+
"ModulePath": modulePath(library),
331+
})
332+
cerr := f.Close()
333+
if err != nil {
334+
return err
335+
}
336+
return cerr
337+
}

internal/librarian/golang/generate_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ package golang
1717
import (
1818
"os"
1919
"path/filepath"
20+
"strings"
2021
"testing"
2122

2223
"github.com/google/go-cmp/cmp"
2324
"github.com/googleapis/librarian/internal/config"
25+
"github.com/googleapis/librarian/internal/serviceconfig"
2426
"github.com/googleapis/librarian/internal/testhelper"
2527
)
2628

@@ -48,6 +50,7 @@ func TestGenerate(t *testing.T) {
4850
"secretmanager/apiv1/secretmanagerpb/service.pb.go",
4951
"secretmanager/apiv1/version.go",
5052
"secretmanager/internal/version.go",
53+
"secretmanager/README.md",
5154
},
5255
removed: []string{
5356
"cloud.google.com",
@@ -208,3 +211,37 @@ func main() {
208211
t.Errorf("mismatch (-want +got):\n%s", diff)
209212
}
210213
}
214+
215+
func TestGenerateREADME(t *testing.T) {
216+
dir := t.TempDir()
217+
moduleRoot := filepath.Join(dir, "secretmanager")
218+
if err := os.MkdirAll(moduleRoot, 0755); err != nil {
219+
t.Fatal(err)
220+
}
221+
222+
library := &config.Library{
223+
Name: "secretmanager",
224+
Output: dir,
225+
APIs: []*config.API{{Path: "google/cloud/secretmanager/v1"}},
226+
}
227+
228+
api, err := serviceconfig.Find(googleapisDir, library.APIs[0].Path)
229+
if err != nil {
230+
t.Fatal(err)
231+
}
232+
if err := generateREADME(library, api, moduleRoot); err != nil {
233+
t.Fatal(err)
234+
}
235+
236+
content, err := os.ReadFile(filepath.Join(moduleRoot, "README.md"))
237+
if err != nil {
238+
t.Fatal(err)
239+
}
240+
s := string(content)
241+
if !strings.Contains(s, "Secret Manager API") {
242+
t.Errorf("want title in README, got:\n%s", s)
243+
}
244+
if !strings.Contains(s, "cloud.google.com/go/secretmanager") {
245+
t.Errorf("want module path in README, got:\n%s", s)
246+
}
247+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# {{.Name}}
2+
3+
[![Go Reference](https://pkg.go.dev/badge/{{.ModulePath}}.svg)](https://pkg.go.dev/{{.ModulePath}})
4+
5+
Go Client Library for {{.Name}}.
6+
7+
## Install
8+
9+
```bash
10+
go get {{.ModulePath}}
11+
```
12+
13+
## Stability
14+
15+
The stability of this module is indicated by SemVer.
16+
17+
However, a `v1+` module may have breaking changes in two scenarios:
18+
19+
* Packages with `alpha` or `beta` in the import path
20+
* The GoDoc has an explicit stability disclaimer (for example, for an experimental feature).
21+
22+
### Which package to use?
23+
24+
Generated client library surfaces can be found in packages whose import path
25+
ends in `.../apivXXX`. The `XXX` could be something like `1` or `2` in the case
26+
of a stable service backend or may be like `1beta2` or `2beta` in the case of a
27+
more experimental service backend. Because of this fact, a given module can have
28+
multiple clients for different service backends. In these cases it is generally
29+
recommended to use clients with stable service backends, with import suffixes like
30+
`apiv1`, unless you need to use features that are only present in a beta backend
31+
or there is not yet a stable backend available.
32+
33+
## Google Cloud Samples
34+
35+
To browse ready to use code samples check [Google Cloud Samples](https://cloud.google.com/docs/samples?l=go).
36+
37+
## Go Version Support
38+
39+
See the [Go Versions Supported](https://github.com/googleapis/google-cloud-go#go-versions-supported)
40+
section in the root directory's README.
41+
42+
## Authorization
43+
44+
See the [Authorization](https://github.com/googleapis/google-cloud-go#authorization)
45+
section in the root directory's README.
46+
47+
## Contributing
48+
49+
Contributions are welcome. Please, see the [CONTRIBUTING](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/main/CONTRIBUTING.md)
50+
document for details.
51+
52+
Please note that this project is released with a Contributor Code of Conduct.
53+
By participating in this project you agree to abide by its terms. See
54+
[Contributor Code of Conduct](https://github.com/GoogleCloudPlatform/google-cloud-go/blob/main/CONTRIBUTING.md#contributor-code-of-conduct)
55+
for more information.

0 commit comments

Comments
 (0)