Skip to content

Commit

Permalink
Merge pull request #112 from hashicorp/f-auto-docs
Browse files Browse the repository at this point in the history
Add tool to copy generated docs to hcp-docs repo
  • Loading branch information
dadgar authored Jun 11, 2024
2 parents a2647b5 + 6d15127 commit 73a672b
Show file tree
Hide file tree
Showing 6 changed files with 198 additions and 31 deletions.
17 changes: 11 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,21 @@ MOCKERY_OUTPUT_FILES=internal/pkg/api/iampolicy/mock_setter.go \

default: help

.PHONY: docs/gen
docs/gen: go/build ## Generate the HCP CLI documentation
@mkdir -p web-docs
@rm -rf web-docs/*
@./bin/gendocs -output-dir web-docs/commands --output-nav-json web-docs/nav.json

.PHONY: docs/move
docs/move: go/build ## Copy the generated documentation to the HCP docs repository
@./bin/mvdocs -generated-commands-dir web-docs/commands --generated-nav-json web-docs/nav.json \
--dest-commands-dir ../hcp-docs/content/docs/cli/commands --dest-nav-json ../hcp-docs/data/docs-nav-data.json

.PHONY: gen/screenshot
gen/screenshot: go/install ## Create a screenshot of the HCP CLI
@go run github.com/homeport/termshot/cmd/[email protected] -c -f assets/hcp.png -- hcp

.PHONY: gen/docs
gen/docs: go/build ## Generate the HCP CLI documentation
@mkdir -p web-docs
@rm -rf web-docs/*
@./bin/gendocs -output-dir web-docs/

.PHONY: gen/releasesapi
gen/releasesapi: ## Generate the releases API client
@./hack/gen_releases_client.sh
Expand Down
14 changes: 2 additions & 12 deletions contributing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,19 +472,9 @@ assume the `hcp-docs` repository is checked out in the same parent directory as
`hcp`.
```sh
$ make gen/docs
$ rm -rf ../hcp-docs/content/docs/cli/commands/*
$ mv web-docs/* ../hcp-docs/content/docs/cli/commands/
$ make docs/gen
$ make docs/move
$ cd ../hcp-docs
```
Next, you need to copy the data from `content/docs/cli/commands/nav.json` into
the correct position in the `data/docs-nav-data.json` file. You need to replace
the entire content of the section beginning with `"title": "Commands (CLI)"`
that is nested under `"title": "HCP CLI"`. Once this is done, delete
`content/docs/cli/commands/nav.json` and run the following command:
```sh
$ make website
```
Expand Down
2 changes: 1 addition & 1 deletion hack/gendocs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func run() error {
var outputNavJSON string
var linkPrefix string

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

Expand Down
171 changes: 171 additions & 0 deletions hack/mvdocs/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

// This tool is used to move the generated commands documentation from the `web-docs` directory to the `hcp-docs` repository.
package main

import (
"encoding/json"
"flag"
"fmt"
"io"
"os"
"path/filepath"
"strings"

"io/fs"

"github.com/hashicorp/hcp/internal/pkg/cmd"
)

func main() {
if err := run(); err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}

func run() error {
// Define the flags
var srcCommandsDir string
var destCommandsDir string
var srcNavJSON string
var destNavJSON string

flag.StringVar(&srcCommandsDir, "generated-commands-dir", "web-docs/commands", "The generated commands documentation to move to hcp-docs repository")
flag.StringVar(&destCommandsDir, "dest-commands-dir", "../hcp-docs/content/docs/cli/commands/", "The destination directory for the generated commands documentation")
flag.StringVar(&srcNavJSON, "generated-nav-json", "web-docs/nav.json", "The output path for the generated nav json")
flag.StringVar(&destNavJSON, "dest-nav-json", "../hcp-docs/data/docs-nav-data.json", "Path to `hcp-docs` nav json file")

// Parse the flags
flag.Parse()

// Delete the existing commands directory
if err := os.RemoveAll(destCommandsDir); err != nil {
return fmt.Errorf("failed to remove destination commands directory: %w", err)
}

// Move the commands directory
if err := CopyDir(destCommandsDir, srcCommandsDir); err != nil {
return fmt.Errorf("failed to copy commands directory: %w", err)
}

// Open the existing nav JSON for both read and writing.
dstNavFD, err := os.OpenFile(destNavJSON, os.O_RDWR, 0644)
if err != nil {
return fmt.Errorf("failed to open destination nav JSON: %w", err)
}

// Parse the JSON
var navData []cmd.DocNavItem
if err := json.NewDecoder(dstNavFD).Decode(&navData); err != nil {
return fmt.Errorf("failed to decode destination nav JSON: %w", err)
}

// Find the HCP CLI section to inject into
var hcpCommandsSection *cmd.DocNavItem

OUTER:
for _, root := range navData {
if root.Title != "HCP CLI" {
continue
}

// Find the generated commands section
for _, route := range root.Routes {
if route.Title != "Commands (CLI)" {
continue
}

hcpCommandsSection = route
break OUTER
}
}
if hcpCommandsSection == nil {
return fmt.Errorf("failed to find HCP CLI section in destination nav JSON")
}

// Open the generated nav JSON
srcNavFD, err := os.Open(srcNavJSON)
if err != nil {
return fmt.Errorf("failed to open source nav JSON: %w", err)
}

// Parse the JSON
var srcNavData cmd.DocNavItem
if err := json.NewDecoder(srcNavFD).Decode(&srcNavData); err != nil {
return fmt.Errorf("failed to decode source nav JSON: %w", err)
}

// Inject the HCP CLI section
*hcpCommandsSection = srcNavData

// Serialize the JSON
if _, err := dstNavFD.Seek(0, 0); err != nil {
return fmt.Errorf("failed to seek destination nav JSON: %w", err)
}

e := json.NewEncoder(dstNavFD)
e.SetIndent("", " ")
e.SetEscapeHTML(false)
if err := e.Encode(navData); err != nil {
return fmt.Errorf("failed to encode destination nav JSON: %w", err)
}

return nil
}

// CopyDir copies the content of src to dst. src should be a full path.
func CopyDir(dst, src string) error {
return filepath.Walk(src, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}

// copy to this path
outpath := filepath.Join(dst, strings.TrimPrefix(path, src))
if info.IsDir() {
if err := os.MkdirAll(outpath, info.Mode()); err != nil {
return err
}
return nil // means recursive
}

// handle irregular files
if !info.Mode().IsRegular() {
if info.Mode().Type()&os.ModeType == os.ModeSymlink {
link, err := os.Readlink(path)
if err != nil {
return err
}
return os.Symlink(link, outpath)
}
return nil
}

// copy contents of regular file efficiently

// open input
in, err := os.Open(path)
if err != nil {
return err
}
defer in.Close()

// create output
fh, err := os.Create(outpath)
if err != nil {
return err
}
defer fh.Close()

// make it the same
if err := fh.Chmod(info.Mode()); err != nil {
return err
}

// copy content
_, err = io.Copy(fh, in)
return err
})
}
Binary file added hack/mvdocs/mvdocs
Binary file not shown.
25 changes: 13 additions & 12 deletions internal/pkg/cmd/gen_nav_json.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,23 @@ import (
"strings"
)

// navItem is a single item in the navigation JSON.
type navItem struct {
Title string `json:"title"`
Path string `json:"path,omitempty"`
Routes []*navItem `json:"routes,omitempty"`
// DocNavItem is a single item in the navigation JSON.
type DocNavItem struct {
Title string `json:"title"`
Href string `json:"href,omitempty"`
Path string `json:"path,omitempty"`
Routes []*DocNavItem `json:"routes,omitempty"`
}

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

root := &navItem{}
root := &DocNavItem{}
genNavJSON(c, root, "cli/commands")

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

// genNavJSON is a recursive function that generates the navigation JSON for
// the command structure.
func genNavJSON(c *Command, nav *navItem, path string) {
func genNavJSON(c *Command, nav *DocNavItem, path string) {
// Generate a new nav item for this command
var self *navItem
var self *DocNavItem

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

// Handle being a command group
if len(c.children) > 0 {
self = &navItem{
self = &DocNavItem{
Title: c.Name,
Routes: []*navItem{
Routes: []*DocNavItem{
{
Title: "Overview",
Path: path,
},
},
}
} else {
self = &navItem{
self = &DocNavItem{
Title: c.Name,
Path: path,
}
Expand Down

0 comments on commit 73a672b

Please sign in to comment.