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

Commit 0ef53e8

Browse files
authored
generate reference docs with cli-docs-tool (#62)
* fix go.mod Signed-off-by: CrazyMax <[email protected]> * generate reference docs with cli-docs-tool Signed-off-by: CrazyMax <[email protected]> * mark command as experimental Signed-off-by: CrazyMax <[email protected]> * docs: don't register global docker flags Signed-off-by: CrazyMax <[email protected]> --------- Signed-off-by: CrazyMax <[email protected]>
1 parent 0efa602 commit 0ef53e8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+12119
-3
lines changed

.github/workflows/validate.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: validate
2+
3+
permissions:
4+
contents: read
5+
6+
concurrency:
7+
group: ${{ github.workflow }}-${{ github.ref }}
8+
cancel-in-progress: true
9+
10+
on:
11+
workflow_dispatch:
12+
push:
13+
branches:
14+
- 'main'
15+
- 'v[0-9]*'
16+
tags:
17+
- 'v*'
18+
19+
jobs:
20+
prepare:
21+
runs-on: ubuntu-24.04
22+
outputs:
23+
targets: ${{ steps.generate.outputs.targets }}
24+
steps:
25+
-
26+
name: Checkout
27+
uses: actions/checkout@v4
28+
-
29+
name: List targets
30+
id: generate
31+
uses: docker/bake-action/subaction/list-targets@v6
32+
with:
33+
target: validate
34+
35+
validate:
36+
runs-on: ubuntu-24.04
37+
needs:
38+
- prepare
39+
strategy:
40+
fail-fast: false
41+
matrix:
42+
target: ${{ fromJson(needs.prepare.outputs.targets) }}
43+
steps:
44+
-
45+
name: Set up Docker Buildx
46+
uses: docker/setup-buildx-action@v3
47+
with:
48+
buildkitd-flags: --debug
49+
-
50+
name: Validate
51+
uses: docker/bake-action@v6
52+
with:
53+
targets: ${{ matrix.target }}

Dockerfile

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# syntax=docker/dockerfile:1
2+
3+
ARG GO_VERSION=1.24
4+
ARG ALPINE_VERSION=3.21
5+
6+
ARG DOCS_FORMATS="md,yaml"
7+
8+
FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION}-alpine${ALPINE_VERSION} AS base
9+
RUN apk add --no-cache rsync git
10+
ENV GOFLAGS=-mod=vendor
11+
ENV CGO_ENABLED=0
12+
WORKDIR /src
13+
14+
FROM base AS docs-gen
15+
WORKDIR /src
16+
RUN --mount=target=. \
17+
--mount=target=/root/.cache,type=cache \
18+
go build -mod=vendor -o /out/docsgen ./docs/generate.go
19+
20+
FROM base AS docs-build
21+
COPY --from=docs-gen /out/docsgen /usr/bin
22+
ENV DOCKER_CLI_PLUGIN_ORIGINAL_CLI_COMMAND="model"
23+
ARG DOCS_FORMATS
24+
RUN --mount=target=/context \
25+
--mount=target=.,type=tmpfs <<EOT
26+
set -e
27+
rsync -a /context/. .
28+
docsgen --formats "$DOCS_FORMATS" --source "docs/reference"
29+
mkdir /out
30+
cp -r docs/reference/* /out/
31+
EOT
32+
33+
FROM scratch AS docs-update
34+
COPY --from=docs-build /out /
35+
36+
FROM docs-build AS docs-validate
37+
RUN --mount=target=/context \
38+
--mount=target=.,type=tmpfs <<EOT
39+
set -e
40+
rsync -a /context/. .
41+
git add -A
42+
rm -rf docs/reference/*
43+
cp -rf /out/* ./docs/reference/
44+
if [ -n "$(git status --porcelain -- docs/reference)" ]; then
45+
echo >&2 'ERROR: Docs result differs. Please update with "make docs"'
46+
git status --porcelain -- docs/reference
47+
exit 1
48+
fi
49+
EOT

Makefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: all build clean link mock unit-tests
1+
.PHONY: all build clean link mock unit-tests docs
22

33
BINARY_NAME=model-cli
44

@@ -67,3 +67,10 @@ clean:
6767
@echo "Cleaning up..."
6868
@rm -f $(BINARY_NAME)
6969
@echo "Cleaned!"
70+
71+
docs:
72+
$(eval $@_TMP_OUT := $(shell mktemp -d -t model-cli-output.XXXXXXXXXX))
73+
docker buildx bake --set "*.output=type=local,dest=$($@_TMP_OUT)" update-docs
74+
rm -rf ./docs/reference/*
75+
cp -R "$($@_TMP_OUT)"/* ./docs/reference/
76+
rm -rf "$($@_TMP_OUT)"/*

commands/root.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ func NewRootCmd(cli *command.DockerCli) *cobra.Command {
8383
TraverseChildren: plugin.RunningStandalone(),
8484
}
8585

86+
markCommandExperimental(rootCmd)
87+
8688
// Initialize client options and register their flags if running in
8789
// standalone mode.
8890
if plugin.RunningStandalone() {
@@ -109,3 +111,16 @@ func NewRootCmd(cli *command.DockerCli) *cobra.Command {
109111
)
110112
return rootCmd
111113
}
114+
115+
const annotationExperimentalCLI = "experimentalCLI"
116+
117+
func markCommandExperimental(c *cobra.Command) {
118+
if _, ok := c.Annotations[annotationExperimentalCLI]; ok {
119+
return
120+
}
121+
if c.Annotations == nil {
122+
c.Annotations = make(map[string]string)
123+
}
124+
c.Annotations[annotationExperimentalCLI] = ""
125+
c.Short += " (EXPERIMENTAL)"
126+
}

docker-bake.hcl

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
variable "GO_VERSION" {
2+
default = null
3+
}
4+
variable "DOCS_FORMATS" {
5+
default = "md,yaml"
6+
}
7+
8+
target "_common" {
9+
args = {
10+
GO_VERSION = GO_VERSION
11+
BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1
12+
}
13+
}
14+
15+
group "default" {
16+
targets = ["validate"]
17+
}
18+
19+
group "validate" {
20+
targets = ["validate-docs"]
21+
}
22+
23+
target "validate-docs" {
24+
inherits = ["_common"]
25+
args = {
26+
DOCS_FORMATS = DOCS_FORMATS
27+
}
28+
target = "docs-validate"
29+
output = ["type=cacheonly"]
30+
}
31+
32+
target "update-docs" {
33+
inherits = ["_common"]
34+
args = {
35+
DOCS_FORMATS = DOCS_FORMATS
36+
}
37+
target = "docs-update"
38+
output = ["./docs/reference"]
39+
}

docs/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+
"log"
5+
"os"
6+
"strings"
7+
8+
clidocstool "github.com/docker/cli-docs-tool"
9+
"github.com/docker/cli/cli/command"
10+
"github.com/docker/model-cli/commands"
11+
"github.com/pkg/errors"
12+
"github.com/spf13/cobra"
13+
"github.com/spf13/pflag"
14+
)
15+
16+
const defaultSourcePath = "docs/reference/"
17+
18+
type options struct {
19+
source string
20+
formats []string
21+
}
22+
23+
func gen(opts *options) error {
24+
log.SetFlags(0)
25+
26+
dockerCLI, err := command.NewDockerCli()
27+
if err != nil {
28+
return err
29+
}
30+
cmd := &cobra.Command{
31+
Use: "docker [OPTIONS] COMMAND [ARG...]",
32+
Short: "The base command for the Docker CLI.",
33+
DisableAutoGenTag: true,
34+
}
35+
36+
cmd.AddCommand(commands.NewRootCmd(dockerCLI))
37+
38+
c, err := clidocstool.New(clidocstool.Options{
39+
Root: cmd,
40+
SourceDir: opts.source,
41+
Plugin: true,
42+
})
43+
if err != nil {
44+
return err
45+
}
46+
47+
for _, format := range opts.formats {
48+
switch format {
49+
case "md":
50+
if err = c.GenMarkdownTree(cmd); err != nil {
51+
return err
52+
}
53+
case "yaml":
54+
fixUpExperimentalCLI(cmd)
55+
if err = c.GenYamlTree(cmd); err != nil {
56+
return err
57+
}
58+
default:
59+
return errors.Errorf("unknown format %q", format)
60+
}
61+
}
62+
63+
return nil
64+
}
65+
66+
func run() error {
67+
opts := &options{}
68+
flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError)
69+
flags.StringVar(&opts.source, "source", defaultSourcePath, "Docs source folder")
70+
flags.StringSliceVar(&opts.formats, "formats", []string{}, "Format (md, yaml)")
71+
if err := flags.Parse(os.Args[1:]); err != nil {
72+
return err
73+
}
74+
if len(opts.formats) == 0 {
75+
return errors.New("Docs format required")
76+
}
77+
return gen(opts)
78+
}
79+
80+
func main() {
81+
if err := run(); err != nil {
82+
log.Printf("ERROR: %+v", err)
83+
os.Exit(1)
84+
}
85+
}
86+
87+
// fixUpExperimentalCLI trims the " (EXPERIMENTAL)" suffix from the CLI output,
88+
// as docs.docker.com already displays "experimental (CLI)",
89+
//
90+
// https://github.com/docker/buildx/pull/2188#issuecomment-1889487022
91+
func fixUpExperimentalCLI(cmd *cobra.Command) {
92+
const (
93+
annotationExperimentalCLI = "experimentalCLI"
94+
suffixExperimental = " (EXPERIMENTAL)"
95+
)
96+
if _, ok := cmd.Annotations[annotationExperimentalCLI]; ok {
97+
cmd.Short = strings.TrimSuffix(cmd.Short, suffixExperimental)
98+
}
99+
cmd.Flags().VisitAll(func(f *pflag.Flag) {
100+
if _, ok := f.Annotations[annotationExperimentalCLI]; ok {
101+
f.Usage = strings.TrimSuffix(f.Usage, suffixExperimental)
102+
}
103+
})
104+
for _, c := range cmd.Commands() {
105+
fixUpExperimentalCLI(c)
106+
}
107+
}

docs/reference/docker_model.yaml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
command: docker model
2+
short: Docker Model Runner
3+
long: Docker Model Runner
4+
pname: docker
5+
plink: docker.yaml
6+
cname:
7+
- docker model inspect
8+
- docker model install-runner
9+
- docker model list
10+
- docker model logs
11+
- docker model package
12+
- docker model pull
13+
- docker model push
14+
- docker model rm
15+
- docker model run
16+
- docker model status
17+
- docker model tag
18+
- docker model uninstall-runner
19+
- docker model version
20+
clink:
21+
- docker_model_inspect.yaml
22+
- docker_model_install-runner.yaml
23+
- docker_model_list.yaml
24+
- docker_model_logs.yaml
25+
- docker_model_package.yaml
26+
- docker_model_pull.yaml
27+
- docker_model_push.yaml
28+
- docker_model_rm.yaml
29+
- docker_model_run.yaml
30+
- docker_model_status.yaml
31+
- docker_model_tag.yaml
32+
- docker_model_uninstall-runner.yaml
33+
- docker_model_version.yaml
34+
deprecated: false
35+
hidden: false
36+
experimental: false
37+
experimentalcli: true
38+
kubernetes: false
39+
swarm: false
40+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
command: docker model compose
2+
pname: docker model
3+
plink: docker_model.yaml
4+
cname:
5+
- docker model compose down
6+
- docker model compose up
7+
clink:
8+
- docker_model_compose_down.yaml
9+
- docker_model_compose_up.yaml
10+
options:
11+
- option: project-name
12+
value_type: string
13+
description: compose project name
14+
deprecated: false
15+
hidden: false
16+
experimental: false
17+
experimentalcli: false
18+
kubernetes: false
19+
swarm: false
20+
deprecated: false
21+
hidden: true
22+
experimental: false
23+
experimentalcli: true
24+
kubernetes: false
25+
swarm: false
26+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
command: docker model compose down
2+
usage: docker model compose down
3+
pname: docker model compose
4+
plink: docker_model_compose.yaml
5+
options:
6+
- option: model
7+
value_type: stringArray
8+
default_value: '[]'
9+
description: model to use
10+
deprecated: false
11+
hidden: false
12+
experimental: false
13+
experimentalcli: false
14+
kubernetes: false
15+
swarm: false
16+
inherited_options:
17+
- option: project-name
18+
value_type: string
19+
description: compose project name
20+
deprecated: false
21+
hidden: false
22+
experimental: false
23+
experimentalcli: false
24+
kubernetes: false
25+
swarm: false
26+
deprecated: false
27+
hidden: true
28+
experimental: false
29+
experimentalcli: true
30+
kubernetes: false
31+
swarm: false
32+

0 commit comments

Comments
 (0)