Skip to content
This repository was archived by the owner on Jul 18, 2025. It is now read-only.

Commit e5b6e29

Browse files
thaJeztahndeloof
authored andcommitted
Add YAML-docs generation
This is a very quick 'n dirty implementation of the YAML docs generation; copied from the docker/cli repository with some minor modifications and a new Makefile target. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent 433639b commit e5b6e29

File tree

5 files changed

+411
-0
lines changed

5 files changed

+411
-0
lines changed

Makefile

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,9 @@ help: ## this help
133133

134134
.PHONY: cross e2e-cross test check lint test-unit test-e2e coverage coverage-bin coverage-test-unit coverage-test-e2e clean vendor schemas help
135135
.DEFAULT: all
136+
137+
138+
.PHONY: yamldocs
139+
yamldocs: ## generate documentation YAML files consumed by docs repo
140+
mkdir -p ./_build/docs
141+
docker build --output type=local,dest=./_build/ -f docs/yaml/Dockerfile .

docs/reference/.keep

Whitespace-only changes.

docs/yaml/Dockerfile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
FROM golang:1.12-alpine AS base
2+
3+
ARG PROJECT=github.com/docker/app
4+
ARG PROJECT_PATH=/go/src/${PROJECT}
5+
ENV CGO_ENABLED=0
6+
ENV PATH=${PATH}:${PROJECT_PATH}/build
7+
WORKDIR $PROJECT_PATH
8+
9+
RUN mkdir -p docs/yaml/gen
10+
11+
COPY . .
12+
RUN go build -o build/yaml-docs-generator ${PROJECT}/docs/yaml
13+
RUN build/yaml-docs-generator \
14+
--root "${PROJECT_PATH}" \
15+
--target "${PROJECT_PATH}/docs/yaml/gen"
16+
17+
18+
FROM scratch
19+
ARG PROJECT=github.com/docker/app
20+
ARG PROJECT_PATH=/go/src/${PROJECT}
21+
ENV PROJECT_PATH=${PROJECT_PATH}
22+
# CMD cannot be nil so we set it to empty string
23+
CMD [""]
24+
COPY --from=base ${PROJECT_PATH}/docs/reference /docs/reference
25+
COPY --from=base ${PROJECT_PATH}/docs/yaml/gen /docs/yaml

docs/yaml/generate.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"log"
7+
"os"
8+
"path/filepath"
9+
"strings"
10+
11+
"github.com/docker/app/internal/commands"
12+
clicommand "github.com/docker/cli/cli/command"
13+
"github.com/spf13/cobra"
14+
"github.com/spf13/pflag"
15+
)
16+
17+
const descriptionSourcePath = "docs/reference/"
18+
19+
func generateCliYaml(opts *options) error {
20+
dockerCLI, err := clicommand.NewDockerCli()
21+
if err != nil {
22+
return err
23+
}
24+
cmd := &cobra.Command{Use: "docker"}
25+
cmd.AddCommand(commands.NewRootCmd("app", dockerCLI))
26+
disableFlagsInUseLine(cmd)
27+
source := filepath.Join(opts.source, descriptionSourcePath)
28+
if err := loadLongDescription(cmd, source); err != nil {
29+
return err
30+
}
31+
32+
cmd.DisableAutoGenTag = true
33+
return GenYamlTree(cmd, opts.target)
34+
}
35+
36+
func disableFlagsInUseLine(cmd *cobra.Command) {
37+
visitAll(cmd, func(ccmd *cobra.Command) {
38+
// do not add a `[flags]` to the end of the usage line.
39+
ccmd.DisableFlagsInUseLine = true
40+
})
41+
}
42+
43+
// visitAll will traverse all commands from the root.
44+
// This is different from the VisitAll of cobra.Command where only parents
45+
// are checked.
46+
func visitAll(root *cobra.Command, fn func(*cobra.Command)) {
47+
for _, cmd := range root.Commands() {
48+
visitAll(cmd, fn)
49+
}
50+
fn(root)
51+
}
52+
53+
func loadLongDescription(cmd *cobra.Command, path ...string) error {
54+
for _, cmd := range cmd.Commands() {
55+
if cmd.Name() == "" {
56+
continue
57+
}
58+
fullpath := filepath.Join(path[0], strings.Join(append(path[1:], cmd.Name()), "_")+".md")
59+
60+
if cmd.HasSubCommands() {
61+
if err := loadLongDescription(cmd, path[0], cmd.Name()); err != nil {
62+
return err
63+
}
64+
}
65+
66+
if _, err := os.Stat(fullpath); err != nil {
67+
log.Printf("WARN: %s does not exist, skipping\n", fullpath)
68+
continue
69+
}
70+
71+
content, err := ioutil.ReadFile(fullpath)
72+
if err != nil {
73+
return err
74+
}
75+
description, examples := parseMDContent(string(content))
76+
cmd.Long = description
77+
cmd.Example = examples
78+
}
79+
return nil
80+
}
81+
82+
type options struct {
83+
source string
84+
target string
85+
}
86+
87+
func parseArgs() (*options, error) {
88+
opts := &options{}
89+
cwd, _ := os.Getwd()
90+
flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
91+
flags.StringVar(&opts.source, "root", cwd, "Path to project root")
92+
flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated yaml files")
93+
err := flags.Parse(os.Args[1:])
94+
return opts, err
95+
}
96+
97+
func main() {
98+
opts, err := parseArgs()
99+
if err != nil {
100+
fmt.Fprintln(os.Stderr, err.Error())
101+
}
102+
fmt.Printf("Project root: %s\n", opts.source)
103+
fmt.Printf("Generating yaml files into %s\n", opts.target)
104+
if err := generateCliYaml(opts); err != nil {
105+
fmt.Fprintf(os.Stderr, "Failed to generate yaml files: %s\n", err.Error())
106+
}
107+
}

0 commit comments

Comments
 (0)