Skip to content

Commit 73a672b

Browse files
authored
Merge pull request #112 from hashicorp/f-auto-docs
Add tool to copy generated docs to hcp-docs repo
2 parents a2647b5 + 6d15127 commit 73a672b

File tree

6 files changed

+198
-31
lines changed

6 files changed

+198
-31
lines changed

Makefile

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,21 @@ MOCKERY_OUTPUT_FILES=internal/pkg/api/iampolicy/mock_setter.go \
1111

1212
default: help
1313

14+
.PHONY: docs/gen
15+
docs/gen: go/build ## Generate the HCP CLI documentation
16+
@mkdir -p web-docs
17+
@rm -rf web-docs/*
18+
@./bin/gendocs -output-dir web-docs/commands --output-nav-json web-docs/nav.json
19+
20+
.PHONY: docs/move
21+
docs/move: go/build ## Copy the generated documentation to the HCP docs repository
22+
@./bin/mvdocs -generated-commands-dir web-docs/commands --generated-nav-json web-docs/nav.json \
23+
--dest-commands-dir ../hcp-docs/content/docs/cli/commands --dest-nav-json ../hcp-docs/data/docs-nav-data.json
24+
1425
.PHONY: gen/screenshot
1526
gen/screenshot: go/install ## Create a screenshot of the HCP CLI
1627
@go run github.com/homeport/termshot/cmd/termshot@v0.2.7 -c -f assets/hcp.png -- hcp
1728

18-
.PHONY: gen/docs
19-
gen/docs: go/build ## Generate the HCP CLI documentation
20-
@mkdir -p web-docs
21-
@rm -rf web-docs/*
22-
@./bin/gendocs -output-dir web-docs/
23-
2429
.PHONY: gen/releasesapi
2530
gen/releasesapi: ## Generate the releases API client
2631
@./hack/gen_releases_client.sh

contributing/README.md

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -472,19 +472,9 @@ assume the `hcp-docs` repository is checked out in the same parent directory as
472472
`hcp`.
473473
474474
```sh
475-
$ make gen/docs
476-
$ rm -rf ../hcp-docs/content/docs/cli/commands/*
477-
$ mv web-docs/* ../hcp-docs/content/docs/cli/commands/
475+
$ make docs/gen
476+
$ make docs/move
478477
$ cd ../hcp-docs
479-
```
480-
481-
Next, you need to copy the data from `content/docs/cli/commands/nav.json` into
482-
the correct position in the `data/docs-nav-data.json` file. You need to replace
483-
the entire content of the section beginning with `"title": "Commands (CLI)"`
484-
that is nested under `"title": "HCP CLI"`. Once this is done, delete
485-
`content/docs/cli/commands/nav.json` and run the following command:
486-
487-
```sh
488478
$ make website
489479
```
490480

hack/gendocs/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func run() error {
3030
var outputNavJSON string
3131
var linkPrefix string
3232

33-
flag.StringVar(&outputDir, "output-dir", "web-docs", "The output directory for the generated documentation")
33+
flag.StringVar(&outputDir, "output-dir", "web-docs/commands/", "The output directory for the generated documentation")
3434
flag.StringVar(&outputNavJSON, "output-nav-json", "web-docs/nav.json", "The output path for the generated nav json")
3535
flag.StringVar(&linkPrefix, "cmd-link-prefix", "/hcp/docs/cli/commands/", "Link prefix for the commands")
3636

hack/mvdocs/main.go

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
// This tool is used to move the generated commands documentation from the `web-docs` directory to the `hcp-docs` repository.
5+
package main
6+
7+
import (
8+
"encoding/json"
9+
"flag"
10+
"fmt"
11+
"io"
12+
"os"
13+
"path/filepath"
14+
"strings"
15+
16+
"io/fs"
17+
18+
"github.com/hashicorp/hcp/internal/pkg/cmd"
19+
)
20+
21+
func main() {
22+
if err := run(); err != nil {
23+
fmt.Fprintf(os.Stderr, "%v\n", err)
24+
os.Exit(1)
25+
}
26+
}
27+
28+
func run() error {
29+
// Define the flags
30+
var srcCommandsDir string
31+
var destCommandsDir string
32+
var srcNavJSON string
33+
var destNavJSON string
34+
35+
flag.StringVar(&srcCommandsDir, "generated-commands-dir", "web-docs/commands", "The generated commands documentation to move to hcp-docs repository")
36+
flag.StringVar(&destCommandsDir, "dest-commands-dir", "../hcp-docs/content/docs/cli/commands/", "The destination directory for the generated commands documentation")
37+
flag.StringVar(&srcNavJSON, "generated-nav-json", "web-docs/nav.json", "The output path for the generated nav json")
38+
flag.StringVar(&destNavJSON, "dest-nav-json", "../hcp-docs/data/docs-nav-data.json", "Path to `hcp-docs` nav json file")
39+
40+
// Parse the flags
41+
flag.Parse()
42+
43+
// Delete the existing commands directory
44+
if err := os.RemoveAll(destCommandsDir); err != nil {
45+
return fmt.Errorf("failed to remove destination commands directory: %w", err)
46+
}
47+
48+
// Move the commands directory
49+
if err := CopyDir(destCommandsDir, srcCommandsDir); err != nil {
50+
return fmt.Errorf("failed to copy commands directory: %w", err)
51+
}
52+
53+
// Open the existing nav JSON for both read and writing.
54+
dstNavFD, err := os.OpenFile(destNavJSON, os.O_RDWR, 0644)
55+
if err != nil {
56+
return fmt.Errorf("failed to open destination nav JSON: %w", err)
57+
}
58+
59+
// Parse the JSON
60+
var navData []cmd.DocNavItem
61+
if err := json.NewDecoder(dstNavFD).Decode(&navData); err != nil {
62+
return fmt.Errorf("failed to decode destination nav JSON: %w", err)
63+
}
64+
65+
// Find the HCP CLI section to inject into
66+
var hcpCommandsSection *cmd.DocNavItem
67+
68+
OUTER:
69+
for _, root := range navData {
70+
if root.Title != "HCP CLI" {
71+
continue
72+
}
73+
74+
// Find the generated commands section
75+
for _, route := range root.Routes {
76+
if route.Title != "Commands (CLI)" {
77+
continue
78+
}
79+
80+
hcpCommandsSection = route
81+
break OUTER
82+
}
83+
}
84+
if hcpCommandsSection == nil {
85+
return fmt.Errorf("failed to find HCP CLI section in destination nav JSON")
86+
}
87+
88+
// Open the generated nav JSON
89+
srcNavFD, err := os.Open(srcNavJSON)
90+
if err != nil {
91+
return fmt.Errorf("failed to open source nav JSON: %w", err)
92+
}
93+
94+
// Parse the JSON
95+
var srcNavData cmd.DocNavItem
96+
if err := json.NewDecoder(srcNavFD).Decode(&srcNavData); err != nil {
97+
return fmt.Errorf("failed to decode source nav JSON: %w", err)
98+
}
99+
100+
// Inject the HCP CLI section
101+
*hcpCommandsSection = srcNavData
102+
103+
// Serialize the JSON
104+
if _, err := dstNavFD.Seek(0, 0); err != nil {
105+
return fmt.Errorf("failed to seek destination nav JSON: %w", err)
106+
}
107+
108+
e := json.NewEncoder(dstNavFD)
109+
e.SetIndent("", " ")
110+
e.SetEscapeHTML(false)
111+
if err := e.Encode(navData); err != nil {
112+
return fmt.Errorf("failed to encode destination nav JSON: %w", err)
113+
}
114+
115+
return nil
116+
}
117+
118+
// CopyDir copies the content of src to dst. src should be a full path.
119+
func CopyDir(dst, src string) error {
120+
return filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
121+
if err != nil {
122+
return err
123+
}
124+
125+
// copy to this path
126+
outpath := filepath.Join(dst, strings.TrimPrefix(path, src))
127+
if info.IsDir() {
128+
if err := os.MkdirAll(outpath, info.Mode()); err != nil {
129+
return err
130+
}
131+
return nil // means recursive
132+
}
133+
134+
// handle irregular files
135+
if !info.Mode().IsRegular() {
136+
if info.Mode().Type()&os.ModeType == os.ModeSymlink {
137+
link, err := os.Readlink(path)
138+
if err != nil {
139+
return err
140+
}
141+
return os.Symlink(link, outpath)
142+
}
143+
return nil
144+
}
145+
146+
// copy contents of regular file efficiently
147+
148+
// open input
149+
in, err := os.Open(path)
150+
if err != nil {
151+
return err
152+
}
153+
defer in.Close()
154+
155+
// create output
156+
fh, err := os.Create(outpath)
157+
if err != nil {
158+
return err
159+
}
160+
defer fh.Close()
161+
162+
// make it the same
163+
if err := fh.Chmod(info.Mode()); err != nil {
164+
return err
165+
}
166+
167+
// copy content
168+
_, err = io.Copy(fh, in)
169+
return err
170+
})
171+
}

hack/mvdocs/mvdocs

11.5 MB
Binary file not shown.

internal/pkg/cmd/gen_nav_json.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,23 @@ import (
1111
"strings"
1212
)
1313

14-
// navItem is a single item in the navigation JSON.
15-
type navItem struct {
16-
Title string `json:"title"`
17-
Path string `json:"path,omitempty"`
18-
Routes []*navItem `json:"routes,omitempty"`
14+
// DocNavItem is a single item in the navigation JSON.
15+
type DocNavItem struct {
16+
Title string `json:"title"`
17+
Href string `json:"href,omitempty"`
18+
Path string `json:"path,omitempty"`
19+
Routes []*DocNavItem `json:"routes,omitempty"`
1920
}
2021

2122
// GenNavJSON generates the navigation JSON in the format that hcp-docs expects,
2223
// for the command structure.
2324
func GenNavJSON(c *Command, w io.Writer) error {
2425

25-
root := &navItem{}
26+
root := &DocNavItem{}
2627
genNavJSON(c, root, "cli/commands")
2728

2829
// Create the top level nav item
29-
nav := &navItem{
30+
nav := &DocNavItem{
3031
Title: "Commands (CLI)",
3132
Routes: root.Routes[0].Routes,
3233
}
@@ -43,27 +44,27 @@ func GenNavJSON(c *Command, w io.Writer) error {
4344

4445
// genNavJSON is a recursive function that generates the navigation JSON for
4546
// the command structure.
46-
func genNavJSON(c *Command, nav *navItem, path string) {
47+
func genNavJSON(c *Command, nav *DocNavItem, path string) {
4748
// Generate a new nav item for this command
48-
var self *navItem
49+
var self *DocNavItem
4950

5051
if c.parent != nil {
5152
path = filepath.Join(path, c.Name)
5253
}
5354

5455
// Handle being a command group
5556
if len(c.children) > 0 {
56-
self = &navItem{
57+
self = &DocNavItem{
5758
Title: c.Name,
58-
Routes: []*navItem{
59+
Routes: []*DocNavItem{
5960
{
6061
Title: "Overview",
6162
Path: path,
6263
},
6364
},
6465
}
6566
} else {
66-
self = &navItem{
67+
self = &DocNavItem{
6768
Title: c.Name,
6869
Path: path,
6970
}

0 commit comments

Comments
 (0)