Skip to content

Commit 37c70ac

Browse files
authored
feat(godocfx): create Go DocFX YAML generator (#2854)
1 parent cca8632 commit 37c70ac

File tree

13 files changed

+1252
-7
lines changed

13 files changed

+1252
-7
lines changed

internal/godocfx/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
obj/
2+
site/
3+
docfxgen

internal/godocfx/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Go DocFX YAML Generator
2+
3+
This tool generates DocFX YAML for Go modules.
4+
5+
Only a single module will be processed at once.
6+
7+
By default, the output files are stored at `./obj/api`. You can convert them to
8+
HTML using [doc-templates](https://github.com/googleapis/doc-templates) and/or
9+
[doc-pipeline](https://github.com/googleapis/doc-pipeline).
10+
11+
Example usage:
12+
13+
```
14+
cd module && godocfx ./...
15+
godocfx cloud.google.com/go/...
16+
godocfx -print cloud.google.com/go/storage/...
17+
godocfx -out custom/output/dir cloud.google.com/go/...
18+
godocfx -rm custom/output/dir cloud.google.com/go/...
19+
```

internal/godocfx/go.mod

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module cloud.google.com/go/internal/godocfx
2+
3+
go 1.15
4+
5+
require (
6+
cloud.google.com/go v0.65.0
7+
cloud.google.com/go/storage v1.11.0
8+
golang.org/x/tools v0.0.0-20200913032122-97363e29fc9b
9+
gopkg.in/yaml.v2 v2.3.0
10+
)
11+
12+
replace cloud.google.com/go => ../..

internal/godocfx/go.sum

Lines changed: 319 additions & 0 deletions
Large diffs are not rendered by default.

internal/godocfx/godocfx_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Copyright 2020 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+
// http://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+
// +build go1.15
16+
17+
package main
18+
19+
import (
20+
"testing"
21+
22+
_ "cloud.google.com/go/storage" // Implicitly required by test.
23+
)
24+
25+
func TestParse(t *testing.T) {
26+
testPath := "cloud.google.com/go/storage"
27+
pages, toc, module, err := parse(testPath)
28+
if err != nil {
29+
t.Fatalf("Parse: %v", err)
30+
}
31+
if got, want := len(toc), 1; got != want {
32+
t.Fatalf("Parse got len(toc) = %d, want %d", got, want)
33+
}
34+
if got, want := len(pages), 1; got != want {
35+
t.Errorf("Parse got len(pages) = %d, want %d", got, want)
36+
}
37+
if got := module.Path; got != testPath {
38+
t.Fatalf("Parse got module = %q, want %q", got, testPath)
39+
}
40+
41+
page := pages[testPath]
42+
43+
// Check invariants for every item.
44+
for _, item := range page.Items {
45+
if got := item.UID; got == "" {
46+
t.Errorf("Parse found missing UID: %v", item)
47+
}
48+
49+
if got, want := item.Langs, []string{"go"}; len(got) != 1 || got[0] != want[0] {
50+
t.Errorf("Parse %v got langs = %v, want %v", item.UID, got, want)
51+
}
52+
}
53+
54+
// Check there is at least one type, const, variable, and function.
55+
// Note: no method because they aren't printed for Namespaces yet.
56+
wants := []string{"type", "const", "variable", "function"}
57+
for _, want := range wants {
58+
found := false
59+
for _, c := range page.Items {
60+
if c.Type == want {
61+
found = true
62+
break
63+
}
64+
}
65+
if !found {
66+
t.Errorf("Parse got no %q, want at least one", want)
67+
}
68+
}
69+
}

internal/godocfx/main.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Copyright 2020 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+
// http://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+
// +build go1.15
16+
17+
/*Command godocfx generates DocFX YAML for Go code.
18+
19+
Usage:
20+
21+
godocfx [flags] path
22+
23+
cd module && godocfx ./...
24+
godocfx cloud.google.com/go/...
25+
godocfx -print cloud.google.com/go/storage/...
26+
godocfx -out custom/output/dir cloud.google.com/go/...
27+
godocfx -rm cloud.google.com/go/...
28+
29+
See:
30+
* https://dotnet.github.io/docfx/spec/metadata_format_spec.html
31+
* https://github.com/googleapis/doc-templates
32+
* https://github.com/googleapis/doc-pipeline
33+
34+
TODO:
35+
* Cross link referenced packages.
36+
*/
37+
package main
38+
39+
import (
40+
"flag"
41+
"fmt"
42+
"log"
43+
"os"
44+
"path/filepath"
45+
"strings"
46+
"time"
47+
48+
"gopkg.in/yaml.v2"
49+
)
50+
51+
func main() {
52+
print := flag.Bool("print", false, "Print instead of save (default false)")
53+
rm := flag.Bool("rm", false, "Delete out directory before generating")
54+
outDir := flag.String("out", "obj/api", "Output directory (default obj/api)")
55+
flag.Parse()
56+
if flag.NArg() != 1 {
57+
log.Fatalf("%s missing required argument: module path", os.Args[0])
58+
}
59+
60+
pages, toc, module, err := parse(flag.Arg(0))
61+
if err != nil {
62+
log.Fatal(err)
63+
}
64+
65+
if *print {
66+
if err := yaml.NewEncoder(os.Stdout).Encode(pages); err != nil {
67+
log.Fatal(err)
68+
}
69+
fmt.Println("----- toc.yaml")
70+
if err := yaml.NewEncoder(os.Stdout).Encode(toc); err != nil {
71+
log.Fatal(err)
72+
}
73+
return
74+
}
75+
76+
if *rm {
77+
os.RemoveAll(*outDir)
78+
}
79+
if err := os.MkdirAll(*outDir, os.ModePerm); err != nil {
80+
log.Fatalf("os.MkdirAll: %v", err)
81+
}
82+
for path, p := range pages {
83+
// Make the module root page the index.
84+
if path == module.Path {
85+
path = "index"
86+
}
87+
// Trim the module path from all other paths.
88+
path = strings.TrimPrefix(path, module.Path+"/")
89+
path = filepath.Join(*outDir, path+".yml")
90+
if err := os.MkdirAll(filepath.Dir(path), os.ModePerm); err != nil {
91+
log.Fatalf("os.MkdirAll: %v", err)
92+
}
93+
f, err := os.Create(path)
94+
if err != nil {
95+
log.Fatal(err)
96+
}
97+
defer f.Close()
98+
fmt.Fprintln(f, "### YamlMime:UniversalReference")
99+
if err := yaml.NewEncoder(f).Encode(p); err != nil {
100+
log.Fatal(err)
101+
}
102+
103+
path = filepath.Join(*outDir, "toc.yml")
104+
f, err = os.Create(path)
105+
if err != nil {
106+
log.Fatal(err)
107+
}
108+
defer f.Close()
109+
fmt.Fprintln(f, "### YamlMime:TableOfContent")
110+
if err := yaml.NewEncoder(f).Encode(toc); err != nil {
111+
log.Fatal(err)
112+
}
113+
}
114+
115+
// Write the docuploader docs.metadata file. Not for DocFX.
116+
// See https://github.com/googleapis/docuploader/issues/11.
117+
// Example:
118+
/*
119+
update_time {
120+
seconds: 1600048103
121+
nanos: 183052000
122+
}
123+
name: "cloud.google.com/go"
124+
version: "v0.65.0"
125+
language: "go"
126+
*/
127+
path := filepath.Join(*outDir, "docs.metadata")
128+
f, err := os.Create(path)
129+
if err != nil {
130+
log.Fatal(err)
131+
}
132+
defer f.Close()
133+
now := time.Now().UTC()
134+
fmt.Fprintf(f, `update_time {
135+
seconds: %d
136+
nanos: %d
137+
}
138+
name: %q
139+
version: %q
140+
language: "go"`, now.Unix(), now.Nanosecond(), module.Path, module.Version)
141+
}

0 commit comments

Comments
 (0)