├── .gitignore ├── .dockerignore ├── fixtures ├── buildx_stop.pre.md ├── buildx_stop.md ├── docker-buildx-stop.1 ├── docker.1 ├── docker-buildx.1 ├── buildx_dial-stdio.md ├── docker-attach.1 ├── docker-buildx-dial-stdio.1 ├── attach.md ├── docker.md ├── buildx.md ├── docker_buildx_stop.yaml ├── docker_buildx_install.yaml ├── docker_buildx_imagetools_create.yaml ├── docker.yaml ├── docker_buildx_imagetools.yaml ├── docker_buildx.yaml ├── docker_buildx_dial-stdio.yaml ├── docker_attach.yaml ├── docker-buildx-build.1 ├── buildx_build.md └── docker_buildx_build.yaml ├── .github ├── CODE_OF_CONDUCT.md ├── dependabot.yml ├── workflows │ └── test.yml └── CONTRIBUTING.md ├── example ├── README.md ├── main.go ├── go.mod └── go.sum ├── go.mod ├── .golangci.yml ├── annotation └── annotation.go ├── docker-bake.hcl ├── go.sum ├── clidocstool_yaml_test.go ├── clidocstool_md_test.go ├── clidocstool_man.go ├── README.md ├── clidocstool_man_test.go ├── Dockerfile ├── markdown.go ├── markdown_test.go ├── clidocstool.go ├── clidocstool_md.go ├── LICENSE ├── clidocstool_test.go └── clidocstool_yaml.go /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage.txt 2 | /example/docs 3 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /coverage.txt 2 | /example/docs 3 | -------------------------------------------------------------------------------- /fixtures/buildx_stop.pre.md: -------------------------------------------------------------------------------- 1 | # docker buildx stop 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of conduct 2 | 3 | - [Moby community guidelines](https://github.com/moby/moby/blob/master/CONTRIBUTING.md#moby-community-guidelines) 4 | - [Docker Code of Conduct](https://github.com/docker/code-of-conduct) 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | open-pull-requests-limit: 10 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | labels: 9 | - "dependencies" 10 | - "bot" 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example 2 | 3 | The following example will generate all supported docs for 4 | [Docker buildx](https://github.com/docker/buildx) CLI. 5 | 6 | ```console 7 | git clone https://github.com/docker/cli-docs-tool 8 | cd cli-docs-tool/example/ 9 | go mod download 10 | go run main.go 11 | ``` 12 | 13 | Generated docs will be available in `./docs` 14 | -------------------------------------------------------------------------------- /fixtures/buildx_stop.md: -------------------------------------------------------------------------------- 1 | # docker buildx stop 2 | 3 | 4 | Stop builder instance 5 | 6 | ### Options 7 | 8 | | Name | Type | Default | Description | 9 | |:------------|:---------|:--------|:-----------------------------------------| 10 | | `--builder` | `string` | | Override the configured builder instance | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /fixtures/docker-buildx-stop.1: -------------------------------------------------------------------------------- 1 | .nh 2 | .TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" 3 | 4 | .SH NAME 5 | docker-buildx-stop - Stop builder instance 6 | 7 | 8 | .SH SYNOPSIS 9 | \fBdocker buildx stop [NAME]\fP 10 | 11 | 12 | .SH DESCRIPTION 13 | Stop builder instance 14 | 15 | 16 | .SH OPTIONS INHERITED FROM PARENT COMMANDS 17 | \fB--builder\fP="" 18 | Override the configured builder instance 19 | 20 | 21 | .SH SEE ALSO 22 | \fBdocker-buildx(1)\fP 23 | -------------------------------------------------------------------------------- /fixtures/docker.1: -------------------------------------------------------------------------------- 1 | .nh 2 | .TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" 3 | 4 | .SH NAME 5 | docker - A self-sufficient runtime for containers 6 | 7 | 8 | .SH SYNOPSIS 9 | \fBdocker [OPTIONS] COMMAND [ARG...]\fP 10 | 11 | 12 | .SH DESCRIPTION 13 | A self-sufficient runtime for containers 14 | 15 | 16 | .SH OPTIONS 17 | \fB-H\fP, \fB--host\fP="unix:///var/run/docker.sock" 18 | Daemon socket to connect to 19 | 20 | 21 | .SH SEE ALSO 22 | \fBdocker-attach(1)\fP, \fBdocker-buildx(1)\fP 23 | -------------------------------------------------------------------------------- /fixtures/docker-buildx.1: -------------------------------------------------------------------------------- 1 | .nh 2 | .TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" 3 | 4 | .SH NAME 5 | docker-buildx - Docker Buildx 6 | 7 | 8 | .SH SYNOPSIS 9 | \fBdocker buildx\fP 10 | 11 | 12 | .SH DESCRIPTION 13 | Extended build capabilities with BuildKit 14 | 15 | 16 | .SH OPTIONS 17 | \fB--builder\fP="" 18 | Override the configured builder instance 19 | 20 | 21 | .SH SEE ALSO 22 | \fBdocker(1)\fP, \fBdocker-buildx-build(1)\fP, \fBdocker-buildx-dial-stdio(1)\fP, \fBdocker-buildx-stop(1)\fP 23 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/docker/cli-docs-tool 2 | 3 | go 1.23.0 4 | 5 | require ( 6 | github.com/spf13/cobra v1.10.2 7 | github.com/spf13/pflag v1.0.10 8 | github.com/stretchr/testify v1.9.0 9 | go.yaml.in/yaml/v3 v3.0.4 10 | ) 11 | 12 | require ( 13 | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect 14 | github.com/davecgh/go-spew v1.1.1 // indirect 15 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 16 | github.com/pmezard/go-difflib v1.0.0 // indirect 17 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 18 | gopkg.in/yaml.v3 v3.0.1 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /fixtures/buildx_dial-stdio.md: -------------------------------------------------------------------------------- 1 | # docker buildx dial-stdio 2 | 3 | 4 | Proxy current stdio streams to builder instance 5 | 6 | ### Options 7 | 8 | | Name | Type | Default | Description | 9 | |:-------------|:---------|:--------|:-------------------------------------------------| 10 | | `--builder` | `string` | | Override the configured builder instance | 11 | | `--platform` | `string` | | Target platform: this is used for node selection | 12 | | `--progress` | `string` | `quiet` | Set type of progress output (auto, plain, tty). | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /fixtures/docker-attach.1: -------------------------------------------------------------------------------- 1 | .nh 2 | .TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" 3 | 4 | .SH NAME 5 | docker-attach - Attach local standard input, output, and error streams to a running container 6 | 7 | 8 | .SH SYNOPSIS 9 | \fBdocker attach [OPTIONS] CONTAINER\fP 10 | 11 | 12 | .SH DESCRIPTION 13 | Attach local standard input, output, and error streams to a running container 14 | 15 | 16 | .SH OPTIONS 17 | \fB--detach-keys\fP="" 18 | Override the key sequence for detaching a container 19 | 20 | .PP 21 | \fB--no-stdin\fP[=false] 22 | Do not attach STDIN 23 | 24 | .PP 25 | \fB--sig-proxy\fP[=true] 26 | Proxy all received signals to the process 27 | 28 | 29 | .SH SEE ALSO 30 | \fBdocker(1)\fP 31 | -------------------------------------------------------------------------------- /fixtures/docker-buildx-dial-stdio.1: -------------------------------------------------------------------------------- 1 | .nh 2 | .TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" 3 | 4 | .SH NAME 5 | docker-buildx-dial-stdio - Proxy current stdio streams to builder instance 6 | 7 | 8 | .SH SYNOPSIS 9 | \fBdocker buildx dial-stdio\fP 10 | 11 | 12 | .SH DESCRIPTION 13 | Proxy current stdio streams to builder instance 14 | 15 | 16 | .SH OPTIONS 17 | \fB--platform\fP="" 18 | Target platform: this is used for node selection 19 | 20 | .PP 21 | \fB--progress\fP="quiet" 22 | Set type of progress output (auto, plain, tty). 23 | 24 | 25 | .SH OPTIONS INHERITED FROM PARENT COMMANDS 26 | \fB--builder\fP="" 27 | Override the configured builder instance 28 | 29 | 30 | .SH SEE ALSO 31 | \fBdocker-buildx(1)\fP 32 | -------------------------------------------------------------------------------- /fixtures/attach.md: -------------------------------------------------------------------------------- 1 | # docker attach 2 | 3 | 4 | Attach local standard input, output, and error streams to a running container 5 | 6 | ### Aliases 7 | 8 | `docker container attach`, `docker attach` 9 | 10 | ### Options 11 | 12 | | Name | Type | Default | Description | 13 | |:----------------|:---------|:--------|:----------------------------------------------------| 14 | | `--detach-keys` | `string` | | Override the key sequence for detaching a container | 15 | | `--no-stdin` | `bool` | | Do not attach STDIN | 16 | | `--sig-proxy` | `bool` | `true` | Proxy all received signals to the process | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | 3 | linters: 4 | default: none 5 | enable: 6 | - depguard 7 | - revive 8 | - govet 9 | - importas 10 | - ineffassign 11 | - misspell 12 | - errname 13 | - makezero 14 | - whitespace 15 | settings: 16 | depguard: 17 | rules: 18 | main: 19 | deny: 20 | - pkg: io/ioutil 21 | desc: The io/ioutil package has been deprecated, see https://go.dev/doc/go1.16#ioutil 22 | importas: 23 | no-unaliased: true 24 | exclusions: 25 | generated: lax 26 | rules: 27 | - linters: 28 | - revive 29 | text: stutters 30 | 31 | formatters: 32 | enable: 33 | - gofmt 34 | - goimports 35 | 36 | issues: 37 | max-issues-per-linter: 0 38 | max-same-issues: 0 39 | -------------------------------------------------------------------------------- /fixtures/docker.md: -------------------------------------------------------------------------------- 1 | # docker 2 | 3 | 4 | A self-sufficient runtime for containers 5 | 6 | ### Subcommands 7 | 8 | | Name | Description | 9 | |:----------------------|:------------------------------------------------------------------------------| 10 | | [`attach`](attach.md) | Attach local standard input, output, and error streams to a running container | 11 | | [`buildx`](buildx.md) | Docker Buildx | 12 | 13 | 14 | ### Options 15 | 16 | | Name | Type | Default | Description | 17 | |:---------------|:---------|:------------------------------|:----------------------------| 18 | | `-H`, `--host` | `string` | `unix:///var/run/docker.sock` | Daemon socket to connect to | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /fixtures/buildx.md: -------------------------------------------------------------------------------- 1 | # docker buildx 2 | 3 | 4 | Extended build capabilities with BuildKit 5 | 6 | ### Subcommands 7 | 8 | | Name | Description | 9 | |:-------------------------------------|:------------------------------------------------| 10 | | [`build`](buildx_build.md) | Start a build | 11 | | [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance | 12 | | [`stop`](buildx_stop.md) | Stop builder instance | 13 | 14 | 15 | ### Options 16 | 17 | | Name | Type | Default | Description | 18 | |:------------|:---------|:--------|:-----------------------------------------| 19 | | `--builder` | `string` | | Override the configured builder instance | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /fixtures/docker_buildx_stop.yaml: -------------------------------------------------------------------------------- 1 | command: docker buildx stop 2 | short: Stop builder instance 3 | long: Stop builder instance 4 | usage: docker buildx stop [NAME] 5 | pname: docker buildx 6 | plink: docker_buildx.yaml 7 | inherited_options: 8 | - option: builder 9 | value_type: string 10 | description: Override the configured builder instance 11 | deprecated: false 12 | hidden: false 13 | experimental: false 14 | experimentalcli: false 15 | kubernetes: false 16 | swarm: false 17 | - option: help 18 | value_type: bool 19 | default_value: "false" 20 | description: Print usage 21 | deprecated: false 22 | hidden: true 23 | experimental: false 24 | experimentalcli: false 25 | kubernetes: false 26 | swarm: false 27 | deprecated: false 28 | hidden: false 29 | experimental: false 30 | experimentalcli: false 31 | kubernetes: false 32 | swarm: false 33 | 34 | -------------------------------------------------------------------------------- /fixtures/docker_buildx_install.yaml: -------------------------------------------------------------------------------- 1 | command: docker buildx install 2 | short: Install buildx as a 'docker builder' alias 3 | long: Install buildx as a 'docker builder' alias 4 | usage: docker buildx install 5 | pname: docker buildx 6 | plink: docker_buildx.yaml 7 | inherited_options: 8 | - option: builder 9 | value_type: string 10 | description: Override the configured builder instance 11 | deprecated: false 12 | hidden: false 13 | experimental: false 14 | experimentalcli: false 15 | kubernetes: false 16 | swarm: false 17 | - option: help 18 | value_type: bool 19 | default_value: "false" 20 | description: Print usage 21 | deprecated: false 22 | hidden: true 23 | experimental: false 24 | experimentalcli: false 25 | kubernetes: false 26 | swarm: false 27 | deprecated: false 28 | hidden: true 29 | experimental: false 30 | experimentalcli: false 31 | kubernetes: false 32 | swarm: false 33 | 34 | -------------------------------------------------------------------------------- /fixtures/docker_buildx_imagetools_create.yaml: -------------------------------------------------------------------------------- 1 | command: docker buildx imagetools create 2 | short: Create a new image based on source images 3 | long: Create a new image based on source images 4 | usage: docker buildx imagetools create [OPTIONS] [SOURCE...] 5 | pname: docker buildx imagetools 6 | plink: docker_buildx_imagetools.yaml 7 | inherited_options: 8 | - option: builder 9 | value_type: string 10 | description: Override the configured builder instance 11 | deprecated: false 12 | hidden: false 13 | experimental: false 14 | experimentalcli: false 15 | kubernetes: false 16 | swarm: false 17 | - option: help 18 | value_type: bool 19 | default_value: "false" 20 | description: Print usage 21 | deprecated: false 22 | hidden: true 23 | experimental: false 24 | experimentalcli: false 25 | kubernetes: false 26 | swarm: false 27 | deprecated: false 28 | hidden: true 29 | experimental: false 30 | experimentalcli: false 31 | kubernetes: false 32 | swarm: false 33 | 34 | -------------------------------------------------------------------------------- /fixtures/docker.yaml: -------------------------------------------------------------------------------- 1 | command: docker 2 | short: A self-sufficient runtime for containers 3 | long: A self-sufficient runtime for containers 4 | usage: docker [OPTIONS] COMMAND [ARG...] 5 | cname: 6 | - docker attach 7 | - docker buildx 8 | clink: 9 | - docker_attach.yaml 10 | - docker_buildx.yaml 11 | options: 12 | - option: help 13 | value_type: bool 14 | default_value: "false" 15 | description: Print usage 16 | deprecated: false 17 | hidden: true 18 | experimental: false 19 | experimentalcli: false 20 | kubernetes: false 21 | swarm: false 22 | - option: host 23 | shorthand: H 24 | value_type: string 25 | default_value: unix:///var/run/docker.sock 26 | description: Daemon socket to connect to 27 | deprecated: false 28 | hidden: false 29 | experimental: false 30 | experimentalcli: false 31 | kubernetes: false 32 | swarm: false 33 | deprecated: false 34 | hidden: false 35 | experimental: false 36 | experimentalcli: false 37 | kubernetes: false 38 | swarm: false 39 | 40 | -------------------------------------------------------------------------------- /fixtures/docker_buildx_imagetools.yaml: -------------------------------------------------------------------------------- 1 | command: docker buildx imagetools 2 | short: Commands to work on images in registry 3 | long: Commands to work on images in registry 4 | usage: docker buildx imagetools 5 | pname: docker buildx 6 | plink: docker_buildx.yaml 7 | cname: 8 | - docker buildx imagetools create 9 | clink: 10 | - docker_buildx_imagetools_create.yaml 11 | inherited_options: 12 | - option: builder 13 | value_type: string 14 | description: Override the configured builder instance 15 | deprecated: false 16 | hidden: false 17 | experimental: false 18 | experimentalcli: false 19 | kubernetes: false 20 | swarm: false 21 | - option: help 22 | value_type: bool 23 | default_value: "false" 24 | description: Print usage 25 | deprecated: false 26 | hidden: true 27 | experimental: false 28 | experimentalcli: false 29 | kubernetes: false 30 | swarm: false 31 | deprecated: false 32 | hidden: true 33 | experimental: false 34 | experimentalcli: false 35 | kubernetes: false 36 | swarm: false 37 | 38 | -------------------------------------------------------------------------------- /fixtures/docker_buildx.yaml: -------------------------------------------------------------------------------- 1 | command: docker buildx 2 | short: Docker Buildx 3 | long: Extended build capabilities with BuildKit 4 | pname: docker 5 | plink: docker.yaml 6 | cname: 7 | - docker buildx build 8 | - docker buildx dial-stdio 9 | - docker buildx stop 10 | clink: 11 | - docker_buildx_build.yaml 12 | - docker_buildx_dial-stdio.yaml 13 | - docker_buildx_stop.yaml 14 | options: 15 | - option: builder 16 | value_type: string 17 | description: Override the configured builder instance 18 | deprecated: false 19 | hidden: false 20 | experimental: false 21 | experimentalcli: false 22 | kubernetes: false 23 | swarm: false 24 | inherited_options: 25 | - option: help 26 | value_type: bool 27 | default_value: "false" 28 | description: Print usage 29 | deprecated: false 30 | hidden: true 31 | experimental: false 32 | experimentalcli: false 33 | kubernetes: false 34 | swarm: false 35 | deprecated: false 36 | hidden: false 37 | experimental: false 38 | experimentalcli: false 39 | kubernetes: false 40 | swarm: false 41 | 42 | -------------------------------------------------------------------------------- /annotation/annotation.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 cli-docs-tool authors 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 | // Package annotation handles annotations for CLI commands. 16 | package annotation 17 | 18 | const ( 19 | // ExternalURL specifies an external link annotation 20 | ExternalURL = "docs.external.url" 21 | // CodeDelimiter specifies the char that will be converted as code backtick. 22 | // Can be used on cmd for inheritance or a specific flag. 23 | CodeDelimiter = "docs.code-delimiter" 24 | // DefaultValue specifies the default value for a flag. 25 | DefaultValue = "docs.default-value" 26 | ) 27 | -------------------------------------------------------------------------------- /fixtures/docker_buildx_dial-stdio.yaml: -------------------------------------------------------------------------------- 1 | command: docker buildx dial-stdio 2 | short: Proxy current stdio streams to builder instance 3 | long: Proxy current stdio streams to builder instance 4 | usage: docker buildx dial-stdio 5 | pname: docker buildx 6 | plink: docker_buildx.yaml 7 | options: 8 | - option: platform 9 | value_type: string 10 | description: 'Target platform: this is used for node selection' 11 | deprecated: false 12 | hidden: false 13 | experimental: false 14 | experimentalcli: false 15 | kubernetes: false 16 | swarm: false 17 | - option: progress 18 | value_type: string 19 | default_value: quiet 20 | description: Set type of progress output (auto, plain, tty). 21 | deprecated: false 22 | hidden: false 23 | experimental: false 24 | experimentalcli: false 25 | kubernetes: false 26 | swarm: false 27 | inherited_options: 28 | - option: builder 29 | value_type: string 30 | description: Override the configured builder instance 31 | deprecated: false 32 | hidden: false 33 | experimental: false 34 | experimentalcli: false 35 | kubernetes: false 36 | swarm: false 37 | - option: help 38 | value_type: bool 39 | default_value: "false" 40 | description: Print usage 41 | deprecated: false 42 | hidden: true 43 | experimental: false 44 | experimentalcli: false 45 | kubernetes: false 46 | swarm: false 47 | deprecated: false 48 | hidden: false 49 | experimental: false 50 | experimentalcli: false 51 | kubernetes: false 52 | swarm: false 53 | 54 | -------------------------------------------------------------------------------- /docker-bake.hcl: -------------------------------------------------------------------------------- 1 | // Copyright 2021 cli-docs-tool authors 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 | target "_common" { 16 | args = { 17 | BUILDKIT_CONTEXT_KEEP_GIT_DIR = 1 18 | } 19 | } 20 | 21 | group "default" { 22 | targets = ["test"] 23 | } 24 | 25 | group "validate" { 26 | targets = ["lint", "vendor-validate", "license-validate"] 27 | } 28 | 29 | target "lint" { 30 | inherits = ["_common"] 31 | target = "lint" 32 | output = ["type=cacheonly"] 33 | } 34 | 35 | target "vendor-validate" { 36 | inherits = ["_common"] 37 | target = "vendor-validate" 38 | output = ["type=cacheonly"] 39 | } 40 | 41 | target "vendor-update" { 42 | inherits = ["_common"] 43 | target = "vendor-update" 44 | output = ["."] 45 | } 46 | 47 | target "test" { 48 | inherits = ["_common"] 49 | target = "test-coverage" 50 | output = ["."] 51 | } 52 | 53 | target "license-validate" { 54 | inherits = ["_common"] 55 | target = "license-validate" 56 | output = ["type=cacheonly"] 57 | } 58 | 59 | target "license-update" { 60 | inherits = ["_common"] 61 | target = "license-update" 62 | output = ["."] 63 | } 64 | -------------------------------------------------------------------------------- /fixtures/docker_attach.yaml: -------------------------------------------------------------------------------- 1 | command: docker attach 2 | aliases: docker container attach, docker attach 3 | short: | 4 | Attach local standard input, output, and error streams to a running container 5 | long: | 6 | Attach local standard input, output, and error streams to a running container 7 | usage: docker attach [OPTIONS] CONTAINER 8 | pname: docker 9 | plink: docker.yaml 10 | options: 11 | - option: detach-keys 12 | value_type: string 13 | description: Override the key sequence for detaching a container 14 | deprecated: false 15 | hidden: false 16 | experimental: false 17 | experimentalcli: false 18 | kubernetes: false 19 | swarm: false 20 | - option: no-stdin 21 | value_type: bool 22 | default_value: "false" 23 | description: Do not attach STDIN 24 | deprecated: false 25 | hidden: false 26 | experimental: false 27 | experimentalcli: false 28 | kubernetes: false 29 | swarm: false 30 | - option: sig-proxy 31 | value_type: bool 32 | default_value: "true" 33 | description: Proxy all received signals to the process 34 | deprecated: false 35 | hidden: false 36 | experimental: false 37 | experimentalcli: false 38 | kubernetes: false 39 | swarm: false 40 | inherited_options: 41 | - option: help 42 | value_type: bool 43 | default_value: "false" 44 | description: Print usage 45 | deprecated: false 46 | hidden: true 47 | experimental: false 48 | experimentalcli: false 49 | kubernetes: false 50 | swarm: false 51 | deprecated: false 52 | hidden: false 53 | experimental: false 54 | experimentalcli: false 55 | kubernetes: false 56 | swarm: false 57 | 58 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 2 | github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= 3 | github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 7 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 8 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 11 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 12 | github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= 13 | github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 14 | github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 15 | github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= 16 | github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 17 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 18 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 19 | go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= 20 | go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 22 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 23 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 24 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: test 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | - 'releases/v*' 8 | tags: 9 | - 'v*' 10 | pull_request: 11 | 12 | jobs: 13 | prepare-validate: 14 | runs-on: ubuntu-latest 15 | outputs: 16 | targets: ${{ steps.generate.outputs.targets }} 17 | steps: 18 | - 19 | name: Checkout 20 | uses: actions/checkout@v6 21 | - 22 | name: List targets 23 | id: generate 24 | uses: docker/bake-action/subaction/list-targets@v6 25 | with: 26 | target: validate 27 | 28 | validate: 29 | runs-on: ubuntu-latest 30 | needs: 31 | - prepare-validate 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | target: ${{ fromJson(needs.prepare-validate.outputs.targets) }} 36 | steps: 37 | - 38 | name: Validate 39 | uses: docker/bake-action@v6 40 | with: 41 | targets: ${{ matrix.target }} 42 | 43 | test: 44 | runs-on: ubuntu-latest 45 | steps: 46 | - 47 | name: Test 48 | uses: docker/bake-action@v6 49 | with: 50 | targets: test 51 | - 52 | name: Upload coverage 53 | uses: codecov/codecov-action@v5 54 | with: 55 | files: ./coverage.txt 56 | token: ${{ secrets.CODECOV_TOKEN }} 57 | 58 | example: 59 | runs-on: ubuntu-latest 60 | steps: 61 | - 62 | name: Checkout 63 | uses: actions/checkout@v6 64 | - 65 | name: Set up Go 66 | uses: actions/setup-go@v6 67 | with: 68 | go-version: "1.24" 69 | - 70 | name: Download modules 71 | run: | 72 | go mod download 73 | working-directory: ./example 74 | - 75 | name: Run 76 | run: | 77 | go run main.go 78 | working-directory: ./example 79 | - 80 | name: List docs 81 | run: | 82 | tree -nh ./example/docs 83 | - 84 | name: Upload docs 85 | uses: actions/upload-artifact@v5 86 | with: 87 | name: example-docs 88 | path: ./example/docs/* 89 | if-no-files-found: error 90 | -------------------------------------------------------------------------------- /clidocstool_yaml_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import ( 18 | "io/fs" 19 | "os" 20 | "path/filepath" 21 | "strings" 22 | "testing" 23 | 24 | "github.com/stretchr/testify/assert" 25 | "github.com/stretchr/testify/require" 26 | ) 27 | 28 | //nolint:errcheck 29 | func TestGenYamlTree(t *testing.T) { 30 | setup() 31 | tmpdir := t.TempDir() 32 | 33 | c, err := New(Options{ 34 | Root: dockerCmd, 35 | SourceDir: tmpdir, 36 | Plugin: false, 37 | }) 38 | require.NoError(t, err) 39 | require.NoError(t, c.GenYamlTree(dockerCmd)) 40 | 41 | seen := make(map[string]struct{}) 42 | 43 | _ = filepath.Walk("fixtures", func(path string, info fs.FileInfo, _ error) error { 44 | fname := filepath.Base(path) 45 | // ignore dirs and any file that is not a .yaml file 46 | if info.IsDir() || !strings.HasSuffix(fname, ".yaml") { 47 | return nil 48 | } 49 | t.Run(fname, func(t *testing.T) { 50 | seen[fname] = struct{}{} 51 | require.NoError(t, err) 52 | 53 | bres, err := os.ReadFile(filepath.Join(tmpdir, fname)) 54 | require.NoError(t, err) 55 | 56 | bexc, err := os.ReadFile(path) 57 | require.NoError(t, err) 58 | assert.Equal(t, string(bexc), string(bres)) 59 | }) 60 | return nil 61 | }) 62 | 63 | _ = filepath.Walk(tmpdir, func(path string, info fs.FileInfo, _ error) error { 64 | fname := filepath.Base(path) 65 | // ignore dirs and any file that is not a .yaml file 66 | if info.IsDir() || !strings.HasSuffix(fname, ".yaml") { 67 | return nil 68 | } 69 | t.Run("seen_"+fname, func(t *testing.T) { 70 | if _, ok := seen[fname]; !ok { 71 | t.Errorf("file %s not found in fixtures", fname) 72 | } 73 | }) 74 | return nil 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /clidocstool_md_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import ( 18 | "io/fs" 19 | "os" 20 | "path" 21 | "path/filepath" 22 | "strings" 23 | "testing" 24 | 25 | "github.com/stretchr/testify/require" 26 | ) 27 | 28 | //nolint:errcheck 29 | func TestGenMarkdownTree(t *testing.T) { 30 | setup() 31 | tmpdir := t.TempDir() 32 | 33 | require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) 34 | 35 | c, err := New(Options{ 36 | Root: dockerCmd, 37 | SourceDir: tmpdir, 38 | Plugin: false, 39 | }) 40 | require.NoError(t, err) 41 | require.NoError(t, c.GenMarkdownTree(dockerCmd)) 42 | 43 | seen := make(map[string]struct{}) 44 | 45 | _ = filepath.Walk("fixtures", func(path string, info fs.FileInfo, _ error) error { 46 | fname := filepath.Base(path) 47 | // ignore dirs, .pre.md files and any file that is not a .md file 48 | if info.IsDir() || !strings.HasSuffix(fname, ".md") || strings.HasSuffix(fname, ".pre.md") { 49 | return nil 50 | } 51 | t.Run(fname, func(t *testing.T) { 52 | seen[fname] = struct{}{} 53 | require.NoError(t, err) 54 | 55 | bres, err := os.ReadFile(filepath.Join(tmpdir, fname)) 56 | require.NoError(t, err) 57 | 58 | bexc, err := os.ReadFile(path) 59 | require.NoError(t, err) 60 | require.Equal(t, string(bexc), string(bres)) 61 | }) 62 | return nil 63 | }) 64 | 65 | _ = filepath.Walk(tmpdir, func(path string, info fs.FileInfo, _ error) error { 66 | fname := filepath.Base(path) 67 | // ignore dirs, .pre.md files and any file that is not a .md file 68 | if info.IsDir() || !strings.HasSuffix(fname, ".md") || strings.HasSuffix(fname, ".pre.md") { 69 | return nil 70 | } 71 | t.Run("seen_"+fname, func(t *testing.T) { 72 | if _, ok := seen[fname]; !ok { 73 | t.Errorf("file %s not found in fixtures", fname) 74 | } 75 | }) 76 | return nil 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /clidocstool_man.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import ( 18 | "fmt" 19 | "log" 20 | "os" 21 | "strconv" 22 | "time" 23 | 24 | "github.com/spf13/cobra" 25 | "github.com/spf13/cobra/doc" 26 | ) 27 | 28 | // GenManTree generates a man page for the command and all descendants. 29 | // If SOURCE_DATE_EPOCH is set, in order to allow reproducible package 30 | // builds, we explicitly set the build time to SOURCE_DATE_EPOCH. 31 | func (c *Client) GenManTree(cmd *cobra.Command) error { 32 | if err := c.loadLongDescription(cmd, "man"); err != nil { 33 | return err 34 | } 35 | 36 | if epoch := os.Getenv("SOURCE_DATE_EPOCH"); c.manHeader != nil && epoch != "" { 37 | unixEpoch, err := strconv.ParseInt(epoch, 10, 64) 38 | if err != nil { 39 | return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err) 40 | } 41 | now := time.Unix(unixEpoch, 0) 42 | c.manHeader.Date = &now 43 | } 44 | 45 | return c.genManTreeCustom(cmd) 46 | } 47 | 48 | func (c *Client) genManTreeCustom(cmd *cobra.Command) error { 49 | for _, sc := range cmd.Commands() { 50 | if err := c.genManTreeCustom(sc); err != nil { 51 | return err 52 | } 53 | } 54 | 55 | // always disable the addition of [flags] to the usage 56 | cmd.DisableFlagsInUseLine = true 57 | 58 | // always disable "spf13/cobra" auto gen tag 59 | cmd.DisableAutoGenTag = true 60 | 61 | // Skip the root command altogether, to prevent generating a useless 62 | // md file for plugins. 63 | if c.plugin && !cmd.HasParent() { 64 | return nil 65 | } 66 | 67 | // Skip hidden command recursively 68 | for curr := cmd; curr != nil; curr = curr.Parent() { 69 | if curr.Hidden { 70 | log.Printf("INFO: Skipping Man for %q (hidden command)", curr.CommandPath()) 71 | return nil 72 | } 73 | } 74 | 75 | log.Printf("INFO: Generating Man for %q", cmd.CommandPath()) 76 | 77 | return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ 78 | Header: c.manHeader, 79 | Path: c.target, 80 | CommandSeparator: "-", 81 | }) 82 | } 83 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![PkgGoDev](https://img.shields.io/badge/go.dev-docs-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/docker/cli-docs-tool) 2 | [![Test Status](https://img.shields.io/github/actions/workflow/status/docker/cli-docs-tool/test.yml?branch=main&label=test&logo=github&style=flat-square)](https://github.com/docker/cli-docs-tool/actions?query=workflow%3Atest) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/docker/cli-docs-tool)](https://goreportcard.com/report/github.com/docker/cli-docs-tool) 4 | 5 | ## About 6 | 7 | This is a library containing utilities to generate (reference) documentation 8 | for the [`docker` CLI](https://github.com/docker/cli) on [docs.docker.com](https://docs.docker.com/reference/). 9 | 10 | ## Disclaimer 11 | 12 | This library is intended for use by Docker's CLIs, and is not intended to be a 13 | general-purpose utility. Various bits are hard-coded or make assumptions that 14 | are very specific to our use-case. Contributions are welcome, but we will not 15 | accept contributions to make this a general-purpose module. 16 | 17 | ## Usage 18 | 19 | To generate the documentation it's recommended to do so using a Go submodule 20 | in your repository. 21 | 22 | We will use the example of `docker/buildx` and create a Go submodule in a 23 | `docs` folder (recommended): 24 | 25 | ```console 26 | $ mkdir docs 27 | $ cd ./docs 28 | $ go mod init github.com/docker/buildx/docs 29 | $ go get github.com/docker/cli-docs-tool 30 | ``` 31 | 32 | Your `go.mod` should look like this: 33 | 34 | ```text 35 | module github.com/docker/buildx/docs 36 | 37 | go 1.16 38 | 39 | require ( 40 | github.com/docker/cli-docs-tool v0.0.0 41 | ) 42 | ``` 43 | 44 | Next, create a file named `main.go` inside that directory containing the 45 | following Go code from [`example/main.go`](example/main.go). 46 | 47 | Running this example should produce the following output: 48 | 49 | ```console 50 | $ go run main.go 51 | INFO: Generating Markdown for "docker buildx bake" 52 | INFO: Generating Markdown for "docker buildx build" 53 | INFO: Generating Markdown for "docker buildx create" 54 | INFO: Generating Markdown for "docker buildx du" 55 | ... 56 | INFO: Generating YAML for "docker buildx uninstall" 57 | INFO: Generating YAML for "docker buildx use" 58 | INFO: Generating YAML for "docker buildx version" 59 | INFO: Generating YAML for "docker buildx" 60 | ``` 61 | 62 | Generated docs will be available in the `./docs` folder of the project. 63 | 64 | ## Contributing 65 | 66 | Want to contribute? Awesome! You can find information about contributing to 67 | this project in the [CONTRIBUTING.md](/.github/CONTRIBUTING.md) 68 | -------------------------------------------------------------------------------- /fixtures/docker-buildx-build.1: -------------------------------------------------------------------------------- 1 | .nh 2 | .TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" 3 | 4 | .SH NAME 5 | docker-buildx-build - Start a build 6 | 7 | 8 | .SH SYNOPSIS 9 | \fBdocker buildx build [OPTIONS] PATH | URL | -\fP 10 | 11 | 12 | .SH DESCRIPTION 13 | Start a build 14 | 15 | 16 | .SH OPTIONS 17 | \fB--add-host\fP=[] 18 | Add a custom host-to-IP mapping (format: 'host:ip') 19 | 20 | .PP 21 | \fB--allow\fP=[] 22 | Allow extra privileged entitlement (e.g., "network.host", "security.insecure") 23 | 24 | .PP 25 | \fB--build-arg\fP=[] 26 | Set build-time variables 27 | 28 | .PP 29 | \fB--cache-from\fP=[] 30 | External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir") 31 | 32 | .PP 33 | \fB--cache-to\fP=[] 34 | Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir") 35 | 36 | .PP 37 | \fB--cgroup-parent\fP="" 38 | Optional parent cgroup for the container 39 | 40 | .PP 41 | \fB--detach\fP[=true] 42 | Dummy flag that tests boolean flags with true as default 43 | 44 | .PP 45 | \fB-f\fP, \fB--file\fP="" 46 | Name of the Dockerfile (default: "PATH/Dockerfile") 47 | 48 | .PP 49 | \fB--iidfile\fP="" 50 | Write the image ID to the file 51 | 52 | .PP 53 | \fB--label\fP=[] 54 | Set metadata for an image 55 | 56 | .PP 57 | \fB--load\fP[=false] 58 | Shorthand for "--output=type=docker" 59 | 60 | .PP 61 | \fB--network\fP="default" 62 | Set the networking mode for the "RUN" instructions during build 63 | 64 | .PP 65 | \fB-o\fP, \fB--output\fP=[] 66 | Output destination (format: "type=local,dest=path") 67 | 68 | .PP 69 | \fB--platform\fP=[] 70 | Set target platform for build 71 | 72 | .PP 73 | \fB--push\fP[=false] 74 | Shorthand for "--output=type=registry" 75 | 76 | .PP 77 | \fB-q\fP, \fB--quiet\fP[=false] 78 | Suppress the build output and print image ID on success 79 | 80 | .PP 81 | \fB--secret\fP=[] 82 | Secret file to expose to the build (format: "id=mysecret,src=/local/secret") 83 | 84 | .PP 85 | \fB--shm-size\fP="" 86 | Size of "/dev/shm" 87 | 88 | .PP 89 | \fB--ssh\fP=[] 90 | SSH agent socket or keys to expose to the build 91 | format: "default|[=|[,]]" 92 | 93 | .PP 94 | \fB-t\fP, \fB--tag\fP=[] 95 | Name and optionally a tag (format: "name:tag") 96 | 97 | .PP 98 | \fB--target\fP="" 99 | Set the target build stage to build. 100 | 101 | .PP 102 | \fB--ulimit\fP="" 103 | Ulimit options 104 | 105 | 106 | .SH OPTIONS INHERITED FROM PARENT COMMANDS 107 | \fB--builder\fP="" 108 | Override the configured builder instance 109 | 110 | 111 | .SH SEE ALSO 112 | \fBdocker-buildx(1)\fP 113 | -------------------------------------------------------------------------------- /clidocstool_man_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2024 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import ( 18 | "io/fs" 19 | "os" 20 | "path" 21 | "path/filepath" 22 | "regexp" 23 | "strconv" 24 | "testing" 25 | "time" 26 | 27 | "github.com/spf13/cobra/doc" 28 | "github.com/stretchr/testify/assert" 29 | "github.com/stretchr/testify/require" 30 | ) 31 | 32 | //nolint:errcheck 33 | func TestGenManTree(t *testing.T) { 34 | setup() 35 | tmpdir := t.TempDir() 36 | 37 | epoch, err := time.Parse("2006-Jan-02", "2020-Jan-10") 38 | require.NoError(t, err) 39 | t.Setenv("SOURCE_DATE_EPOCH", strconv.FormatInt(epoch.Unix(), 10)) 40 | 41 | require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) 42 | 43 | c, err := New(Options{ 44 | Root: dockerCmd, 45 | SourceDir: tmpdir, 46 | Plugin: false, 47 | ManHeader: &doc.GenManHeader{ 48 | Title: "DOCKER", 49 | Section: "1", 50 | Source: "Docker Community", 51 | Manual: "Docker User Manuals", 52 | }, 53 | }) 54 | require.NoError(t, err) 55 | require.NoError(t, c.GenManTree(dockerCmd)) 56 | 57 | seen := make(map[string]struct{}) 58 | remanpage := regexp.MustCompile(`\.\d+$`) 59 | 60 | _ = filepath.Walk("fixtures", func(path string, info fs.FileInfo, _ error) error { 61 | fname := filepath.Base(path) 62 | // ignore dirs and any file that is not a manpage 63 | if info.IsDir() || !remanpage.MatchString(fname) { 64 | return nil 65 | } 66 | t.Run(fname, func(t *testing.T) { 67 | seen[fname] = struct{}{} 68 | require.NoError(t, err) 69 | 70 | bres, err := os.ReadFile(filepath.Join(tmpdir, fname)) 71 | require.NoError(t, err) 72 | 73 | bexc, err := os.ReadFile(path) 74 | require.NoError(t, err) 75 | assert.Equal(t, string(bexc), string(bres)) 76 | }) 77 | return nil 78 | }) 79 | 80 | _ = filepath.Walk(tmpdir, func(path string, info fs.FileInfo, _ error) error { 81 | fname := filepath.Base(path) 82 | // ignore dirs and any file that is not a manpage 83 | if info.IsDir() || !remanpage.MatchString(fname) { 84 | return nil 85 | } 86 | t.Run("seen_"+fname, func(t *testing.T) { 87 | if _, ok := seen[fname]; !ok { 88 | t.Errorf("file %s not found in fixtures", fname) 89 | } 90 | }) 91 | return nil 92 | }) 93 | } 94 | -------------------------------------------------------------------------------- /example/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 cli-docs-tool authors 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 | package main 16 | 17 | import ( 18 | "log" 19 | "os" 20 | 21 | "github.com/docker/buildx/commands" 22 | clidocstool "github.com/docker/cli-docs-tool" 23 | "github.com/docker/cli/cli/command" 24 | "github.com/spf13/cobra" 25 | "github.com/spf13/cobra/doc" 26 | "github.com/spf13/pflag" 27 | 28 | // import drivers otherwise factories are empty 29 | // for --driver output flag usage 30 | _ "github.com/docker/buildx/driver/docker" 31 | _ "github.com/docker/buildx/driver/docker-container" 32 | _ "github.com/docker/buildx/driver/kubernetes" 33 | _ "github.com/docker/buildx/driver/remote" 34 | ) 35 | 36 | const ( 37 | pluginName = "buildx" 38 | defaultSourcePath = "docs/" 39 | ) 40 | 41 | type options struct { 42 | source string 43 | target string 44 | } 45 | 46 | func gen(opts *options) error { 47 | log.SetFlags(0) 48 | 49 | // create a new instance of Docker CLI 50 | dockerCLI, err := command.NewDockerCli() 51 | if err != nil { 52 | return err 53 | } 54 | 55 | // root command 56 | cmd := &cobra.Command{ 57 | Use: "docker [OPTIONS] COMMAND [ARG...]", 58 | Short: "The base command for the Docker CLI.", 59 | } 60 | 61 | // subcommand for the plugin 62 | cmd.AddCommand(commands.NewRootCmd(pluginName, true, dockerCLI)) 63 | 64 | // create a new instance of cli-docs-tool 65 | c, err := clidocstool.New(clidocstool.Options{ 66 | Root: cmd, 67 | SourceDir: opts.source, 68 | TargetDir: opts.target, 69 | Plugin: true, 70 | ManHeader: &doc.GenManHeader{ 71 | Title: "BUILDX", 72 | Section: "1", 73 | Source: "Docker Community", 74 | Manual: "Docker User Manuals", 75 | }, 76 | }) 77 | if err != nil { 78 | return err 79 | } 80 | 81 | // generate all supported docs formats 82 | return c.GenAllTree() 83 | } 84 | 85 | func run() error { 86 | opts := &options{} 87 | flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError) 88 | flags.StringVar(&opts.source, "source", defaultSourcePath, "Docs source folder") 89 | flags.StringVar(&opts.target, "target", defaultSourcePath, "Docs target folder") 90 | if err := flags.Parse(os.Args[1:]); err != nil { 91 | return err 92 | } 93 | return gen(opts) 94 | } 95 | 96 | func main() { 97 | if err := run(); err != nil { 98 | log.Printf("ERROR: %+v", err) 99 | os.Exit(1) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | # Copyright 2021 cli-docs-tool authors 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ARG GO_VERSION="1.24" 18 | ARG XX_VERSION="1.9.0" 19 | ARG GOLANGCI_LINT_VERSION="v2.7.1" 20 | ARG ADDLICENSE_VERSION="v1.2.0" 21 | 22 | ARG LICENSE_ARGS="-c cli-docs-tool -l apache" 23 | ARG LICENSE_FILES=".*\(Dockerfile\|\.go\|\.hcl\|\.sh\)" 24 | 25 | FROM golangci/golangci-lint:${GOLANGCI_LINT_VERSION}-alpine AS golangci-lint 26 | FROM --platform=$BUILDPLATFORM tonistiigi/xx:${XX_VERSION} AS xx 27 | 28 | FROM --platform=$BUILDPLATFORM golang:${GO_VERSION}-alpine AS base 29 | RUN apk add --no-cache cpio findutils git linux-headers 30 | ENV CGO_ENABLED=0 31 | WORKDIR /src 32 | COPY --link --from=xx / / 33 | 34 | FROM base AS addlicense 35 | ARG ADDLICENSE_VERSION 36 | ARG TARGETPLATFORM 37 | RUN --mount=target=/root/.cache,type=cache \ 38 | --mount=type=cache,target=/go/pkg/mod <&2 'ERROR: Vendor result differs. Please vendor your package with "docker buildx bake vendor"' 66 | echo "$diff" 67 | exit 1 68 | fi 69 | EOT 70 | 71 | FROM base AS lint 72 | RUN --mount=type=bind,target=. \ 73 | --mount=type=cache,target=/root/.cache \ 74 | --mount=from=golangci-lint,source=/usr/bin/golangci-lint,target=/usr/bin/golangci-lint \ 75 | golangci-lint run ./... 76 | 77 | FROM base AS license-set 78 | ARG LICENSE_ARGS 79 | ARG LICENSE_FILES 80 | RUN --mount=type=bind,target=.,rw \ 81 | --mount=from=addlicense,source=/out/addlicense,target=/usr/bin/addlicense \ 82 | find . -regex "${LICENSE_FILES}" | xargs addlicense ${LICENSE_ARGS} \ 83 | && mkdir /out \ 84 | && find . -regex "${LICENSE_FILES}" | cpio -pdm /out 85 | 86 | FROM scratch AS license-update 87 | COPY --from=set /out / 88 | 89 | FROM base AS license-validate 90 | ARG LICENSE_ARGS 91 | ARG LICENSE_FILES 92 | RUN --mount=type=bind,target=. \ 93 | --mount=from=addlicense,source=/out/addlicense,target=/usr/bin/addlicense \ 94 | find . -regex "${LICENSE_FILES}" | xargs addlicense -check ${LICENSE_ARGS} 95 | 96 | FROM vendored AS test 97 | RUN --mount=type=bind,target=. \ 98 | --mount=type=cache,target=/root/.cache \ 99 | --mount=type=cache,target=/go/pkg/mod \ 100 | go test -v -coverprofile=/tmp/coverage.txt -covermode=atomic ./... 101 | 102 | FROM scratch AS test-coverage 103 | COPY --from=test /tmp/coverage.txt /coverage.txt 104 | -------------------------------------------------------------------------------- /markdown.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import ( 18 | "regexp" 19 | "strings" 20 | "unicode" 21 | ) 22 | 23 | var ( 24 | // mdHeading matches MarkDown H1..h6 headings. Note that this regex may produce 25 | // false positives for (e.g.) comments in code-blocks (# this is a comment), 26 | // so should not be used as a generic regex for other purposes. 27 | mdHeading = regexp.MustCompile(`^([#]{1,6})\s(.*)$`) 28 | // htmlAnchor matches inline HTML anchors. This is intended to only match anchors 29 | // for our use-case; DO NOT consider using this as a generic regex, or at least 30 | // not before reading https://stackoverflow.com/a/1732454/1811501. 31 | htmlAnchor = regexp.MustCompile(`\s*`) 32 | // relativeLink matches parts of internal links between .md documents 33 | // e.g. "](buildx_build.md)" 34 | relativeLink = regexp.MustCompile(`\]\((\.\/)?[a-z-_]+\.md(#.*)?\)`) 35 | ) 36 | 37 | // getSections returns all H2 sections by title (lowercase) 38 | func getSections(mdString string) map[string]string { 39 | parsedContent := strings.Split("\n"+mdString, "\n## ") 40 | sections := make(map[string]string, len(parsedContent)) 41 | for _, s := range parsedContent { 42 | if strings.HasPrefix(s, "#") { 43 | // not a H2 Section 44 | continue 45 | } 46 | parts := strings.SplitN(s, "\n", 2) 47 | if len(parts) == 2 { 48 | sections[strings.ToLower(parts[0])] = parts[1] 49 | } 50 | } 51 | return sections 52 | } 53 | 54 | // cleanupMarkDown cleans up the MarkDown passed in mdString for inclusion in 55 | // YAML. It removes trailing whitespace and substitutes tabs for four spaces 56 | // to prevent YAML switching to use "compact" form; ("line1 \nline\t2\n") 57 | // which, although equivalent, is hard to read. 58 | func cleanupMarkDown(mdString string) (md string, anchors []string) { 59 | // remove leading/trailing whitespace, and replace tabs in the whole content 60 | mdString = strings.TrimSpace(mdString) 61 | mdString = strings.ReplaceAll(mdString, "\t", " ") 62 | mdString = strings.ReplaceAll(mdString, "https://docs.docker.com", "") 63 | 64 | // Rewrite internal links, replacing relative paths with absolute path 65 | // e.g. from [docker buildx build](buildx_build.md#build-arg) 66 | // to [docker buildx build](/reference/cli/docker/buildx/build/#build-arg) 67 | mdString = relativeLink.ReplaceAllStringFunc(mdString, func(link string) string { 68 | link = strings.TrimLeft(link, "](./") 69 | link = strings.ReplaceAll(link, "_", "/") 70 | link = strings.ReplaceAll(link, ".md", "/") 71 | return "](/reference/cli/docker/" + link 72 | }) 73 | 74 | var id string 75 | // replace trailing whitespace per line, and handle custom anchors 76 | lines := strings.Split(mdString, "\n") 77 | for i := 0; i < len(lines); i++ { 78 | lines[i] = strings.TrimRightFunc(lines[i], unicode.IsSpace) 79 | lines[i], id = convertHTMLAnchor(lines[i]) 80 | if id != "" { 81 | anchors = append(anchors, id) 82 | } 83 | } 84 | return strings.Join(lines, "\n"), anchors 85 | } 86 | 87 | // convertHTMLAnchor converts inline anchor-tags in headings () 88 | // to an extended-markdown property ({#myanchor}). Extended Markdown properties 89 | // are not supported in GitHub Flavored Markdown, but are supported by Jekyll, 90 | // and lead to cleaner HTML in our docs, and prevents duplicate anchors. 91 | // It returns the converted MarkDown heading and the custom ID (if present) 92 | func convertHTMLAnchor(mdLine string) (md string, customID string) { 93 | if m := mdHeading.FindStringSubmatch(mdLine); len(m) > 0 { 94 | if a := htmlAnchor.FindStringSubmatch(m[2]); len(a) > 0 { 95 | customID = a[1] 96 | mdLine = m[1] + " " + htmlAnchor.ReplaceAllString(m[2], "") + " {#" + customID + "}" 97 | } 98 | } 99 | return mdLine, customID 100 | } 101 | -------------------------------------------------------------------------------- /markdown_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import "testing" 18 | 19 | func TestCleanupMarkDown(t *testing.T) { 20 | tests := []struct { 21 | doc, in, expected string 22 | }{ 23 | { 24 | doc: "whitespace around sections", 25 | in: ` 26 | 27 | ## Section start 28 | 29 | Some lines. 30 | And more lines. 31 | 32 | `, 33 | expected: `## Section start 34 | 35 | Some lines. 36 | And more lines.`, 37 | }, 38 | { 39 | doc: "lines with inline tabs", 40 | in: `## Some Heading 41 | 42 | A line with tabs in it. 43 | Tabs should be replaced by spaces`, 44 | expected: `## Some Heading 45 | 46 | A line with tabs in it. 47 | Tabs should be replaced by spaces`, 48 | }, 49 | { 50 | doc: "lines with trailing spaces", 51 | in: `## Some Heading with spaces 52 | 53 | This is a line. 54 | This is an indented line 55 | 56 | ### Some other heading 57 | 58 | Last line.`, 59 | expected: `## Some Heading with spaces 60 | 61 | This is a line. 62 | This is an indented line 63 | 64 | ### Some other heading 65 | 66 | Last line.`, 67 | }, 68 | { 69 | doc: "lines with trailing tabs", 70 | in: `## Some Heading with tabs 71 | 72 | This is a line. 73 | This is an indented line 74 | 75 | ### Some other heading 76 | 77 | Last line.`, 78 | expected: `## Some Heading with tabs 79 | 80 | This is a line. 81 | This is an indented line 82 | 83 | ### Some other heading 84 | 85 | Last line.`, 86 | }, 87 | { 88 | doc: "Link preprocessing", 89 | in: `[link1](https://example.com/) 90 | [link2](https://docs.docker.com/foo/bar/) 91 | [link3](buildx_build.md) 92 | [link4](buildx_imagetools_create.md) 93 | [link5](buildx_build.md#build-arg) 94 | [link6](./swarm_join-token.md)`, 95 | expected: `[link1](https://example.com/) 96 | [link2](/foo/bar/) 97 | [link3](/reference/cli/docker/buildx/build/) 98 | [link4](/reference/cli/docker/buildx/imagetools/create/) 99 | [link5](/reference/cli/docker/buildx/build/#build-arg) 100 | [link6](/reference/cli/docker/swarm/join-token/)`, 101 | }, 102 | } 103 | for _, tc := range tests { 104 | tc := tc 105 | t.Run(tc.doc, func(t *testing.T) { 106 | out, _ := cleanupMarkDown(tc.in) 107 | if out != tc.expected { 108 | t.Fatalf("\nexpected:\n%q\nactual:\n%q\n", tc.expected, out) 109 | } 110 | }) 111 | } 112 | } 113 | 114 | func TestConvertHTMLAnchor(t *testing.T) { 115 | tests := []struct { 116 | in, id, expected string 117 | }{ 118 | { 119 | in: `# Heading 1`, 120 | id: "heading1", 121 | expected: `# Heading 1 {#heading1}`, 122 | }, 123 | { 124 | in: `## Heading 2 `, 125 | id: "heading2", 126 | expected: `## Heading 2 {#heading2}`, 127 | }, 128 | { 129 | in: `### Heading 3`, 130 | id: "heading3", 131 | expected: `### Heading 3 {#heading3}`, 132 | }, 133 | { 134 | in: `#### Heading 4`, 135 | id: "heading4", 136 | expected: `#### Heading 4 {#heading4}`, 137 | }, 138 | { 139 | in: `##### Heading 5`, 140 | id: "heading5", 141 | expected: `##### Heading 5 {#heading5}`, 142 | }, 143 | { 144 | in: `###### hello!Heading 6`, 145 | id: "", 146 | expected: `###### hello!Heading 6`, 147 | }, 148 | } 149 | for _, tc := range tests { 150 | tc := tc 151 | t.Run(tc.in, func(t *testing.T) { 152 | out, id := convertHTMLAnchor(tc.in) 153 | if id != tc.id { 154 | t.Fatalf("expected: %s, actual: %s\n", tc.id, id) 155 | } 156 | if out != tc.expected { 157 | t.Fatalf("\nexpected: %s\nactual: %s\n", tc.expected, out) 158 | } 159 | }) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /clidocstool.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 cli-docs-tool authors 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 | // Package clidocstool provides tools for generating CLI documentation. 16 | package clidocstool 17 | 18 | import ( 19 | "errors" 20 | "io" 21 | "log" 22 | "os" 23 | "path/filepath" 24 | "strings" 25 | 26 | "github.com/spf13/cobra" 27 | "github.com/spf13/cobra/doc" 28 | ) 29 | 30 | // Options defines options for cli-docs-tool 31 | type Options struct { 32 | Root *cobra.Command 33 | SourceDir string 34 | TargetDir string 35 | Plugin bool 36 | 37 | ManHeader *doc.GenManHeader 38 | } 39 | 40 | // Client represents an active cli-docs-tool object 41 | type Client struct { 42 | root *cobra.Command 43 | source string 44 | target string 45 | plugin bool 46 | 47 | manHeader *doc.GenManHeader 48 | } 49 | 50 | // New initializes a new cli-docs-tool client 51 | func New(opts Options) (*Client, error) { 52 | if opts.Root == nil { 53 | return nil, errors.New("root cmd required") 54 | } 55 | if len(opts.SourceDir) == 0 { 56 | return nil, errors.New("source dir required") 57 | } 58 | c := &Client{ 59 | root: opts.Root, 60 | source: opts.SourceDir, 61 | plugin: opts.Plugin, 62 | manHeader: opts.ManHeader, 63 | } 64 | if len(opts.TargetDir) == 0 { 65 | c.target = c.source 66 | } else { 67 | c.target = opts.TargetDir 68 | } 69 | if err := os.MkdirAll(c.target, 0o755); err != nil { 70 | return nil, err 71 | } 72 | return c, nil 73 | } 74 | 75 | // GenAllTree creates all structured ref files for this command and 76 | // all descendants in the directory given. 77 | func (c *Client) GenAllTree() error { 78 | var err error 79 | if err = c.GenMarkdownTree(c.root); err != nil { 80 | return err 81 | } 82 | if err = c.GenYamlTree(c.root); err != nil { 83 | return err 84 | } 85 | if err = c.GenManTree(c.root); err != nil { 86 | return err 87 | } 88 | return nil 89 | } 90 | 91 | // loadLongDescription gets long descriptions and examples from markdown. 92 | func (c *Client) loadLongDescription(cmd *cobra.Command, generator string) error { 93 | if cmd.HasSubCommands() { 94 | for _, sub := range cmd.Commands() { 95 | if err := c.loadLongDescription(sub, generator); err != nil { 96 | return err 97 | } 98 | } 99 | } 100 | name := cmd.CommandPath() 101 | if i := strings.Index(name, " "); i >= 0 { 102 | // remove root command / binary name 103 | name = name[i+1:] 104 | } 105 | if name == "" { 106 | return nil 107 | } 108 | mdFile := strings.ReplaceAll(name, " ", "_") + ".md" 109 | sourcePath := filepath.Join(c.source, mdFile) 110 | content, err := os.ReadFile(sourcePath) 111 | if os.IsNotExist(err) { 112 | log.Printf("WARN: %s does not exist, skipping Markdown examples for %s docs\n", mdFile, generator) 113 | return nil 114 | } 115 | if err != nil { 116 | return err 117 | } 118 | applyDescriptionAndExamples(cmd, string(content)) 119 | return nil 120 | } 121 | 122 | // applyDescriptionAndExamples fills in cmd.Long and cmd.Example with the 123 | // "Description" and "Examples" H2 sections in mdString (if present). 124 | func applyDescriptionAndExamples(cmd *cobra.Command, mdString string) { 125 | sections := getSections(mdString) 126 | var ( 127 | anchors []string 128 | md string 129 | ) 130 | if sections["description"] != "" { 131 | md, anchors = cleanupMarkDown(sections["description"]) 132 | cmd.Long = md 133 | anchors = append(anchors, md) 134 | } 135 | if sections["examples"] != "" { 136 | md, anchors = cleanupMarkDown(sections["examples"]) 137 | cmd.Example = md 138 | anchors = append(anchors, md) 139 | } 140 | if len(anchors) > 0 { 141 | if cmd.Annotations == nil { 142 | cmd.Annotations = make(map[string]string) 143 | } 144 | cmd.Annotations["anchors"] = strings.Join(anchors, ",") 145 | } 146 | } 147 | 148 | func fileExists(f string) bool { 149 | info, err := os.Stat(f) 150 | if os.IsNotExist(err) { 151 | return false 152 | } 153 | return !info.IsDir() 154 | } 155 | 156 | func copyFile(src string, dst string) error { 157 | sf, err := os.Open(src) 158 | if err != nil { 159 | return err 160 | } 161 | defer sf.Close() 162 | df, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY, 0o600) 163 | if err != nil { 164 | return err 165 | } 166 | defer df.Close() 167 | _, err = io.Copy(df, sf) 168 | return err 169 | } 170 | 171 | func getAliases(cmd *cobra.Command) []string { 172 | if a := cmd.Annotations["aliases"]; a != "" { 173 | aliases := strings.Split(a, ",") 174 | for i := 0; i < len(aliases); i++ { 175 | aliases[i] = strings.TrimSpace(aliases[i]) 176 | } 177 | return aliases 178 | } 179 | if len(cmd.Aliases) == 0 { 180 | return cmd.Aliases 181 | } 182 | 183 | var parentPath string 184 | if cmd.HasParent() { 185 | parentPath = cmd.Parent().CommandPath() + " " 186 | } 187 | aliases := []string{cmd.CommandPath()} 188 | for _, a := range cmd.Aliases { 189 | aliases = append(aliases, parentPath+a) 190 | } 191 | return aliases 192 | } 193 | -------------------------------------------------------------------------------- /fixtures/buildx_build.md: -------------------------------------------------------------------------------- 1 | # docker buildx build 2 | 3 | 4 | Start a build 5 | 6 | ### Aliases 7 | 8 | `docker image build`, `docker buildx build`, `docker buildx b`, `docker build` 9 | 10 | ### Options 11 | 12 | | Name | Type | Default | Description | 13 | |:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------|:----------|:-----------------------------------------------------------------------------------------------------| 14 | | [`--add-host`](https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) | 15 | | `--allow` | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) | 16 | | [`--build-arg`](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg) | `stringArray` | | Set build-time variables | 17 | | `--builder` | `string` | | Override the configured builder instance | 18 | | `--cache-from` | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) | 19 | | `--cache-to` | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) | 20 | | [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent) | `string` | | Optional parent cgroup for the container | 21 | | `--detach` | `bool` | `true` | Dummy flag that tests boolean flags with true as default | 22 | | [`-f`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) | 23 | | `--iidfile` | `string` | | Write the image ID to the file | 24 | | `--label` | `stringArray` | | Set metadata for an image | 25 | | `--load` | `bool` | | Shorthand for `--output=type=docker` | 26 | | `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build | 27 | | `-o`, `--output` | `stringArray` | | Output destination (format: `type=local,dest=path`) | 28 | | `--platform` | `stringArray` | local | Set target platform for build | 29 | | `--push` | `bool` | | Shorthand for `--output=type=registry` | 30 | | `-q`, `--quiet` | `bool` | | Suppress the build output and print image ID on success | 31 | | `--secret` | `stringArray` | | Secret file to expose to the build (format: `id=mysecret,src=/local/secret`) | 32 | | `--shm-size` | `string` | | Size of `/dev/shm` | 33 | | `--ssh` | `stringArray` | | SSH agent socket or keys to expose to the build
format: `default\|[=\|[,]]` | 34 | | [`-t`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t), [`--tag`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t) | `stringArray` | | Name and optionally a tag (format: `name:tag`) | 35 | | [`--target`](https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target) | `string` | | Set the target build stage to build. | 36 | | `--ulimit` | `string` | | Ulimit options | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /clidocstool_md.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import ( 18 | "bytes" 19 | "fmt" 20 | "log" 21 | "os" 22 | "path/filepath" 23 | "regexp" 24 | "strings" 25 | "text/tabwriter" 26 | "text/template" 27 | 28 | "github.com/docker/cli-docs-tool/annotation" 29 | "github.com/spf13/cobra" 30 | "github.com/spf13/pflag" 31 | ) 32 | 33 | var ( 34 | nlRegexp = regexp.MustCompile(`\r?\n`) 35 | adjustSep = regexp.MustCompile(`\|:---(\s+)`) 36 | ) 37 | 38 | // GenMarkdownTree will generate a markdown page for this command and all 39 | // descendants in the directory given. 40 | func (c *Client) GenMarkdownTree(cmd *cobra.Command) error { 41 | for _, sc := range cmd.Commands() { 42 | if err := c.GenMarkdownTree(sc); err != nil { 43 | return err 44 | } 45 | } 46 | 47 | // always disable the addition of [flags] to the usage 48 | cmd.DisableFlagsInUseLine = true 49 | 50 | // Skip the root command altogether, to prevent generating a useless 51 | // md file for plugins. 52 | if c.plugin && !cmd.HasParent() { 53 | return nil 54 | } 55 | 56 | // Skip hidden command recursively 57 | for curr := cmd; curr != nil; curr = curr.Parent() { 58 | if curr.Hidden { 59 | log.Printf("INFO: Skipping Markdown for %q (hidden command)", curr.CommandPath()) 60 | return nil 61 | } 62 | } 63 | 64 | log.Printf("INFO: Generating Markdown for %q", cmd.CommandPath()) 65 | mdFile := mdFilename(cmd) 66 | sourcePath := filepath.Join(c.source, mdFile) 67 | targetPath := filepath.Join(c.target, mdFile) 68 | 69 | // check recursively to handle inherited annotations 70 | for curr := cmd; curr != nil; curr = curr.Parent() { 71 | if _, ok := cmd.Annotations[annotation.CodeDelimiter]; !ok { 72 | if cd, cok := curr.Annotations[annotation.CodeDelimiter]; cok { 73 | if cmd.Annotations == nil { 74 | cmd.Annotations = map[string]string{} 75 | } 76 | cmd.Annotations[annotation.CodeDelimiter] = cd 77 | } 78 | } 79 | } 80 | 81 | if !fileExists(sourcePath) { 82 | var icBuf bytes.Buffer 83 | icTpl, err := template.New("ic").Option("missingkey=error").Parse(`# {{ .Command }} 84 | 85 | 86 | 87 | 88 | `) 89 | if err != nil { 90 | return err 91 | } 92 | if err = icTpl.Execute(&icBuf, struct { 93 | Command string 94 | }{ 95 | Command: cmd.CommandPath(), 96 | }); err != nil { 97 | return err 98 | } 99 | if err = os.WriteFile(targetPath, icBuf.Bytes(), 0o644); err != nil { 100 | return err 101 | } 102 | } else if err := copyFile(sourcePath, targetPath); err != nil { 103 | return err 104 | } 105 | 106 | content, err := os.ReadFile(targetPath) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | cs := string(content) 112 | 113 | start := strings.Index(cs, "") 114 | end := strings.Index(cs, "") 115 | 116 | if start == -1 { 117 | return fmt.Errorf("no start marker in %s", mdFile) 118 | } 119 | if end == -1 { 120 | return fmt.Errorf("no end marker in %s", mdFile) 121 | } 122 | 123 | out, err := mdCmdOutput(cmd, cs) 124 | if err != nil { 125 | return err 126 | } 127 | cont := cs[:start] + "" + "\n" + out + "\n" + cs[end:] 128 | 129 | fi, err := os.Stat(targetPath) 130 | if err != nil { 131 | return err 132 | } 133 | if err = os.WriteFile(targetPath, []byte(cont), fi.Mode()); err != nil { 134 | return fmt.Errorf("failed to write %s: %w", targetPath, err) 135 | } 136 | 137 | return nil 138 | } 139 | 140 | func mdFilename(cmd *cobra.Command) string { 141 | name := cmd.CommandPath() 142 | if i := strings.Index(name, " "); i >= 0 { 143 | name = name[i+1:] 144 | } 145 | return strings.ReplaceAll(name, " ", "_") + ".md" 146 | } 147 | 148 | func mdMakeLink(txt, link string, f *pflag.Flag, isAnchor bool) string { 149 | link = "#" + link 150 | annotations, ok := f.Annotations[annotation.ExternalURL] 151 | if ok && len(annotations) > 0 { 152 | link = annotations[0] 153 | } else { 154 | if !isAnchor { 155 | return txt 156 | } 157 | } 158 | 159 | return "[" + txt + "](" + link + ")" 160 | } 161 | 162 | type mdTable struct { 163 | out *strings.Builder 164 | tabWriter *tabwriter.Writer 165 | } 166 | 167 | func newMdTable(headers ...string) *mdTable { 168 | w := &strings.Builder{} 169 | t := &mdTable{ 170 | out: w, 171 | // Using tabwriter.Debug, which uses "|" as separator instead of tabs, 172 | // which is what we want. It's a bit of a hack, but does the job :) 173 | tabWriter: tabwriter.NewWriter(w, 5, 5, 1, ' ', tabwriter.Debug), 174 | } 175 | t.addHeader(headers...) 176 | return t 177 | } 178 | 179 | func (t *mdTable) addHeader(cols ...string) { 180 | t.AddRow(cols...) 181 | _, _ = t.tabWriter.Write([]byte("|" + strings.Repeat(":---\t", len(cols)) + "\n")) 182 | } 183 | 184 | func (t *mdTable) AddRow(cols ...string) { 185 | for i := range cols { 186 | cols[i] = mdEscapePipe(cols[i]) 187 | } 188 | _, _ = t.tabWriter.Write([]byte("| " + strings.Join(cols, "\t ") + "\t\n")) 189 | } 190 | 191 | func (t *mdTable) String() string { 192 | _ = t.tabWriter.Flush() 193 | return adjustSep.ReplaceAllStringFunc(t.out.String()+"\n", func(in string) string { 194 | return strings.ReplaceAll(in, " ", "-") 195 | }) 196 | } 197 | 198 | func mdCmdOutput(cmd *cobra.Command, old string) (string, error) { 199 | b := &strings.Builder{} 200 | 201 | desc := cmd.Short 202 | if cmd.Long != "" { 203 | desc = cmd.Long 204 | } 205 | if desc != "" { 206 | b.WriteString(desc + "\n\n") 207 | } 208 | 209 | if aliases := getAliases(cmd); len(aliases) != 0 { 210 | b.WriteString("### Aliases\n\n") 211 | b.WriteString("`" + strings.Join(aliases, "`, `") + "`") 212 | b.WriteString("\n\n") 213 | } 214 | 215 | if len(cmd.Commands()) != 0 { 216 | b.WriteString("### Subcommands\n\n") 217 | table := newMdTable("Name", "Description") 218 | for _, c := range cmd.Commands() { 219 | if c.Hidden { 220 | continue 221 | } 222 | table.AddRow(fmt.Sprintf("[`%s`](%s)", c.Name(), mdFilename(c)), c.Short) 223 | } 224 | b.WriteString(table.String() + "\n") 225 | } 226 | 227 | // add inherited flags before checking for flags availability 228 | cmd.Flags().AddFlagSet(cmd.InheritedFlags()) 229 | 230 | if cmd.Flags().HasAvailableFlags() { 231 | b.WriteString("### Options\n\n") 232 | table := newMdTable("Name", "Type", "Default", "Description") 233 | cmd.Flags().VisitAll(func(f *pflag.Flag) { 234 | if f.Hidden { 235 | return 236 | } 237 | isLink := strings.Contains(old, "") 238 | var name string 239 | if f.Shorthand != "" { 240 | name = mdMakeLink("`-"+f.Shorthand+"`", f.Name, f, isLink) 241 | name += ", " 242 | } 243 | name += mdMakeLink("`--"+f.Name+"`", f.Name, f, isLink) 244 | 245 | ftype := "`" + f.Value.Type() + "`" 246 | 247 | var defval string 248 | if v, ok := f.Annotations[annotation.DefaultValue]; ok && len(v) > 0 { 249 | defval = v[0] 250 | if cd, ok := f.Annotations[annotation.CodeDelimiter]; ok { 251 | defval = strings.ReplaceAll(defval, cd[0], "`") 252 | } else if cd, ok := cmd.Annotations[annotation.CodeDelimiter]; ok { 253 | defval = strings.ReplaceAll(defval, cd, "`") 254 | } 255 | } else if f.DefValue != "" && ((f.Value.Type() != "bool" && f.DefValue != "true") || (f.Value.Type() == "bool" && f.DefValue == "true")) && f.DefValue != "[]" { 256 | defval = "`" + f.DefValue + "`" 257 | } 258 | 259 | usage := f.Usage 260 | if cd, ok := f.Annotations[annotation.CodeDelimiter]; ok { 261 | usage = strings.ReplaceAll(usage, cd[0], "`") 262 | } else if cd, ok := cmd.Annotations[annotation.CodeDelimiter]; ok { 263 | usage = strings.ReplaceAll(usage, cd, "`") 264 | } 265 | table.AddRow(name, ftype, defval, mdReplaceNewline(usage)) 266 | }) 267 | b.WriteString(table.String()) 268 | } 269 | 270 | return b.String(), nil 271 | } 272 | 273 | func mdEscapePipe(s string) string { 274 | return strings.ReplaceAll(s, `|`, `\|`) 275 | } 276 | 277 | func mdReplaceNewline(s string) string { 278 | return nlRegexp.ReplaceAllString(s, "
") 279 | } 280 | -------------------------------------------------------------------------------- /example/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/docker/cli-docs-tool/example 2 | 3 | go 1.24.3 4 | 5 | require ( 6 | github.com/docker/buildx v0.30.0 7 | github.com/docker/cli v28.5.1+incompatible 8 | github.com/docker/cli-docs-tool v0.10.0 9 | github.com/spf13/cobra v1.10.2 10 | github.com/spf13/pflag v1.0.10 11 | ) 12 | 13 | require ( 14 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect 15 | github.com/Masterminds/semver/v3 v3.4.0 // indirect 16 | github.com/Microsoft/go-winio v0.6.2 // indirect 17 | github.com/agext/levenshtein v1.2.3 // indirect 18 | github.com/apparentlymart/go-cidr v1.0.1 // indirect 19 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 20 | github.com/aws/aws-sdk-go-v2 v1.38.1 // indirect 21 | github.com/aws/aws-sdk-go-v2/config v1.31.3 // indirect 22 | github.com/aws/aws-sdk-go-v2/credentials v1.18.7 // indirect 23 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 // indirect 24 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 // indirect 25 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 // indirect 26 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect 27 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect 28 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 // indirect 29 | github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 // indirect 30 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.0 // indirect 31 | github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 // indirect 32 | github.com/aws/smithy-go v1.22.5 // indirect 33 | github.com/cenkalti/backoff/v5 v5.0.3 // indirect 34 | github.com/compose-spec/compose-go/v2 v2.9.1 // indirect 35 | github.com/containerd/console v1.0.5 // indirect 36 | github.com/containerd/containerd/api v1.10.0 // indirect 37 | github.com/containerd/containerd/v2 v2.2.0 // indirect 38 | github.com/containerd/continuity v0.4.5 // indirect 39 | github.com/containerd/errdefs v1.0.0 // indirect 40 | github.com/containerd/errdefs/pkg v0.3.0 // indirect 41 | github.com/containerd/log v0.1.0 // indirect 42 | github.com/containerd/platforms v1.0.0-rc.2 // indirect 43 | github.com/containerd/ttrpc v1.2.7 // indirect 44 | github.com/containerd/typeurl/v2 v2.2.3 // indirect 45 | github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect 46 | github.com/davecgh/go-spew v1.1.1 // indirect 47 | github.com/distribution/reference v0.6.0 // indirect 48 | github.com/docker/docker v28.5.1+incompatible // indirect 49 | github.com/docker/docker-credential-helpers v0.9.3 // indirect 50 | github.com/docker/go v1.5.1-1 // indirect 51 | github.com/docker/go-connections v0.5.0 // indirect 52 | github.com/docker/go-units v0.5.0 // indirect 53 | github.com/emicklei/go-restful/v3 v3.13.0 // indirect 54 | github.com/felixge/httpsnoop v1.0.4 // indirect 55 | github.com/fvbommel/sortorder v1.0.1 // indirect 56 | github.com/fxamacker/cbor/v2 v2.9.0 // indirect 57 | github.com/go-logr/logr v1.4.3 // indirect 58 | github.com/go-logr/stdr v1.2.2 // indirect 59 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 60 | github.com/go-openapi/jsonreference v0.20.2 // indirect 61 | github.com/go-openapi/swag v0.23.0 // indirect 62 | github.com/go-viper/mapstructure/v2 v2.4.0 // indirect 63 | github.com/gofrs/flock v0.13.0 // indirect 64 | github.com/gogo/protobuf v1.3.2 // indirect 65 | github.com/golang/protobuf v1.5.4 // indirect 66 | github.com/google/gnostic-models v0.7.0 // indirect 67 | github.com/google/go-cmp v0.7.0 // indirect 68 | github.com/google/go-dap v0.12.0 // indirect 69 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect 70 | github.com/google/uuid v1.6.0 // indirect 71 | github.com/gorilla/mux v1.8.1 // indirect 72 | github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect 73 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect 74 | github.com/hashicorp/errwrap v1.1.0 // indirect 75 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 76 | github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928 // indirect 77 | github.com/hashicorp/go-multierror v1.1.1 // indirect 78 | github.com/hashicorp/hcl/v2 v2.24.0 // indirect 79 | github.com/in-toto/in-toto-golang v0.9.0 // indirect 80 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 81 | github.com/josharian/intern v1.0.0 // indirect 82 | github.com/json-iterator/go v1.1.12 // indirect 83 | github.com/klauspost/compress v1.18.2 // indirect 84 | github.com/mailru/easyjson v0.7.7 // indirect 85 | github.com/mattn/go-runewidth v0.0.16 // indirect 86 | github.com/mattn/go-shellwords v1.0.12 // indirect 87 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 88 | github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect 89 | github.com/moby/buildkit v0.26.1 // indirect 90 | github.com/moby/docker-image-spec v1.3.1 // indirect 91 | github.com/moby/go-archive v0.1.0 // indirect 92 | github.com/moby/locker v1.0.1 // indirect 93 | github.com/moby/patternmatcher v0.6.0 // indirect 94 | github.com/moby/spdystream v0.5.0 // indirect 95 | github.com/moby/sys/atomicwriter v0.1.0 // indirect 96 | github.com/moby/sys/mountinfo v0.7.2 // indirect 97 | github.com/moby/sys/sequential v0.6.0 // indirect 98 | github.com/moby/sys/signal v0.7.1 // indirect 99 | github.com/moby/sys/user v0.4.0 // indirect 100 | github.com/moby/sys/userns v0.1.0 // indirect 101 | github.com/moby/term v0.5.2 // indirect 102 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 103 | github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect 104 | github.com/morikuni/aec v1.0.0 // indirect 105 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 106 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect 107 | github.com/opencontainers/go-digest v1.0.0 // indirect 108 | github.com/opencontainers/image-spec v1.1.1 // indirect 109 | github.com/pelletier/go-toml v1.9.5 // indirect 110 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect 111 | github.com/pkg/errors v0.9.1 // indirect 112 | github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect 113 | github.com/rivo/uniseg v0.2.0 // indirect 114 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 115 | github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect 116 | github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect 117 | github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b // indirect 118 | github.com/shibumi/go-pathspec v1.3.0 // indirect 119 | github.com/sirupsen/logrus v1.9.3 // indirect 120 | github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect 121 | github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f // indirect 122 | github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect 123 | github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117 // indirect 124 | github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect 125 | github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect 126 | github.com/x448/float16 v0.8.4 // indirect 127 | github.com/xhit/go-str2duration/v2 v2.1.0 // indirect 128 | github.com/zclconf/go-cty v1.17.0 // indirect 129 | go.opentelemetry.io/auto/sdk v1.2.1 // indirect 130 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect 131 | go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 // indirect 132 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect 133 | go.opentelemetry.io/otel v1.38.0 // indirect 134 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect 135 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect 136 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect 137 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect 138 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect 139 | go.opentelemetry.io/otel/metric v1.38.0 // indirect 140 | go.opentelemetry.io/otel/sdk v1.38.0 // indirect 141 | go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect 142 | go.opentelemetry.io/otel/trace v1.38.0 // indirect 143 | go.opentelemetry.io/proto/otlp v1.7.1 // indirect 144 | go.yaml.in/yaml/v2 v2.4.2 // indirect 145 | go.yaml.in/yaml/v3 v3.0.4 // indirect 146 | golang.org/x/crypto v0.45.0 // indirect 147 | golang.org/x/mod v0.29.0 // indirect 148 | golang.org/x/net v0.47.0 // indirect 149 | golang.org/x/oauth2 v0.30.0 // indirect 150 | golang.org/x/sync v0.18.0 // indirect 151 | golang.org/x/sys v0.38.0 // indirect 152 | golang.org/x/term v0.37.0 // indirect 153 | golang.org/x/text v0.31.0 // indirect 154 | golang.org/x/time v0.14.0 // indirect 155 | golang.org/x/tools v0.38.0 // indirect 156 | google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect 157 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect 158 | google.golang.org/grpc v1.76.0 // indirect 159 | google.golang.org/protobuf v1.36.10 // indirect 160 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 161 | gopkg.in/inf.v0 v0.9.1 // indirect 162 | gopkg.in/yaml.v3 v3.0.1 // indirect 163 | k8s.io/api v0.34.1 // indirect 164 | k8s.io/apimachinery v0.34.1 // indirect 165 | k8s.io/client-go v0.34.1 // indirect 166 | k8s.io/klog/v2 v2.130.1 // indirect 167 | k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect 168 | k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect 169 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect 170 | sigs.k8s.io/randfill v1.0.0 // indirect 171 | sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect 172 | sigs.k8s.io/yaml v1.6.0 // indirect 173 | ) 174 | 175 | replace github.com/docker/cli-docs-tool => ../ 176 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /fixtures/docker_buildx_build.yaml: -------------------------------------------------------------------------------- 1 | command: docker buildx build 2 | aliases: docker image build, docker buildx build, docker buildx b, docker build 3 | short: Start a build 4 | long: Start a build 5 | usage: docker buildx build [OPTIONS] PATH | URL | - 6 | pname: docker buildx 7 | plink: docker_buildx.yaml 8 | options: 9 | - option: add-host 10 | value_type: stringSlice 11 | default_value: '[]' 12 | description: 'Add a custom host-to-IP mapping (format: `host:ip`)' 13 | details_url: /engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host 14 | deprecated: false 15 | hidden: false 16 | experimental: false 17 | experimentalcli: false 18 | kubernetes: false 19 | swarm: false 20 | - option: allow 21 | value_type: stringSlice 22 | default_value: '[]' 23 | description: | 24 | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) 25 | deprecated: false 26 | hidden: false 27 | experimental: false 28 | experimentalcli: false 29 | kubernetes: false 30 | swarm: false 31 | - option: build-arg 32 | value_type: stringArray 33 | default_value: '[]' 34 | description: Set build-time variables 35 | details_url: /engine/reference/commandline/build/#set-build-time-variables---build-arg 36 | deprecated: false 37 | hidden: false 38 | experimental: false 39 | experimentalcli: false 40 | kubernetes: false 41 | swarm: false 42 | - option: cache-from 43 | value_type: stringArray 44 | default_value: '[]' 45 | description: | 46 | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) 47 | deprecated: false 48 | hidden: false 49 | experimental: false 50 | experimentalcli: false 51 | kubernetes: false 52 | swarm: false 53 | - option: cache-to 54 | value_type: stringArray 55 | default_value: '[]' 56 | description: | 57 | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) 58 | deprecated: false 59 | hidden: false 60 | experimental: false 61 | experimentalcli: false 62 | kubernetes: false 63 | swarm: false 64 | - option: cgroup-parent 65 | value_type: string 66 | description: Optional parent cgroup for the container 67 | details_url: /engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent 68 | deprecated: false 69 | hidden: false 70 | experimental: false 71 | experimentalcli: false 72 | kubernetes: false 73 | swarm: false 74 | - option: compress 75 | value_type: bool 76 | default_value: "false" 77 | description: Compress the build context using gzip 78 | deprecated: false 79 | hidden: true 80 | experimental: false 81 | experimentalcli: false 82 | kubernetes: false 83 | swarm: false 84 | - option: cpu-period 85 | value_type: int64 86 | default_value: "0" 87 | description: Limit the CPU CFS (Completely Fair Scheduler) period 88 | deprecated: false 89 | hidden: true 90 | experimental: false 91 | experimentalcli: false 92 | kubernetes: false 93 | swarm: false 94 | - option: cpu-quota 95 | value_type: int64 96 | default_value: "0" 97 | description: Limit the CPU CFS (Completely Fair Scheduler) quota 98 | deprecated: false 99 | hidden: true 100 | experimental: false 101 | experimentalcli: false 102 | kubernetes: false 103 | swarm: false 104 | - option: cpu-shares 105 | shorthand: c 106 | value_type: int64 107 | default_value: "0" 108 | description: CPU shares (relative weight) 109 | deprecated: false 110 | hidden: true 111 | experimental: false 112 | experimentalcli: false 113 | kubernetes: false 114 | swarm: false 115 | - option: cpuset-cpus 116 | value_type: string 117 | description: CPUs in which to allow execution (`0-3`, `0,1`) 118 | deprecated: false 119 | hidden: true 120 | experimental: false 121 | experimentalcli: false 122 | kubernetes: false 123 | swarm: false 124 | - option: cpuset-mems 125 | value_type: string 126 | description: MEMs in which to allow execution (`0-3`, `0,1`) 127 | deprecated: false 128 | hidden: true 129 | experimental: false 130 | experimentalcli: false 131 | kubernetes: false 132 | swarm: false 133 | - option: detach 134 | value_type: bool 135 | default_value: "true" 136 | description: Dummy flag that tests boolean flags with true as default 137 | deprecated: false 138 | hidden: false 139 | experimental: false 140 | experimentalcli: false 141 | kubernetes: false 142 | swarm: false 143 | - option: file 144 | shorthand: f 145 | value_type: string 146 | description: 'Name of the Dockerfile (default: `PATH/Dockerfile`)' 147 | details_url: /engine/reference/commandline/build/#specify-a-dockerfile--f 148 | deprecated: false 149 | hidden: false 150 | experimental: false 151 | experimentalcli: false 152 | kubernetes: false 153 | swarm: false 154 | - option: force-rm 155 | value_type: bool 156 | default_value: "false" 157 | description: Always remove intermediate containers 158 | deprecated: false 159 | hidden: true 160 | experimental: false 161 | experimentalcli: false 162 | kubernetes: false 163 | swarm: false 164 | - option: iidfile 165 | value_type: string 166 | description: Write the image ID to the file 167 | deprecated: false 168 | hidden: false 169 | experimental: false 170 | experimentalcli: false 171 | kubernetes: false 172 | swarm: false 173 | - option: isolation 174 | value_type: string 175 | description: Container isolation technology 176 | deprecated: false 177 | hidden: true 178 | experimental: false 179 | experimentalcli: false 180 | kubernetes: false 181 | swarm: false 182 | - option: label 183 | value_type: stringArray 184 | default_value: '[]' 185 | description: Set metadata for an image 186 | deprecated: false 187 | hidden: false 188 | experimental: false 189 | experimentalcli: false 190 | kubernetes: false 191 | swarm: false 192 | - option: load 193 | value_type: bool 194 | default_value: "false" 195 | description: Shorthand for `--output=type=docker` 196 | deprecated: false 197 | hidden: false 198 | experimental: false 199 | experimentalcli: false 200 | kubernetes: false 201 | swarm: false 202 | - option: memory 203 | shorthand: m 204 | value_type: string 205 | description: Memory limit 206 | deprecated: false 207 | hidden: true 208 | experimental: false 209 | experimentalcli: false 210 | kubernetes: false 211 | swarm: false 212 | - option: memory-swap 213 | value_type: string 214 | description: | 215 | Swap limit equal to memory plus swap: `-1` to enable unlimited swap 216 | deprecated: false 217 | hidden: true 218 | experimental: false 219 | experimentalcli: false 220 | kubernetes: false 221 | swarm: false 222 | - option: network 223 | value_type: string 224 | default_value: default 225 | description: Set the networking mode for the `RUN` instructions during build 226 | deprecated: false 227 | hidden: false 228 | experimental: false 229 | experimentalcli: false 230 | kubernetes: false 231 | swarm: false 232 | - option: output 233 | shorthand: o 234 | value_type: stringArray 235 | default_value: '[]' 236 | description: 'Output destination (format: `type=local,dest=path`)' 237 | deprecated: false 238 | hidden: false 239 | experimental: false 240 | experimentalcli: false 241 | kubernetes: false 242 | swarm: false 243 | - option: platform 244 | value_type: stringArray 245 | default_value: local 246 | description: Set target platform for build 247 | deprecated: false 248 | hidden: false 249 | experimental: false 250 | experimentalcli: false 251 | kubernetes: false 252 | swarm: false 253 | - option: push 254 | value_type: bool 255 | default_value: "false" 256 | description: Shorthand for `--output=type=registry` 257 | deprecated: false 258 | hidden: false 259 | experimental: false 260 | experimentalcli: false 261 | kubernetes: false 262 | swarm: false 263 | - option: quiet 264 | shorthand: q 265 | value_type: bool 266 | default_value: "false" 267 | description: Suppress the build output and print image ID on success 268 | deprecated: false 269 | hidden: false 270 | experimental: false 271 | experimentalcli: false 272 | kubernetes: false 273 | swarm: false 274 | - option: rm 275 | value_type: bool 276 | default_value: "true" 277 | description: Remove intermediate containers after a successful build 278 | deprecated: false 279 | hidden: true 280 | experimental: false 281 | experimentalcli: false 282 | kubernetes: false 283 | swarm: false 284 | - option: secret 285 | value_type: stringArray 286 | default_value: '[]' 287 | description: | 288 | Secret file to expose to the build (format: `id=mysecret,src=/local/secret`) 289 | deprecated: false 290 | hidden: false 291 | experimental: false 292 | experimentalcli: false 293 | kubernetes: false 294 | swarm: false 295 | - option: security-opt 296 | value_type: stringSlice 297 | default_value: '[]' 298 | description: Security options 299 | deprecated: false 300 | hidden: true 301 | experimental: false 302 | experimentalcli: false 303 | kubernetes: false 304 | swarm: false 305 | - option: shm-size 306 | value_type: string 307 | description: Size of `/dev/shm` 308 | deprecated: false 309 | hidden: false 310 | experimental: false 311 | experimentalcli: false 312 | kubernetes: false 313 | swarm: false 314 | - option: squash 315 | value_type: bool 316 | default_value: "false" 317 | description: Squash newly built layers into a single new layer 318 | deprecated: false 319 | hidden: true 320 | experimental: false 321 | experimentalcli: false 322 | kubernetes: false 323 | swarm: false 324 | - option: ssh 325 | value_type: stringArray 326 | default_value: '[]' 327 | description: |- 328 | SSH agent socket or keys to expose to the build 329 | format: `default|[=|[,]]` 330 | deprecated: false 331 | hidden: false 332 | experimental: false 333 | experimentalcli: false 334 | kubernetes: false 335 | swarm: false 336 | - option: tag 337 | shorthand: t 338 | value_type: stringArray 339 | default_value: '[]' 340 | description: 'Name and optionally a tag (format: `name:tag`)' 341 | details_url: /engine/reference/commandline/build/#tag-an-image--t 342 | deprecated: false 343 | hidden: false 344 | experimental: false 345 | experimentalcli: false 346 | kubernetes: false 347 | swarm: false 348 | - option: target 349 | value_type: string 350 | description: Set the target build stage to build. 351 | details_url: /engine/reference/commandline/build/#specifying-target-build-stage---target 352 | deprecated: false 353 | hidden: false 354 | experimental: false 355 | experimentalcli: false 356 | kubernetes: false 357 | swarm: false 358 | - option: ulimit 359 | value_type: string 360 | description: Ulimit options 361 | deprecated: false 362 | hidden: false 363 | experimental: false 364 | experimentalcli: false 365 | kubernetes: false 366 | swarm: false 367 | inherited_options: 368 | - option: builder 369 | value_type: string 370 | description: Override the configured builder instance 371 | deprecated: false 372 | hidden: false 373 | experimental: false 374 | experimentalcli: false 375 | kubernetes: false 376 | swarm: false 377 | - option: help 378 | value_type: bool 379 | default_value: "false" 380 | description: Print usage 381 | deprecated: false 382 | hidden: true 383 | experimental: false 384 | experimentalcli: false 385 | kubernetes: false 386 | swarm: false 387 | deprecated: false 388 | hidden: false 389 | experimental: false 390 | experimentalcli: false 391 | kubernetes: false 392 | swarm: false 393 | 394 | -------------------------------------------------------------------------------- /clidocstool_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import ( 18 | "io/fs" 19 | "os" 20 | "path" 21 | "path/filepath" 22 | "strconv" 23 | "strings" 24 | "testing" 25 | "time" 26 | 27 | "github.com/docker/cli-docs-tool/annotation" 28 | "github.com/spf13/cobra" 29 | "github.com/spf13/cobra/doc" 30 | "github.com/stretchr/testify/assert" 31 | "github.com/stretchr/testify/require" 32 | ) 33 | 34 | var ( 35 | dockerCmd *cobra.Command 36 | attachCmd *cobra.Command 37 | buildxCmd *cobra.Command 38 | buildxBuildCmd *cobra.Command 39 | buildxDialStdioCmd *cobra.Command 40 | buildxImagetoolsCmd *cobra.Command 41 | buildxImagetoolsCreateCmd *cobra.Command 42 | buildxInstallCmd *cobra.Command 43 | buildxStopCmd *cobra.Command 44 | ) 45 | 46 | //nolint:errcheck 47 | func setup() { 48 | dockerCmd = &cobra.Command{ 49 | Use: "docker [OPTIONS] COMMAND [ARG...]", 50 | Short: "A self-sufficient runtime for containers", 51 | SilenceUsage: true, 52 | SilenceErrors: true, 53 | TraverseChildren: true, 54 | Run: func(*cobra.Command, []string) {}, 55 | Version: "20.10.8", 56 | DisableFlagsInUseLine: true, 57 | } 58 | 59 | dockerCmd.PersistentFlags().BoolP("help", "h", false, "Print usage") 60 | dockerCmd.PersistentFlags().MarkShorthandDeprecated("help", "please use --help") 61 | dockerCmd.PersistentFlags().Lookup("help").Hidden = true 62 | dockerCmd.Flags().StringP("host", "H", "unix:///var/run/docker.sock", "Daemon socket to connect to") 63 | 64 | attachCmd = &cobra.Command{ 65 | Use: "attach [OPTIONS] CONTAINER", 66 | Short: "Attach local standard input, output, and error streams to a running container", 67 | Annotations: map[string]string{ 68 | "aliases": "docker container attach, docker attach", 69 | }, 70 | Run: func(*cobra.Command, []string) {}, 71 | } 72 | 73 | attachFlags := attachCmd.Flags() 74 | attachFlags.Bool("no-stdin", false, "Do not attach STDIN") 75 | attachFlags.Bool("sig-proxy", true, "Proxy all received signals to the process") 76 | attachFlags.String("detach-keys", "", "Override the key sequence for detaching a container") 77 | dockerCmd.AddCommand(attachCmd) 78 | 79 | buildxCmd = &cobra.Command{ 80 | Use: "buildx", 81 | Short: "Docker Buildx", 82 | Long: `Extended build capabilities with BuildKit`, 83 | Annotations: map[string]string{ 84 | annotation.CodeDelimiter: `"`, 85 | }, 86 | } 87 | buildxBuildCmd = &cobra.Command{ 88 | Use: "build [OPTIONS] PATH | URL | -", 89 | Aliases: []string{"b"}, 90 | Short: "Start a build", 91 | Run: func(*cobra.Command, []string) {}, 92 | Annotations: map[string]string{ 93 | "aliases": "docker image build, docker buildx build, docker buildx b, docker build", 94 | }, 95 | } 96 | buildxDialStdioCmd = &cobra.Command{ 97 | Use: "dial-stdio", 98 | Short: "Proxy current stdio streams to builder instance", 99 | Args: cobra.NoArgs, 100 | Run: func(*cobra.Command, []string) {}, 101 | } 102 | buildxImagetoolsCmd = &cobra.Command{ 103 | Use: "imagetools", 104 | Short: "Commands to work on images in registry", 105 | Run: func(*cobra.Command, []string) {}, 106 | Hidden: true, 107 | } 108 | buildxImagetoolsCreateCmd = &cobra.Command{ 109 | Use: "create [OPTIONS] [SOURCE...]", 110 | Short: "Create a new image based on source images", 111 | Run: func(*cobra.Command, []string) {}, 112 | } 113 | buildxInstallCmd = &cobra.Command{ 114 | Use: "install", 115 | Short: "Install buildx as a 'docker builder' alias", 116 | Args: cobra.ExactArgs(0), 117 | Run: func(*cobra.Command, []string) {}, 118 | Hidden: true, 119 | } 120 | buildxStopCmd = &cobra.Command{ 121 | Use: "stop [NAME]", 122 | Short: "Stop builder instance", 123 | Run: func(*cobra.Command, []string) {}, 124 | } 125 | 126 | buildxPFlags := buildxCmd.PersistentFlags() 127 | buildxPFlags.String("builder", os.Getenv("BUILDX_BUILDER"), "Override the configured builder instance") 128 | 129 | buildxBuildFlags := buildxBuildCmd.Flags() 130 | 131 | var ignore string 132 | var ignoreSlice []string 133 | var ignoreBool bool 134 | var ignoreInt int64 135 | 136 | buildxBuildFlags.StringSlice("add-host", []string{}, `Add a custom host-to-IP mapping (format: 'host:ip')`) 137 | buildxBuildFlags.SetAnnotation("add-host", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host"}) 138 | buildxBuildFlags.SetAnnotation("add-host", annotation.CodeDelimiter, []string{`'`}) 139 | 140 | buildxBuildFlags.StringSlice("allow", []string{}, `Allow extra privileged entitlement (e.g., "network.host", "security.insecure")`) 141 | 142 | buildxBuildFlags.StringArray("build-arg", []string{}, "Set build-time variables") 143 | buildxBuildFlags.SetAnnotation("build-arg", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg"}) 144 | 145 | buildxBuildFlags.StringArray("cache-from", []string{}, `External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir")`) 146 | 147 | buildxBuildFlags.StringArray("cache-to", []string{}, `Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir")`) 148 | 149 | buildxBuildFlags.String("cgroup-parent", "", "Optional parent cgroup for the container") 150 | buildxBuildFlags.SetAnnotation("cgroup-parent", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent"}) 151 | 152 | buildxBuildFlags.StringP("file", "f", "", `Name of the Dockerfile (default: "PATH/Dockerfile")`) 153 | buildxBuildFlags.SetAnnotation("file", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f"}) 154 | 155 | buildxBuildFlags.String("iidfile", "", "Write the image ID to the file") 156 | 157 | buildxBuildFlags.StringArray("label", []string{}, "Set metadata for an image") 158 | 159 | buildxBuildFlags.Bool("load", false, `Shorthand for "--output=type=docker"`) 160 | 161 | buildxBuildFlags.String("network", "default", `Set the networking mode for the "RUN" instructions during build`) 162 | 163 | buildxBuildFlags.StringArrayP("output", "o", []string{}, `Output destination (format: "type=local,dest=path")`) 164 | 165 | buildxBuildFlags.StringArray("platform", []string{}, "Set target platform for build") 166 | buildxBuildFlags.SetAnnotation("platform", annotation.DefaultValue, []string{"local"}) 167 | 168 | buildxBuildFlags.Bool("push", false, `Shorthand for "--output=type=registry"`) 169 | 170 | buildxBuildFlags.BoolP("quiet", "q", false, "Suppress the build output and print image ID on success") 171 | 172 | buildxBuildFlags.StringArray("secret", []string{}, `Secret file to expose to the build (format: "id=mysecret,src=/local/secret")`) 173 | 174 | buildxBuildFlags.StringVar(&ignore, "shm-size", "", `Size of "/dev/shm"`) 175 | 176 | buildxBuildFlags.StringArray("ssh", []string{}, `SSH agent socket or keys to expose to the build 177 | format: "default|[=|[,]]"`) 178 | 179 | buildxBuildFlags.StringArrayP("tag", "t", []string{}, `Name and optionally a tag (format: "name:tag")`) 180 | buildxBuildFlags.SetAnnotation("tag", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t"}) 181 | 182 | buildxBuildFlags.String("target", "", "Set the target build stage to build.") 183 | buildxBuildFlags.SetAnnotation("target", annotation.ExternalURL, []string{"https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target"}) 184 | 185 | buildxBuildFlags.StringVar(&ignore, "ulimit", "", "Ulimit options") 186 | 187 | buildxBuildFlags.BoolVar(&ignoreBool, "detach", true, "Dummy flag that tests boolean flags with true as default") 188 | 189 | // hidden flags 190 | buildxBuildFlags.BoolVar(&ignoreBool, "compress", false, "Compress the build context using gzip") 191 | buildxBuildFlags.MarkHidden("compress") 192 | 193 | buildxBuildFlags.StringVar(&ignore, "isolation", "", "Container isolation technology") 194 | buildxBuildFlags.MarkHidden("isolation") 195 | buildxBuildFlags.SetAnnotation("isolation", "flag-warn", []string{"isolation flag is deprecated with BuildKit."}) 196 | 197 | buildxBuildFlags.StringSliceVar(&ignoreSlice, "security-opt", []string{}, "Security options") 198 | buildxBuildFlags.MarkHidden("security-opt") 199 | buildxBuildFlags.SetAnnotation("security-opt", "flag-warn", []string{`security-opt flag is deprecated. "RUN --security=insecure" should be used with BuildKit.`}) 200 | 201 | buildxBuildFlags.BoolVar(&ignoreBool, "squash", false, "Squash newly built layers into a single new layer") 202 | buildxBuildFlags.MarkHidden("squash") 203 | buildxBuildFlags.SetAnnotation("squash", "flag-warn", []string{"experimental flag squash is removed with BuildKit. You should squash inside build using a multi-stage Dockerfile for efficiency."}) 204 | 205 | buildxBuildFlags.StringVarP(&ignore, "memory", "m", "", "Memory limit") 206 | buildxBuildFlags.MarkHidden("memory") 207 | 208 | buildxBuildFlags.StringVar(&ignore, "memory-swap", "", `Swap limit equal to memory plus swap: "-1" to enable unlimited swap`) 209 | buildxBuildFlags.MarkHidden("memory-swap") 210 | 211 | buildxBuildFlags.Int64VarP(&ignoreInt, "cpu-shares", "c", 0, "CPU shares (relative weight)") 212 | buildxBuildFlags.MarkHidden("cpu-shares") 213 | 214 | buildxBuildFlags.Int64Var(&ignoreInt, "cpu-period", 0, "Limit the CPU CFS (Completely Fair Scheduler) period") 215 | buildxBuildFlags.MarkHidden("cpu-period") 216 | 217 | buildxBuildFlags.Int64Var(&ignoreInt, "cpu-quota", 0, "Limit the CPU CFS (Completely Fair Scheduler) quota") 218 | buildxBuildFlags.MarkHidden("cpu-quota") 219 | 220 | buildxBuildFlags.StringVar(&ignore, "cpuset-cpus", "", `CPUs in which to allow execution ("0-3", "0,1")`) 221 | buildxBuildFlags.MarkHidden("cpuset-cpus") 222 | 223 | buildxBuildFlags.StringVar(&ignore, "cpuset-mems", "", `MEMs in which to allow execution ("0-3", "0,1")`) 224 | buildxBuildFlags.MarkHidden("cpuset-mems") 225 | 226 | buildxBuildFlags.BoolVar(&ignoreBool, "rm", true, "Remove intermediate containers after a successful build") 227 | buildxBuildFlags.MarkHidden("rm") 228 | 229 | buildxBuildFlags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers") 230 | buildxBuildFlags.MarkHidden("force-rm") 231 | 232 | buildxDialStdioFlags := buildxDialStdioCmd.Flags() 233 | 234 | buildxDialStdioFlags.String("platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Target platform: this is used for node selection") 235 | buildxDialStdioFlags.String("progress", "quiet", "Set type of progress output (auto, plain, tty).") 236 | 237 | buildxCmd.AddCommand(buildxBuildCmd) 238 | buildxCmd.AddCommand(buildxDialStdioCmd) 239 | buildxImagetoolsCmd.AddCommand(buildxImagetoolsCreateCmd) 240 | buildxCmd.AddCommand(buildxImagetoolsCmd) 241 | buildxCmd.AddCommand(buildxInstallCmd) 242 | buildxCmd.AddCommand(buildxStopCmd) 243 | dockerCmd.AddCommand(buildxCmd) 244 | } 245 | 246 | //nolint:errcheck 247 | func TestGenAllTree(t *testing.T) { 248 | setup() 249 | tmpdir := t.TempDir() 250 | 251 | // keep for testing 252 | //tmpdir, err := os.MkdirTemp("", "cli-docs-tools") 253 | //require.NoError(t, err) 254 | //t.Log(tmpdir) 255 | 256 | epoch, err := time.Parse("2006-Jan-02", "2020-Jan-10") 257 | require.NoError(t, err) 258 | t.Setenv("SOURCE_DATE_EPOCH", strconv.FormatInt(epoch.Unix(), 10)) 259 | 260 | require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) 261 | 262 | c, err := New(Options{ 263 | Root: dockerCmd, 264 | SourceDir: tmpdir, 265 | Plugin: false, 266 | ManHeader: &doc.GenManHeader{ 267 | Title: "DOCKER", 268 | Section: "1", 269 | Source: "Docker Community", 270 | Manual: "Docker User Manuals", 271 | }, 272 | }) 273 | require.NoError(t, err) 274 | require.NoError(t, c.GenAllTree()) 275 | 276 | seen := make(map[string]struct{}) 277 | 278 | _ = filepath.Walk("fixtures", func(path string, info fs.FileInfo, err error) error { 279 | fname := filepath.Base(path) 280 | // ignore dirs and .pre.md files 281 | if info.IsDir() || strings.HasSuffix(fname, ".pre.md") { 282 | return nil 283 | } 284 | t.Run(fname, func(t *testing.T) { 285 | seen[fname] = struct{}{} 286 | require.NoError(t, err) 287 | 288 | bres, err := os.ReadFile(filepath.Join(tmpdir, fname)) 289 | require.NoError(t, err) 290 | 291 | bexc, err := os.ReadFile(path) 292 | require.NoError(t, err) 293 | assert.Equal(t, string(bexc), string(bres)) 294 | }) 295 | return nil 296 | }) 297 | 298 | _ = filepath.Walk(tmpdir, func(path string, info fs.FileInfo, _ error) error { 299 | fname := filepath.Base(path) 300 | // ignore dirs and .pre.md files 301 | if info.IsDir() || strings.HasSuffix(fname, ".pre.md") { 302 | return nil 303 | } 304 | t.Run("seen_"+fname, func(t *testing.T) { 305 | if _, ok := seen[fname]; !ok { 306 | t.Errorf("file %s not found in fixtures", fname) 307 | } 308 | }) 309 | return nil 310 | }) 311 | } 312 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribute to the cli-docs-tool project 2 | 3 | This page contains information about reporting issues as well as some tips and 4 | guidelines useful to experienced open source contributors. 5 | 6 | ## Reporting security issues 7 | 8 | The project maintainers take security seriously. If you discover a security 9 | issue, please bring it to their attention right away! 10 | 11 | **Please _DO NOT_ file a public issue**, instead send your report privately to 12 | [security@docker.com](mailto:security@docker.com). 13 | 14 | Security reports are greatly appreciated and we will publicly thank you for it. 15 | We also like to send gifts—if you're into schwag, make sure to let 16 | us know. We currently do not offer a paid security bounty program, but are not 17 | ruling it out in the future. 18 | 19 | 20 | ## Reporting other issues 21 | 22 | A great way to contribute to the project is to send a detailed report when you 23 | encounter an issue. We always appreciate a well-written, thorough bug report, 24 | and will thank you for it! 25 | 26 | Check that [our issue database](https://github.com/docker/cli-docs-tool/issues) 27 | doesn't already include that problem or suggestion before submitting an issue. 28 | If you find a match, you can use the "subscribe" button to get notified on 29 | updates. Do *not* leave random "+1" or "I have this too" comments, as they 30 | only clutter the discussion, and don't help resolving it. However, if you 31 | have ways to reproduce the issue or have additional information that may help 32 | resolving the issue, please leave a comment. 33 | 34 | Include the steps required to reproduce the problem if possible and applicable. 35 | This information will help us review and fix your issue faster. When sending 36 | lengthy log-files, consider posting them as an attachment, instead of posting 37 | inline. 38 | 39 | **Do not forget to remove sensitive data from your logfiles before submitting** 40 | (you can replace those parts with "REDACTED"). 41 | 42 | ### Pull requests are always welcome 43 | 44 | Not sure if that typo is worth a pull request? Found a bug and know how to fix 45 | it? Do it! We will appreciate it. 46 | 47 | If your pull request is not accepted on the first try, don't be discouraged! If 48 | there's a problem with the implementation, hopefully you received feedback on 49 | what to improve. 50 | 51 | We're trying very hard to keep Buildx lean and focused. We don't want it to 52 | do everything for everybody. This means that we might decide against 53 | incorporating a new feature. However, there might be a way to implement that 54 | feature *on top of* Buildx. 55 | 56 | ### Design and cleanup proposals 57 | 58 | You can propose new designs for existing features. You can also design 59 | entirely new features. We really appreciate contributors who want to refactor or 60 | otherwise cleanup our project. 61 | 62 | ### Sign your work 63 | 64 | The sign-off is a simple line at the end of the explanation for the patch. Your 65 | signature certifies that you wrote the patch or otherwise have the right to pass 66 | it on as an open-source patch. The rules are pretty simple: if you can certify 67 | the below (from [developercertificate.org](http://developercertificate.org/)): 68 | 69 | ``` 70 | Developer Certificate of Origin 71 | Version 1.1 72 | 73 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 74 | 1 Letterman Drive 75 | Suite D4700 76 | San Francisco, CA, 94129 77 | 78 | Everyone is permitted to copy and distribute verbatim copies of this 79 | license document, but changing it is not allowed. 80 | 81 | Developer's Certificate of Origin 1.1 82 | 83 | By making a contribution to this project, I certify that: 84 | 85 | (a) The contribution was created in whole or in part by me and I 86 | have the right to submit it under the open source license 87 | indicated in the file; or 88 | 89 | (b) The contribution is based upon previous work that, to the best 90 | of my knowledge, is covered under an appropriate open source 91 | license and I have the right under that license to submit that 92 | work with modifications, whether created in whole or in part 93 | by me, under the same open source license (unless I am 94 | permitted to submit under a different license), as indicated 95 | in the file; or 96 | 97 | (c) The contribution was provided directly to me by some other 98 | person who certified (a), (b) or (c) and I have not modified 99 | it. 100 | 101 | (d) I understand and agree that this project and the contribution 102 | are public and that a record of the contribution (including all 103 | personal information I submit with it, including my sign-off) is 104 | maintained indefinitely and may be redistributed consistent with 105 | this project or the open source license(s) involved. 106 | ``` 107 | 108 | Then you just add a line to every git commit message: 109 | 110 | Signed-off-by: Joe Smith 111 | 112 | **Use your real name** (sorry, no pseudonyms or anonymous contributions.) 113 | 114 | If you set your `user.name` and `user.email` git configs, you can sign your 115 | commit automatically with `git commit -s`. 116 | 117 | ### Run the unit- and integration-tests 118 | 119 | To validate PRs before submitting them you should run: 120 | 121 | ```console 122 | $ docker buildx bake validate test 123 | ``` 124 | 125 | To generate new vendored with go modules run: 126 | 127 | ```console 128 | $ docker buildx bake vendor-update 129 | ``` 130 | 131 | 132 | ### Conventions 133 | 134 | - Fork the repository and make changes on your fork in a feature branch 135 | - Submit tests for your changes. See [run the unit- and integration-tests](#run-the-unit--and-integration-tests) 136 | for details. 137 | - [Sign your work](#sign-your-work) 138 | 139 | Write clean code. Universally formatted code promotes ease of writing, reading, 140 | and maintenance. Always run `gofmt -s -w file.go` on each changed file before 141 | committing your changes. Most editors have plug-ins that do this automatically. 142 | 143 | Pull request descriptions should be as clear as possible and include a 144 | reference to all the issues that they address. Be sure that the [commit 145 | messages](#commit-messages) also contain the relevant information. 146 | 147 | ### Successful Changes 148 | 149 | Before contributing large or high impact changes, make the effort to coordinate 150 | with the maintainers of the project before submitting a pull request. This 151 | prevents you from doing extra work that may or may not be merged. 152 | 153 | Large PRs that are just submitted without any prior communication are unlikely 154 | to be successful. 155 | 156 | While pull requests are the methodology for submitting changes to code, changes 157 | are much more likely to be accepted if they are accompanied by additional 158 | engineering work. While we don't define this explicitly, most of these goals 159 | are accomplished through communication of the design goals and subsequent 160 | solutions. Often times, it helps to first state the problem before presenting 161 | solutions. 162 | 163 | Typically, the best methods of accomplishing this are to submit an issue, 164 | stating the problem. This issue can include a problem statement and a 165 | checklist with requirements. If solutions are proposed, alternatives should be 166 | listed and eliminated. Even if the criteria for elimination of a solution is 167 | frivolous, say so. 168 | 169 | Larger changes typically work best with design documents. These are focused on 170 | providing context to the design at the time the feature was conceived and can 171 | inform future documentation contributions. 172 | 173 | ### Commit Messages 174 | 175 | Commit messages must start with a capitalized and short summary (max. 50 chars) 176 | written in the imperative, followed by an optional, more detailed explanatory 177 | text which is separated from the summary by an empty line. 178 | 179 | Commit messages should follow best practices, including explaining the context 180 | of the problem and how it was solved, including in caveats or follow up changes 181 | required. They should tell the story of the change and provide readers 182 | understanding of what led to it. 183 | 184 | If you're lost about what this even means, please see [How to Write a Git 185 | Commit Message](http://chris.beams.io/posts/git-commit/) for a start. 186 | 187 | In practice, the best approach to maintaining a nice commit message is to 188 | leverage a `git add -p` and `git commit --amend` to formulate a solid 189 | changeset. This allows one to piece together a change, as information becomes 190 | available. 191 | 192 | If you squash a series of commits, don't just submit that. Re-write the commit 193 | message, as if the series of commits was a single stroke of brilliance. 194 | 195 | That said, there is no requirement to have a single commit for a PR, as long as 196 | each commit tells the story. For example, if there is a feature that requires a 197 | package, it might make sense to have the package in a separate commit then have 198 | a subsequent commit that uses it. 199 | 200 | Remember, you're telling part of the story with the commit message. Don't make 201 | your chapter weird. 202 | 203 | ### Review 204 | 205 | Code review comments may be added to your pull request. Discuss, then make the 206 | suggested modifications and push additional commits to your feature branch. Post 207 | a comment after pushing. New commits show up in the pull request automatically, 208 | but the reviewers are notified only when you comment. 209 | 210 | Pull requests must be cleanly rebased on top of master without multiple branches 211 | mixed into the PR. 212 | 213 | > **Git tip**: If your PR no longer merges cleanly, use `rebase master` in your 214 | > feature branch to update your pull request rather than `merge master`. 215 | 216 | Before you make a pull request, squash your commits into logical units of work 217 | using `git rebase -i` and `git push -f`. A logical unit of work is a consistent 218 | set of patches that should be reviewed together: for example, upgrading the 219 | version of a vendored dependency and taking advantage of its now available new 220 | feature constitute two separate units of work. Implementing a new function and 221 | calling it in another file constitute a single logical unit of work. The very 222 | high majority of submissions should have a single commit, so if in doubt: squash 223 | down to one. 224 | 225 | - After every commit, [make sure the test suite passes](#run-the-unit--and-integration-tests). 226 | Include documentation changes in the same pull request so that a revert would 227 | remove all traces of the feature or fix. 228 | - Include an issue reference like `closes #XXXX` or `fixes #XXXX` in the PR 229 | description that close an issue. Including references automatically closes 230 | the issue on a merge. 231 | - Do not add yourself to the `AUTHORS` file, as it is regenerated regularly 232 | from the Git history. 233 | - See the [Coding Style](#coding-style) for further guidelines. 234 | 235 | 236 | ### Merge approval 237 | 238 | Project maintainers use LGTM (Looks Good To Me) in comments on the code review to 239 | indicate acceptance, or use the Github review approval feature. 240 | 241 | 242 | ## Coding Style 243 | 244 | Unless explicitly stated, we follow all coding guidelines from the Go 245 | community. While some of these standards may seem arbitrary, they somehow seem 246 | to result in a solid, consistent codebase. 247 | 248 | It is possible that the code base does not currently comply with these 249 | guidelines. We are not looking for a massive PR that fixes this, since that 250 | goes against the spirit of the guidelines. All new contributions should make a 251 | best effort to clean up and make the code base better than they left it. 252 | Obviously, apply your best judgement. Remember, the goal here is to make the 253 | code base easier for humans to navigate and understand. Always keep that in 254 | mind when nudging others to comply. 255 | 256 | The rules: 257 | 258 | 1. All code should be formatted with `gofmt -s`. 259 | 2. All code should pass the default levels of 260 | [`golint`](https://github.com/golang/lint). 261 | 3. All code should follow the guidelines covered in [Effective 262 | Go](http://golang.org/doc/effective_go.html) and [Go Code Review 263 | Comments](https://github.com/golang/go/wiki/CodeReviewComments). 264 | 4. Comment the code. Tell us the why, the history and the context. 265 | 5. Document _all_ declarations and methods, even private ones. Declare 266 | expectations, caveats and anything else that may be important. If a type 267 | gets exported, having the comments already there will ensure it's ready. 268 | 6. Variable name length should be proportional to its context and no longer. 269 | `noCommaALongVariableNameLikeThisIsNotMoreClearWhenASimpleCommentWouldDo`. 270 | In practice, short methods will have short variable names and globals will 271 | have longer names. 272 | 7. No underscores in package names. If you need a compound name, step back, 273 | and re-examine why you need a compound name. If you still think you need a 274 | compound name, lose the underscore. 275 | 8. No utils or helpers packages. If a function is not general enough to 276 | warrant its own package, it has not been written generally enough to be a 277 | part of a util package. Just leave it unexported and well-documented. 278 | 9. All tests should run with `go test` and outside tooling should not be 279 | required. No, we don't need another unit testing framework. Assertion 280 | packages are acceptable if they provide _real_ incremental value. 281 | 10. Even though we call these "rules" above, they are actually just 282 | guidelines. Since you've read all the rules, you now know that. 283 | 284 | If you are having trouble getting into the mood of idiomatic Go, we recommend 285 | reading through [Effective Go](https://golang.org/doc/effective_go.html). The 286 | [Go Blog](https://blog.golang.org) is also a great resource. 287 | -------------------------------------------------------------------------------- /clidocstool_yaml.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 cli-docs-tool authors 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 | package clidocstool 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "log" 21 | "os" 22 | "path/filepath" 23 | "sort" 24 | "strings" 25 | 26 | "github.com/docker/cli-docs-tool/annotation" 27 | "github.com/spf13/cobra" 28 | "github.com/spf13/pflag" 29 | "go.yaml.in/yaml/v3" 30 | ) 31 | 32 | type cmdOption struct { 33 | Option string 34 | Shorthand string `yaml:",omitempty"` 35 | ValueType string `yaml:"value_type,omitempty"` 36 | DefaultValue string `yaml:"default_value,omitempty"` 37 | Description string `yaml:",omitempty"` 38 | DetailsURL string `yaml:"details_url,omitempty"` // DetailsURL contains an anchor-id or link for more information on this flag 39 | Deprecated bool 40 | Hidden bool 41 | MinAPIVersion string `yaml:"min_api_version,omitempty"` 42 | Experimental bool 43 | ExperimentalCLI bool 44 | Kubernetes bool 45 | Swarm bool 46 | OSType string `yaml:"os_type,omitempty"` 47 | } 48 | 49 | type cmdDoc struct { 50 | Name string `yaml:"command"` 51 | SeeAlso []string `yaml:"parent,omitempty"` 52 | Version string `yaml:"engine_version,omitempty"` 53 | Aliases string `yaml:",omitempty"` 54 | Short string `yaml:",omitempty"` 55 | Long string `yaml:",omitempty"` 56 | Usage string `yaml:",omitempty"` 57 | Pname string `yaml:",omitempty"` 58 | Plink string `yaml:",omitempty"` 59 | Cname []string `yaml:",omitempty"` 60 | Clink []string `yaml:",omitempty"` 61 | Options []cmdOption `yaml:",omitempty"` 62 | InheritedOptions []cmdOption `yaml:"inherited_options,omitempty"` 63 | Example string `yaml:"examples,omitempty"` 64 | Deprecated bool 65 | Hidden bool 66 | MinAPIVersion string `yaml:"min_api_version,omitempty"` 67 | Experimental bool 68 | ExperimentalCLI bool 69 | Kubernetes bool 70 | Swarm bool 71 | OSType string `yaml:"os_type,omitempty"` 72 | } 73 | 74 | // GenYamlTree creates yaml structured ref files for this command and all descendants 75 | // in the directory given. This function may not work 76 | // correctly if your command names have `-` in them. If you have `cmd` with two 77 | // subcmds, `sub` and `sub-third`, and `sub` has a subcommand called `third` 78 | // it is undefined which help output will be in the file `cmd-sub-third.1`. 79 | func (c *Client) GenYamlTree(cmd *cobra.Command) error { 80 | emptyStr := func(string) string { return "" } 81 | if err := c.loadLongDescription(cmd, "yaml"); err != nil { 82 | return err 83 | } 84 | return c.genYamlTreeCustom(cmd, emptyStr) 85 | } 86 | 87 | // genYamlTreeCustom creates yaml structured ref files. 88 | func (c *Client) genYamlTreeCustom(cmd *cobra.Command, filePrepender func(string) string) error { 89 | for _, sc := range cmd.Commands() { 90 | if !sc.Runnable() && !sc.HasAvailableSubCommands() { 91 | // skip non-runnable commands without subcommands 92 | // but *do* generate YAML for hidden and deprecated commands 93 | // the YAML will have those included as metadata, so that the 94 | // documentation repository can decide whether or not to present them 95 | continue 96 | } 97 | if err := c.genYamlTreeCustom(sc, filePrepender); err != nil { 98 | return err 99 | } 100 | } 101 | 102 | // always disable the addition of [flags] to the usage 103 | cmd.DisableFlagsInUseLine = true 104 | 105 | // The "root" command used in the generator is just a "stub", and only has a 106 | // list of subcommands, but not (e.g.) global options/flags. We should fix 107 | // that, so that the YAML file for the docker "root" command contains the 108 | // global flags. 109 | 110 | // Skip the root command altogether, to prevent generating a useless 111 | // YAML file for plugins. 112 | if c.plugin && !cmd.HasParent() { 113 | return nil 114 | } 115 | 116 | log.Printf("INFO: Generating YAML for %q", cmd.CommandPath()) 117 | basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".yaml" 118 | target := filepath.Join(c.target, basename) 119 | f, err := os.Create(target) 120 | if err != nil { 121 | return err 122 | } 123 | defer f.Close() 124 | 125 | if _, err := io.WriteString(f, filePrepender(target)); err != nil { 126 | return err 127 | } 128 | return c.genYamlCustom(cmd, f) 129 | } 130 | 131 | // genYamlCustom creates custom yaml output. 132 | // nolint: gocyclo 133 | func (c *Client) genYamlCustom(cmd *cobra.Command, w io.Writer) error { 134 | const ( 135 | // shortMaxWidth is the maximum width for the "Short" description before 136 | // we force YAML to use multi-line syntax. The goal is to make the total 137 | // width fit within 80 characters. This value is based on 80 characters 138 | // minus the with of the field, colon, and whitespace ('short: '). 139 | shortMaxWidth = 73 140 | 141 | // longMaxWidth is the maximum width for the "Short" description before 142 | // we force YAML to use multi-line syntax. The goal is to make the total 143 | // width fit within 80 characters. This value is based on 80 characters 144 | // minus the with of the field, colon, and whitespace ('long: '). 145 | longMaxWidth = 74 146 | ) 147 | 148 | // necessary to add inherited flags otherwise some 149 | // fields are not properly declared like usage 150 | cmd.Flags().AddFlagSet(cmd.InheritedFlags()) 151 | 152 | cliDoc := cmdDoc{ 153 | Name: cmd.CommandPath(), 154 | Aliases: strings.Join(getAliases(cmd), ", "), 155 | Short: forceMultiLine(cmd.Short, shortMaxWidth), 156 | Long: forceMultiLine(cmd.Long, longMaxWidth), 157 | Example: cmd.Example, 158 | Deprecated: len(cmd.Deprecated) > 0, 159 | Hidden: cmd.Hidden, 160 | } 161 | 162 | if len(cliDoc.Long) == 0 { 163 | cliDoc.Long = cliDoc.Short 164 | } 165 | 166 | if cmd.Runnable() { 167 | cliDoc.Usage = cmd.UseLine() 168 | } 169 | 170 | // check recursively to handle inherited annotations 171 | for curr := cmd; curr != nil; curr = curr.Parent() { 172 | if curr.Hidden { 173 | cliDoc.Hidden = true 174 | } 175 | if v, ok := curr.Annotations["version"]; ok && cliDoc.MinAPIVersion == "" { 176 | cliDoc.MinAPIVersion = v 177 | } 178 | if _, ok := curr.Annotations["experimental"]; ok && !cliDoc.Experimental { 179 | cliDoc.Experimental = true 180 | } 181 | if _, ok := curr.Annotations["experimentalCLI"]; ok && !cliDoc.ExperimentalCLI { 182 | cliDoc.ExperimentalCLI = true 183 | } 184 | if _, ok := curr.Annotations["kubernetes"]; ok && !cliDoc.Kubernetes { 185 | cliDoc.Kubernetes = true 186 | } 187 | if _, ok := curr.Annotations["swarm"]; ok && !cliDoc.Swarm { 188 | cliDoc.Swarm = true 189 | } 190 | if o, ok := curr.Annotations["ostype"]; ok && cliDoc.OSType == "" { 191 | cliDoc.OSType = o 192 | } 193 | if _, ok := cmd.Annotations[annotation.CodeDelimiter]; !ok { 194 | if cd, cok := curr.Annotations[annotation.CodeDelimiter]; cok { 195 | if cmd.Annotations == nil { 196 | cmd.Annotations = map[string]string{} 197 | } 198 | cmd.Annotations[annotation.CodeDelimiter] = cd 199 | } 200 | } 201 | } 202 | 203 | anchors := make(map[string]struct{}) 204 | if a, ok := cmd.Annotations["anchors"]; ok && a != "" { 205 | for _, anchor := range strings.Split(a, ",") { 206 | anchors[anchor] = struct{}{} 207 | } 208 | } 209 | 210 | flags := cmd.NonInheritedFlags() 211 | if flags.HasFlags() { 212 | cliDoc.Options = genFlagResult(cmd, flags, anchors) 213 | } 214 | flags = cmd.InheritedFlags() 215 | if flags.HasFlags() { 216 | cliDoc.InheritedOptions = genFlagResult(cmd, flags, anchors) 217 | } 218 | 219 | if hasSeeAlso(cmd) { 220 | if cmd.HasParent() { 221 | parent := cmd.Parent() 222 | cliDoc.Pname = parent.CommandPath() 223 | cliDoc.Plink = strings.Replace(cliDoc.Pname, " ", "_", -1) + ".yaml" 224 | cmd.VisitParents(func(c *cobra.Command) { 225 | if c.DisableAutoGenTag { 226 | cmd.DisableAutoGenTag = c.DisableAutoGenTag 227 | } 228 | }) 229 | } 230 | 231 | children := cmd.Commands() 232 | sort.Sort(byName(children)) 233 | 234 | for _, child := range children { 235 | if !child.IsAvailableCommand() || child.IsAdditionalHelpTopicCommand() { 236 | continue 237 | } 238 | cliDoc.Cname = append(cliDoc.Cname, cliDoc.Name+" "+child.Name()) 239 | cliDoc.Clink = append(cliDoc.Clink, strings.Replace(cliDoc.Name+"_"+child.Name(), " ", "_", -1)+".yaml") 240 | } 241 | } 242 | 243 | final, err := yaml.Marshal(&cliDoc) 244 | if err != nil { 245 | fmt.Println(err) 246 | os.Exit(1) 247 | } 248 | if _, err := fmt.Fprintln(w, string(final)); err != nil { 249 | return err 250 | } 251 | return nil 252 | } 253 | 254 | func genFlagResult(cmd *cobra.Command, flags *pflag.FlagSet, anchors map[string]struct{}) []cmdOption { 255 | var ( 256 | result []cmdOption 257 | opt cmdOption 258 | ) 259 | 260 | const ( 261 | // shortMaxWidth is the maximum width for the "Short" description before 262 | // we force YAML to use multi-line syntax. The goal is to make the total 263 | // width fit within 80 characters. This value is based on 80 characters 264 | // minus the with of the field, colon, and whitespace (' default_value: '). 265 | defaultValueMaxWidth = 64 266 | 267 | // longMaxWidth is the maximum width for the "Short" description before 268 | // we force YAML to use multi-line syntax. The goal is to make the total 269 | // width fit within 80 characters. This value is based on 80 characters 270 | // minus the with of the field, colon, and whitespace (' description: '). 271 | descriptionMaxWidth = 66 272 | ) 273 | 274 | flags.VisitAll(func(flag *pflag.Flag) { 275 | opt = cmdOption{ 276 | Option: flag.Name, 277 | ValueType: flag.Value.Type(), 278 | Deprecated: len(flag.Deprecated) > 0, 279 | Hidden: flag.Hidden, 280 | } 281 | 282 | var defval string 283 | if v, ok := flag.Annotations[annotation.DefaultValue]; ok && len(v) > 0 { 284 | defval = v[0] 285 | if cd, ok := flag.Annotations[annotation.CodeDelimiter]; ok { 286 | defval = strings.ReplaceAll(defval, cd[0], "`") 287 | } else if cd, ok := cmd.Annotations[annotation.CodeDelimiter]; ok { 288 | defval = strings.ReplaceAll(defval, cd, "`") 289 | } 290 | } else { 291 | defval = flag.DefValue 292 | } 293 | opt.DefaultValue = forceMultiLine(defval, defaultValueMaxWidth) 294 | 295 | usage := flag.Usage 296 | if cd, ok := flag.Annotations[annotation.CodeDelimiter]; ok { 297 | usage = strings.ReplaceAll(usage, cd[0], "`") 298 | } else if cd, ok := cmd.Annotations[annotation.CodeDelimiter]; ok { 299 | usage = strings.ReplaceAll(usage, cd, "`") 300 | } 301 | opt.Description = forceMultiLine(usage, descriptionMaxWidth) 302 | 303 | if v, ok := flag.Annotations[annotation.ExternalURL]; ok && len(v) > 0 { 304 | opt.DetailsURL = strings.TrimPrefix(v[0], "https://docs.docker.com") 305 | } else if _, ok = anchors[flag.Name]; ok { 306 | opt.DetailsURL = "#" + flag.Name 307 | } 308 | 309 | // Todo, when we mark a shorthand is deprecated, but specify an empty message. 310 | // The flag.ShorthandDeprecated is empty as the shorthand is deprecated. 311 | // Using len(flag.ShorthandDeprecated) > 0 can't handle this, others are ok. 312 | if !(len(flag.ShorthandDeprecated) > 0) && len(flag.Shorthand) > 0 { 313 | opt.Shorthand = flag.Shorthand 314 | } 315 | if _, ok := flag.Annotations["experimental"]; ok { 316 | opt.Experimental = true 317 | } 318 | if _, ok := flag.Annotations["deprecated"]; ok { 319 | opt.Deprecated = true 320 | } 321 | if v, ok := flag.Annotations["version"]; ok { 322 | opt.MinAPIVersion = v[0] 323 | } 324 | if _, ok := flag.Annotations["experimentalCLI"]; ok { 325 | opt.ExperimentalCLI = true 326 | } 327 | if _, ok := flag.Annotations["kubernetes"]; ok { 328 | opt.Kubernetes = true 329 | } 330 | if _, ok := flag.Annotations["swarm"]; ok { 331 | opt.Swarm = true 332 | } 333 | 334 | // Note that the annotation can have multiple ostypes set, however, multiple 335 | // values are currently not used (and unlikely will). 336 | // 337 | // To simplify usage of the os_type property in the YAML, and for consistency 338 | // with the same property for commands, we're only using the first ostype that's set. 339 | if ostypes, ok := flag.Annotations["ostype"]; ok && len(opt.OSType) == 0 && len(ostypes) > 0 { 340 | opt.OSType = ostypes[0] 341 | } 342 | 343 | result = append(result, opt) 344 | }) 345 | 346 | return result 347 | } 348 | 349 | // forceMultiLine appends a newline (\n) to strings that are longer than max 350 | // to force the yaml lib to use block notation (https://yaml.org/spec/1.2/spec.html#Block) 351 | // instead of a single-line string with newlines and tabs encoded("string\nline1\nline2"). 352 | // 353 | // This makes the generated YAML more readable, and easier to review changes. 354 | // max can be used to customize the width to keep the whole line < 80 chars. 355 | func forceMultiLine(s string, maxWidth int) string { 356 | s = strings.TrimSpace(s) 357 | if len(s) > maxWidth && !strings.Contains(s, "\n") { 358 | s = s + "\n" 359 | } 360 | return s 361 | } 362 | 363 | // Small duplication for cobra utils 364 | func hasSeeAlso(cmd *cobra.Command) bool { 365 | if cmd.HasParent() { 366 | return true 367 | } 368 | for _, c := range cmd.Commands() { 369 | if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() { 370 | continue 371 | } 372 | return true 373 | } 374 | return false 375 | } 376 | 377 | type byName []*cobra.Command 378 | 379 | func (s byName) Len() int { return len(s) } 380 | func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 381 | func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() } 382 | -------------------------------------------------------------------------------- /example/go.sum: -------------------------------------------------------------------------------- 1 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= 2 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= 3 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= 4 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 5 | github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= 6 | github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 7 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 8 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 9 | github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ= 10 | github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c= 11 | github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= 12 | github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 13 | github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= 14 | github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= 15 | github.com/apparentlymart/go-cidr v1.0.1 h1:NmIwLZ/KdsjIUlhf+/Np40atNXm/+lZ5txfTJ/SpF+U= 16 | github.com/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= 17 | github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= 18 | github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= 19 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= 20 | github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= 21 | github.com/aws/aws-sdk-go-v2 v1.38.1 h1:j7sc33amE74Rz0M/PoCpsZQ6OunLqys/m5antM0J+Z8= 22 | github.com/aws/aws-sdk-go-v2 v1.38.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= 23 | github.com/aws/aws-sdk-go-v2/config v1.31.3 h1:RIb3yr/+PZ18YYNe6MDiG/3jVoJrPmdoCARwNkMGvco= 24 | github.com/aws/aws-sdk-go-v2/config v1.31.3/go.mod h1:jjgx1n7x0FAKl6TnakqrpkHWWKcX3xfWtdnIJs5K9CE= 25 | github.com/aws/aws-sdk-go-v2/credentials v1.18.7 h1:zqg4OMrKj+t5HlswDApgvAHjxKtlduKS7KicXB+7RLg= 26 | github.com/aws/aws-sdk-go-v2/credentials v1.18.7/go.mod h1:/4M5OidTskkgkv+nCIfC9/tbiQ/c8qTox9QcUDV0cgc= 27 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 h1:lpdMwTzmuDLkgW7086jE94HweHCqG+uOJwHf3LZs7T0= 28 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4/go.mod h1:9xzb8/SV62W6gHQGC/8rrvgNXU6ZoYM3sAIJCIrXJxY= 29 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 h1:IdCLsiiIj5YJ3AFevsewURCPV+YWUlOW8JiPhoAy8vg= 30 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4/go.mod h1:l4bdfCD7XyyZA9BolKBo1eLqgaJxl0/x91PL4Yqe0ao= 31 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 h1:j7vjtr1YIssWQOMeOWRbh3z8g2oY/xPjnZH2gLY4sGw= 32 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4/go.mod h1:yDmJgqOiH4EA8Hndnv4KwAo8jCGTSnM5ASG1nBI+toA= 33 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= 34 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= 35 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM= 36 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44= 37 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 h1:ueB2Te0NacDMnaC+68za9jLwkjzxGWm0KB5HTUHjLTI= 38 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4/go.mod h1:nLEfLnVMmLvyIG58/6gsSA03F1voKGaCfHV7+lR8S7s= 39 | github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 h1:ve9dYBB8CfJGTFqcQ3ZLAAb/KXWgYlgu/2R2TZL2Ko0= 40 | github.com/aws/aws-sdk-go-v2/service/sso v1.28.2/go.mod h1:n9bTZFZcBa9hGGqVz3i/a6+NG0zmZgtkB9qVVFDqPA8= 41 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.0 h1:Bnr+fXrlrPEoR1MAFrHVsge3M/WoK4n23VNhRM7TPHI= 42 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.0/go.mod h1:eknndR9rU8UpE/OmFpqU78V1EcXPKFTTm5l/buZYgvM= 43 | github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 h1:iV1Ko4Em/lkJIsoKyGfc0nQySi+v0Udxr6Igq+y9JZc= 44 | github.com/aws/aws-sdk-go-v2/service/sts v1.38.0/go.mod h1:bEPcjW7IbolPfK67G1nilqWyoxYMSPrDiIQ3RdIdKgo= 45 | github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= 46 | github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= 47 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 48 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 49 | github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= 50 | github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= 51 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 52 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 53 | github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= 54 | github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= 55 | github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs= 56 | github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE= 57 | github.com/containerd/cgroups/v3 v3.1.0 h1:azxYVj+91ZgSnIBp2eI3k9y2iYQSR/ZQIgh9vKO+HSY= 58 | github.com/containerd/cgroups/v3 v3.1.0/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= 59 | github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= 60 | github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= 61 | github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o= 62 | github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM= 63 | github.com/containerd/containerd/v2 v2.2.0 h1:K7TqcXy+LnFmZaui2DgHsnp2gAHhVNWYaHlx7HXfys8= 64 | github.com/containerd/containerd/v2 v2.2.0/go.mod h1:YCMjKjA4ZA7egdHNi3/93bJR1+2oniYlnS+c0N62HdE= 65 | github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= 66 | github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= 67 | github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= 68 | github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= 69 | github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= 70 | github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= 71 | github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= 72 | github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= 73 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 74 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 75 | github.com/containerd/nydus-snapshotter v0.15.4 h1:l59kGRVMtwMLDLh322HsWhEsBCkRKMkGWYV5vBeLYCE= 76 | github.com/containerd/nydus-snapshotter v0.15.4/go.mod h1:eRJqnxQDr48HNop15kZdLZpFF5B6vf6Q11Aq1K0E4Ms= 77 | github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= 78 | github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= 79 | github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= 80 | github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= 81 | github.com/containerd/stargz-snapshotter v0.17.0 h1:djNS4KU8ztFhLdEDZ1bsfzOiYuVHT6TgSU5qwRk+cNc= 82 | github.com/containerd/stargz-snapshotter/estargz v0.17.0 h1:+TyQIsR/zSFI1Rm31EQBwpAA1ovYgIKHy7kctL3sLcE= 83 | github.com/containerd/stargz-snapshotter/estargz v0.17.0/go.mod h1:s06tWAiJcXQo9/8AReBCIo/QxcXFZ2n4qfsRnpl71SM= 84 | github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= 85 | github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= 86 | github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= 87 | github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= 88 | github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 89 | github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= 90 | github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= 91 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 92 | github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= 93 | github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= 94 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 95 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 96 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 97 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 98 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 99 | github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= 100 | github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= 101 | github.com/docker/buildx v0.30.0 h1:Fz7LzHqEmnE8JNpDPxLWVSKUdD5ysXn2bq8JOpRpvAY= 102 | github.com/docker/buildx v0.30.0/go.mod h1:m7/CUbSd2skvugtGYENwn96mk5Ffar28xmLmeAVMc4c= 103 | github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY= 104 | github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 105 | github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= 106 | github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 107 | github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM= 108 | github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 109 | github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= 110 | github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= 111 | github.com/docker/go v1.5.1-1 h1:hr4w35acWBPhGBXlzPoHpmZ/ygPjnmFVxGxxGnMyP7k= 112 | github.com/docker/go v1.5.1-1/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= 113 | github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= 114 | github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= 115 | github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= 116 | github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= 117 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 118 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 119 | github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= 120 | github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 121 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 122 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 123 | github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE= 124 | github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= 125 | github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= 126 | github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= 127 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 128 | github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= 129 | github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 130 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 131 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 132 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 133 | github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= 134 | github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= 135 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 136 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 137 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 138 | github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= 139 | github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= 140 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 141 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 142 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= 143 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 144 | github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= 145 | github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= 146 | github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= 147 | github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= 148 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 149 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 150 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= 151 | github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= 152 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 153 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 154 | github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= 155 | github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= 156 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 157 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 158 | github.com/google/go-dap v0.12.0 h1:rVcjv3SyMIrpaOoTAdFDyHs99CwVOItIJGKLQFQhNeM= 159 | github.com/google/go-dap v0.12.0/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc= 160 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 161 | github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= 162 | github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= 163 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= 164 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= 165 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 166 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 167 | github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= 168 | github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= 169 | github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= 170 | github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= 171 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= 172 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= 173 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 174 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 175 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 176 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 177 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 178 | github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928 h1:NFpfJOqEV8DUhomtXx9A0FfcMg7OCYRugdyw6Ar/Y7s= 179 | github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928/go.mod h1:YC9ASYt9Z9sQEAtzCe+yaAzi3E7wcxfRphDXtwZoWC0= 180 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 181 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 182 | github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= 183 | github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= 184 | github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= 185 | github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= 186 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 187 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 188 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 189 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 190 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 191 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 192 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 193 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 194 | github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= 195 | github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= 196 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 197 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 198 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 199 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 200 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 201 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 202 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 203 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 204 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 205 | github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= 206 | github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 207 | github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= 208 | github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= 209 | github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= 210 | github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= 211 | github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= 212 | github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= 213 | github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= 214 | github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= 215 | github.com/moby/buildkit v0.26.1 h1:Cf/AB/8/5N+GBQnVPBW+hR2tDWoImuZ28ciqaF+mzgs= 216 | github.com/moby/buildkit v0.26.1/go.mod h1:ylDa7IqzVJgLdi/wO7H1qLREFQpmhFbw2fbn4yoTw40= 217 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 218 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 219 | github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= 220 | github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= 221 | github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= 222 | github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= 223 | github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= 224 | github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 225 | github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= 226 | github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= 227 | github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= 228 | github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= 229 | github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= 230 | github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= 231 | github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= 232 | github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= 233 | github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= 234 | github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= 235 | github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= 236 | github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= 237 | github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= 238 | github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= 239 | github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= 240 | github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= 241 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 242 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 243 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 244 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 245 | github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= 246 | github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 247 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 248 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 249 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 250 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 251 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= 252 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= 253 | github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= 254 | github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= 255 | github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= 256 | github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 257 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 258 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 259 | github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= 260 | github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= 261 | github.com/opencontainers/runtime-spec v1.2.1 h1:S4k4ryNgEpxW1dzyqffOmhI1BHYcjzU8lpJfSlR0xww= 262 | github.com/opencontainers/runtime-spec v1.2.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= 263 | github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8= 264 | github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U= 265 | github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= 266 | github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= 267 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= 268 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= 269 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 270 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 271 | github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= 272 | github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 273 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 274 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 275 | github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= 276 | github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= 277 | github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= 278 | github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= 279 | github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= 280 | github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= 281 | github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= 282 | github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= 283 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 284 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 285 | github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= 286 | github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= 287 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 288 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 289 | github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= 290 | github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= 291 | github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g= 292 | github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU= 293 | github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b h1:h+3JX2VoWTFuyQEo87pStk/a99dzIO1mM9KxIyLPGTU= 294 | github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= 295 | github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= 296 | github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= 297 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 298 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 299 | github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk= 300 | github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE= 301 | github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= 302 | github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= 303 | github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 304 | github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= 305 | github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 306 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 307 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 308 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 309 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 310 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 311 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 312 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 313 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 314 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 315 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 316 | github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= 317 | github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 318 | github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c= 319 | github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= 320 | github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4= 321 | github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY= 322 | github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f h1:MoxeMfHAe5Qj/ySSBfL8A7l1V+hxuluj8owsIEEZipI= 323 | github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98= 324 | github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE= 325 | github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE= 326 | github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117 h1:XFwyh2JZwR5aiKLXHX2C1n0v5F11dCJpyGL1W/Cpl3U= 327 | github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117/go.mod h1:3Ez1Paeg+0Ghu3KwpEGC1HgZ4CHDlg+Ez/5Baeomk54= 328 | github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0= 329 | github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk= 330 | github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw= 331 | github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc= 332 | github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= 333 | github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= 334 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 335 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 336 | github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= 337 | github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= 338 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 339 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 340 | github.com/zclconf/go-cty v1.17.0 h1:seZvECve6XX4tmnvRzWtJNHdscMtYEx5R7bnnVyd/d0= 341 | github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U= 342 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= 343 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= 344 | go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= 345 | go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= 346 | go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= 347 | go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= 348 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ= 349 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo= 350 | go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0 h1:lREC4C0ilyP4WibDhQ7Gg2ygAQFP8oR07Fst/5cafwI= 351 | go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.61.0/go.mod h1:HfvuU0kW9HewH14VCOLImqKvUgONodURG7Alj/IrnGI= 352 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= 353 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= 354 | go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= 355 | go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= 356 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8= 357 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw= 358 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= 359 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= 360 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= 361 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= 362 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= 363 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= 364 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= 365 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= 366 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0 h1:UGZ1QwZWY67Z6BmckTU+9Rxn04m2bD3gD6Mk0OIOCPk= 367 | go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64= 368 | go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= 369 | go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= 370 | go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= 371 | go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= 372 | go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= 373 | go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= 374 | go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= 375 | go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= 376 | go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= 377 | go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= 378 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= 379 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= 380 | go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI= 381 | go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= 382 | go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= 383 | go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 384 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 385 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 386 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 387 | golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= 388 | golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= 389 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 390 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 391 | golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= 392 | golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= 393 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 394 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 395 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 396 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 397 | golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= 398 | golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= 399 | golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= 400 | golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= 401 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 402 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 403 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 404 | golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= 405 | golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 406 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 407 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 408 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 409 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 410 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 411 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 412 | golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= 413 | golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 414 | golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= 415 | golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= 416 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 417 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 418 | golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= 419 | golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= 420 | golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= 421 | golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= 422 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 423 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 424 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 425 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 426 | golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= 427 | golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= 428 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 429 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 430 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 431 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 432 | gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= 433 | gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= 434 | google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= 435 | google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= 436 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= 437 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= 438 | google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= 439 | google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= 440 | google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= 441 | google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 442 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 443 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 444 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 445 | gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= 446 | gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= 447 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 448 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 449 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 450 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 451 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 452 | gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= 453 | gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= 454 | k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= 455 | k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= 456 | k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= 457 | k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= 458 | k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= 459 | k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= 460 | k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= 461 | k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 462 | k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= 463 | k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b/go.mod h1:UZ2yyWbFTpuhSbFhv24aGNOdoRdJZgsIObGBUaYVsts= 464 | k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 h1:hwvWFiBzdWw1FhfY1FooPn3kzWuJ8tmbZBHi4zVsl1Y= 465 | k8s.io/utils v0.0.0-20250604170112-4c0f3b243397/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 466 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= 467 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= 468 | sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= 469 | sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= 470 | sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= 471 | sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= 472 | sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= 473 | sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= 474 | --------------------------------------------------------------------------------