From 891740ec8a7b27e047e21abc12bb89f6423c088e Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Thu, 9 Jun 2016 11:33:28 -0400 Subject: [PATCH 01/14] Add a script to generate man pages from cobra commands. Use the generate.sh script instead of md2man directly. Update Dockerfile for generating man pages. Signed-off-by: Daniel Nephin --- man/generate.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 man/generate.go diff --git a/man/generate.go b/man/generate.go new file mode 100644 index 0000000..7bcc570 --- /dev/null +++ b/man/generate.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "os" + + "github.com/docker/docker/cli/cobraadaptor" + cliflags "github.com/docker/docker/cli/flags" + "github.com/spf13/cobra/doc" +) + +func generateManPages(path string) error { + header := &doc.GenManHeader{ + Title: "DOCKER", + Section: "1", + Source: "Docker Community", + } + flags := &cliflags.ClientFlags{ + Common: cliflags.InitCommonFlags(), + } + cmd := cobraadaptor.NewCobraAdaptor(flags).GetRootCommand() + cmd.DisableAutoGenTag = true + return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ + Header: header, + Path: path, + CommandSeparator: "-", + }) +} + +func main() { + path := "/tmp" + if len(os.Args) > 1 { + path = os.Args[1] + } + fmt.Printf("Generating man pages into %s\n", path) + if err := generateManPages(path); err != nil { + fmt.Fprintf(os.Stderr, "Failed to generate man pages: %s\n", err.Error()) + } +} From 357f26d574e8d76b833ddd64f4c21e96173e7553 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Thu, 23 Jun 2016 11:25:51 -0400 Subject: [PATCH 02/14] Remove old cli framework. Also consolidate the leftover packages under cli. Remove pkg/mflag. Make manpage generation work with new cobra layout. Remove remaining mflag and fix tests after rebase with master. Signed-off-by: Daniel Nephin --- man/generate.go | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/man/generate.go b/man/generate.go index 7bcc570..cee5a7f 100644 --- a/man/generate.go +++ b/man/generate.go @@ -4,8 +4,10 @@ import ( "fmt" "os" - "github.com/docker/docker/cli/cobraadaptor" - cliflags "github.com/docker/docker/cli/flags" + "github.com/docker/docker/api/client" + "github.com/docker/docker/api/client/command" + "github.com/docker/docker/pkg/term" + "github.com/spf13/cobra" "github.com/spf13/cobra/doc" ) @@ -15,10 +17,12 @@ func generateManPages(path string) error { Section: "1", Source: "Docker Community", } - flags := &cliflags.ClientFlags{ - Common: cliflags.InitCommonFlags(), - } - cmd := cobraadaptor.NewCobraAdaptor(flags).GetRootCommand() + + stdin, stdout, stderr := term.StdStreams() + dockerCli := client.NewDockerCli(stdin, stdout, stderr) + cmd := &cobra.Command{Use: "docker"} + command.AddCommands(cmd, dockerCli) + cmd.DisableAutoGenTag = true return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ Header: header, From 7b32646aeb04095f226c75aadc17e8f6377bec05 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Thu, 8 Sep 2016 13:11:39 -0400 Subject: [PATCH 03/14] Move api/client -> cli/command Using gomvpkg -from github.com/docker/docker/api/client -to github.com/docker/docker/cli/command -vcs_mv_cmd 'git mv {{.Src}} {{.Dst}}' Signed-off-by: Daniel Nephin --- man/generate.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/man/generate.go b/man/generate.go index cee5a7f..f21614d 100644 --- a/man/generate.go +++ b/man/generate.go @@ -4,8 +4,8 @@ import ( "fmt" "os" - "github.com/docker/docker/api/client" - "github.com/docker/docker/api/client/command" + "github.com/docker/docker/cli/command" + "github.com/docker/docker/cli/command/commands" "github.com/docker/docker/pkg/term" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" @@ -19,9 +19,9 @@ func generateManPages(path string) error { } stdin, stdout, stderr := term.StdStreams() - dockerCli := client.NewDockerCli(stdin, stdout, stderr) + dockerCli := command.NewDockerCli(stdin, stdout, stderr) cmd := &cobra.Command{Use: "docker"} - command.AddCommands(cmd, dockerCli) + commands.AddCommands(cmd, dockerCli) cmd.DisableAutoGenTag = true return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ From 12670c0c752fb1963827614c4dde9178602de66d Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Thu, 22 Sep 2016 14:11:08 -0400 Subject: [PATCH 04/14] Read long description from a file. Signed-off-by: Daniel Nephin --- man/generate.go | 65 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 7 deletions(-) diff --git a/man/generate.go b/man/generate.go index f21614d..1516158 100644 --- a/man/generate.go +++ b/man/generate.go @@ -2,16 +2,22 @@ package main import ( "fmt" + "io/ioutil" + "log" "os" + "path/filepath" "github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command/commands" "github.com/docker/docker/pkg/term" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" + "github.com/spf13/pflag" ) -func generateManPages(path string) error { +const descriptionSourcePath = "man/src/" + +func generateManPages(opts *options) error { header := &doc.GenManHeader{ Title: "DOCKER", Section: "1", @@ -22,22 +28,67 @@ func generateManPages(path string) error { dockerCli := command.NewDockerCli(stdin, stdout, stderr) cmd := &cobra.Command{Use: "docker"} commands.AddCommands(cmd, dockerCli) + source := filepath.Join(opts.source, descriptionSourcePath) + if err := loadLongDescription(cmd, source); err != nil { + return err + } cmd.DisableAutoGenTag = true return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ Header: header, - Path: path, + Path: opts.target, CommandSeparator: "-", }) } +func loadLongDescription(cmd *cobra.Command, path string) error { + for _, cmd := range cmd.Commands() { + if cmd.Name() == "" { + continue + } + fullpath := filepath.Join(path, cmd.Name()+".md") + + if cmd.HasSubCommands() { + loadLongDescription(cmd, filepath.Join(path, cmd.Name())) + } + + if _, err := os.Stat(fullpath); err != nil { + log.Printf("WARN: %s does not exist, skipping\n", fullpath) + continue + } + + content, err := ioutil.ReadFile(fullpath) + if err != nil { + return err + } + cmd.Long = string(content) + } + return nil +} + +type options struct { + source string + target string +} + +func parseArgs() (*options, error) { + opts := &options{} + cwd, _ := os.Getwd() + flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError) + flags.StringVar(&opts.source, "root", cwd, "Path to project root") + flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated man pages") + err := flags.Parse(os.Args[1:]) + return opts, err +} + func main() { - path := "/tmp" - if len(os.Args) > 1 { - path = os.Args[1] + opts, err := parseArgs() + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) } - fmt.Printf("Generating man pages into %s\n", path) - if err := generateManPages(path); err != nil { + fmt.Printf("Project root: %s\n", opts.source) + fmt.Printf("Generating man pages into %s\n", opts.target) + if err := generateManPages(opts); err != nil { fmt.Fprintf(os.Stderr, "Failed to generate man pages: %s\n", err.Error()) } } From 5f4920e8f08f7e98002ca11c25f3a5834e00f07f Mon Sep 17 00:00:00 2001 From: Kenfe-Mickael Laventure Date: Fri, 13 Jan 2017 09:05:29 -0800 Subject: [PATCH 05/14] Add example for device-cgroup-rule to man Signed-off-by: Kenfe-Mickael Laventure --- man/generate.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/man/generate.go b/man/generate.go index 1516158..964d692 100644 --- a/man/generate.go +++ b/man/generate.go @@ -62,6 +62,18 @@ func loadLongDescription(cmd *cobra.Command, path string) error { return err } cmd.Long = string(content) + + fullpath = filepath.Join(path, cmd.Name()+"-example.md") + if _, err := os.Stat(fullpath); err != nil { + continue + } + + content, err = ioutil.ReadFile(fullpath) + if err != nil { + return err + } + cmd.Example = string(content) + } return nil } From 51917b72415f3dbe8b20e28614f9bde35ed92e6f Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Fri, 2 Jun 2017 10:54:54 -0400 Subject: [PATCH 06/14] Add missing dependencies to vendor, and fix generation imports Signed-off-by: Daniel Nephin --- man/generate.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/generate.go b/man/generate.go index 964d692..f6e28f8 100644 --- a/man/generate.go +++ b/man/generate.go @@ -7,8 +7,8 @@ import ( "os" "path/filepath" - "github.com/docker/docker/cli/command" - "github.com/docker/docker/cli/command/commands" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/command/commands" "github.com/docker/docker/pkg/term" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" From 524adaef57bf04deb9931f14dc59a205ad1500f3 Mon Sep 17 00:00:00 2001 From: Daniel Nephin Date: Mon, 20 Nov 2017 12:48:10 -0500 Subject: [PATCH 07/14] Disable adding [flags] to UseLine in man pages Signed-off-by: Daniel Nephin --- man/generate.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/man/generate.go b/man/generate.go index f6e28f8..605e601 100644 --- a/man/generate.go +++ b/man/generate.go @@ -34,6 +34,7 @@ func generateManPages(opts *options) error { } cmd.DisableAutoGenTag = true + cmd.DisableFlagsInUseLine = true return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ Header: header, Path: opts.target, @@ -43,6 +44,7 @@ func generateManPages(opts *options) error { func loadLongDescription(cmd *cobra.Command, path string) error { for _, cmd := range cmd.Commands() { + cmd.DisableFlagsInUseLine = true if cmd.Name() == "" { continue } From 9d26ab26237dc8befb138f3d44e19e2c7e5fb73f Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Thu, 8 Mar 2018 14:35:17 +0100 Subject: [PATCH 08/14] Refactor content_trust cli/flags handling Remove the global variable used. Allows easier unit testing. Signed-off-by: Vincent Demeester --- man/generate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/generate.go b/man/generate.go index 605e601..4197558 100644 --- a/man/generate.go +++ b/man/generate.go @@ -25,7 +25,7 @@ func generateManPages(opts *options) error { } stdin, stdout, stderr := term.StdStreams() - dockerCli := command.NewDockerCli(stdin, stdout, stderr) + dockerCli := command.NewDockerCli(stdin, stdout, stderr, false) cmd := &cobra.Command{Use: "docker"} commands.AddCommands(cmd, dockerCli) source := filepath.Join(opts.source, descriptionSourcePath) From 2635ccf136e18142cf47d016d9839472096670c5 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Thu, 23 Aug 2018 19:53:55 +1000 Subject: [PATCH 09/14] man: obey SOURCE_DATE_EPOCH when generating man pages Previously our man pages included the current time each time they were generated. This causes an issue for reproducible builds, since each re-build of a package that includes the man pages will have different times listed in the man pages. To fix this, add support for SOURCE_DATE_EPOCH (which is a standardised packaging environment variable, designed to be used specifically for this purpose[1]). spf13/cobra doesn't support this natively yet (though I will push a patch for that as well), but it's simpler to fix it directly in docker/cli. [1]: https://reproducible-builds.org/specs/source-date-epoch/ Signed-off-by: Aleksa Sarai --- man/generate.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/man/generate.go b/man/generate.go index 4197558..4a3e98f 100644 --- a/man/generate.go +++ b/man/generate.go @@ -6,6 +6,8 @@ import ( "log" "os" "path/filepath" + "strconv" + "time" "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/commands" @@ -24,6 +26,17 @@ func generateManPages(opts *options) error { Source: "Docker Community", } + // If SOURCE_DATE_EPOCH is set, in order to allow reproducible package + // builds, we explicitly set the build time to SOURCE_DATE_EPOCH. + if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" { + unixEpoch, err := strconv.ParseInt(epoch, 10, 64) + if err != nil { + return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err) + } + now := time.Unix(unixEpoch, 0) + header.Date = &now + } + stdin, stdout, stderr := term.StdStreams() dockerCli := command.NewDockerCli(stdin, stdout, stderr, false) cmd := &cobra.Command{Use: "docker"} From 98cc7dc9ba7cf6dc2b48dcbbce9c2d7d054148bb Mon Sep 17 00:00:00 2001 From: Vincent Demeester Date: Tue, 11 Sep 2018 14:46:30 +0200 Subject: [PATCH 10/14] =?UTF-8?q?Remove=20containerizedengine=20package=20?= =?UTF-8?q?dependency=20from=20docker/cli/command=E2=80=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … this removes a whole lot of dependencies from people depending on docker/cli… Signed-off-by: Vincent Demeester --- man/generate.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/generate.go b/man/generate.go index 4197558..2d940e3 100644 --- a/man/generate.go +++ b/man/generate.go @@ -25,7 +25,7 @@ func generateManPages(opts *options) error { } stdin, stdout, stderr := term.StdStreams() - dockerCli := command.NewDockerCli(stdin, stdout, stderr, false) + dockerCli := command.NewDockerCli(stdin, stdout, stderr, false, nil) cmd := &cobra.Command{Use: "docker"} commands.AddCommands(cmd, dockerCli) source := filepath.Join(opts.source, descriptionSourcePath) From 22ad6373671afa8d540c213b28070c3c42bbc7a6 Mon Sep 17 00:00:00 2001 From: Silvin Lubecki Date: Mon, 28 Jan 2019 14:52:58 +0100 Subject: [PATCH 11/14] Introduce functional arguments to NewDockerCli for a more stable API. Signed-off-by: Silvin Lubecki --- man/generate.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/man/generate.go b/man/generate.go index e5e480b..63c4219 100644 --- a/man/generate.go +++ b/man/generate.go @@ -11,7 +11,6 @@ import ( "github.com/docker/cli/cli/command" "github.com/docker/cli/cli/command/commands" - "github.com/docker/docker/pkg/term" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" "github.com/spf13/pflag" @@ -37,8 +36,10 @@ func generateManPages(opts *options) error { header.Date = &now } - stdin, stdout, stderr := term.StdStreams() - dockerCli := command.NewDockerCli(stdin, stdout, stderr, false, nil) + dockerCli, err := command.NewDockerCli() + if err != nil { + return err + } cmd := &cobra.Command{Use: "docker"} commands.AddCommands(cmd, dockerCli) source := filepath.Join(opts.source, descriptionSourcePath) From 3c98406fd3069feb24f845ea58b732fa1e09302c Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Tue, 20 Oct 2020 12:48:52 +0200 Subject: [PATCH 12/14] man-pages: fix missing manual title in heading This was set in our manually written markdowns, but not in the man pages generated through Cobra. Signed-off-by: Sebastiaan van Stijn --- man/generate.go | 1 + 1 file changed, 1 insertion(+) diff --git a/man/generate.go b/man/generate.go index 63c4219..d21a646 100644 --- a/man/generate.go +++ b/man/generate.go @@ -23,6 +23,7 @@ func generateManPages(opts *options) error { Title: "DOCKER", Section: "1", Source: "Docker Community", + Manual: "Docker User Manuals", } // If SOURCE_DATE_EPOCH is set, in order to allow reproducible package From be388bff2a005cba8b0c99e1fc6ec76993ab4007 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:04:55 +0100 Subject: [PATCH 13/14] add man support Signed-off-by: CrazyMax --- clidocstool.go | 74 ++++++++++++++++++- clidocstool_man.go | 74 +++++++++++++++++++ clidocstool_man_test.go | 93 +++++++++++++++++++++++ clidocstool_md_test.go | 1 + clidocstool_test.go | 16 +++- clidocstool_yaml.go | 59 +-------------- clidocstool_yaml_test.go | 1 + example/go.mod | 2 + example/go.sum | 2 + example/main.go | 7 ++ fixtures/docker-buildx-build.1 | 122 ++++++++++++++++++++++++++++++ fixtures/docker-buildx-install.1 | 33 +++++++++ fixtures/docker-buildx-stop.1 | 33 +++++++++ fixtures/docker-buildx.1 | 31 ++++++++ go.mod | 2 + go.sum | 2 + man/generate.go | 123 ------------------------------- 17 files changed, 490 insertions(+), 185 deletions(-) create mode 100644 clidocstool_man.go create mode 100644 clidocstool_man_test.go create mode 100644 fixtures/docker-buildx-build.1 create mode 100644 fixtures/docker-buildx-install.1 create mode 100644 fixtures/docker-buildx-stop.1 create mode 100644 fixtures/docker-buildx.1 delete mode 100644 man/generate.go diff --git a/clidocstool.go b/clidocstool.go index d4aeaba..1c67a52 100644 --- a/clidocstool.go +++ b/clidocstool.go @@ -17,10 +17,13 @@ package clidocstool import ( "errors" "io" + "log" "os" + "path/filepath" "strings" "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" ) // Options defines options for cli-docs-tool @@ -29,6 +32,8 @@ type Options struct { SourceDir string TargetDir string Plugin bool + + ManHeader *doc.GenManHeader } // Client represents an active cli-docs-tool object @@ -37,6 +42,8 @@ type Client struct { source string target string plugin bool + + manHeader *doc.GenManHeader } // New initializes a new cli-docs-tool client @@ -48,9 +55,10 @@ func New(opts Options) (*Client, error) { return nil, errors.New("source dir required") } c := &Client{ - root: opts.Root, - source: opts.SourceDir, - plugin: opts.Plugin, + root: opts.Root, + source: opts.SourceDir, + plugin: opts.Plugin, + manHeader: opts.ManHeader, } if len(opts.TargetDir) == 0 { c.target = c.source @@ -73,9 +81,69 @@ func (c *Client) GenAllTree() error { if err = c.GenYamlTree(c.root); err != nil { return err } + if err = c.GenManTree(c.root); err != nil { + return err + } return nil } +// loadLongDescription gets long descriptions and examples from markdown. +func (c *Client) loadLongDescription(parentCmd *cobra.Command, generator string) error { + for _, cmd := range parentCmd.Commands() { + if cmd.HasSubCommands() { + if err := c.loadLongDescription(cmd, generator); err != nil { + return err + } + } + name := cmd.CommandPath() + if i := strings.Index(name, " "); i >= 0 { + // remove root command / binary name + name = name[i+1:] + } + if name == "" { + continue + } + mdFile := strings.ReplaceAll(name, " ", "_") + ".md" + sourcePath := filepath.Join(c.source, mdFile) + content, err := os.ReadFile(sourcePath) + if os.IsNotExist(err) { + log.Printf("WARN: %s does not exist, skipping Markdown examples for %s docs\n", mdFile, generator) + continue + } + if err != nil { + return err + } + applyDescriptionAndExamples(cmd, string(content)) + } + return nil +} + +// applyDescriptionAndExamples fills in cmd.Long and cmd.Example with the +// "Description" and "Examples" H2 sections in mdString (if present). +func applyDescriptionAndExamples(cmd *cobra.Command, mdString string) { + sections := getSections(mdString) + var ( + anchors []string + md string + ) + if sections["description"] != "" { + md, anchors = cleanupMarkDown(sections["description"]) + cmd.Long = md + anchors = append(anchors, md) + } + if sections["examples"] != "" { + md, anchors = cleanupMarkDown(sections["examples"]) + cmd.Example = md + anchors = append(anchors, md) + } + if len(anchors) > 0 { + if cmd.Annotations == nil { + cmd.Annotations = make(map[string]string) + } + cmd.Annotations["anchors"] = strings.Join(anchors, ",") + } +} + func fileExists(f string) bool { info, err := os.Stat(f) if os.IsNotExist(err) { diff --git a/clidocstool_man.go b/clidocstool_man.go new file mode 100644 index 0000000..e043f99 --- /dev/null +++ b/clidocstool_man.go @@ -0,0 +1,74 @@ +// Copyright 2016 cli-docs-tool authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package clidocstool + +import ( + "fmt" + "log" + "os" + "strconv" + "time" + + "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" +) + +// GenManTree generates a man page for the command and all descendants. +// If SOURCE_DATE_EPOCH is set, in order to allow reproducible package +// builds, we explicitly set the build time to SOURCE_DATE_EPOCH. +func (c *Client) GenManTree(cmd *cobra.Command) error { + if err := c.loadLongDescription(cmd, "man"); err != nil { + return err + } + + if epoch := os.Getenv("SOURCE_DATE_EPOCH"); c.manHeader != nil && epoch != "" { + unixEpoch, err := strconv.ParseInt(epoch, 10, 64) + if err != nil { + return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err) + } + now := time.Unix(unixEpoch, 0) + c.manHeader.Date = &now + } + + return c.genManTreeCustom(cmd) +} + +func (c *Client) genManTreeCustom(cmd *cobra.Command) error { + for _, sc := range cmd.Commands() { + if err := c.genManTreeCustom(sc); err != nil { + return err + } + } + + // always disable the addition of [flags] to the usage + cmd.DisableFlagsInUseLine = true + + // always disable "spf13/cobra" auto gen tag + cmd.DisableAutoGenTag = true + + // Skip the root command altogether, to prevent generating a useless + // md file for plugins. + if c.plugin && !cmd.HasParent() { + return nil + } + + log.Printf("INFO: Generating Man for %q", cmd.CommandPath()) + + return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ + Header: c.manHeader, + Path: c.target, + CommandSeparator: "-", + }) +} diff --git a/clidocstool_man_test.go b/clidocstool_man_test.go new file mode 100644 index 0000000..7c58860 --- /dev/null +++ b/clidocstool_man_test.go @@ -0,0 +1,93 @@ +// Copyright 2024 cli-docs-tool authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package clidocstool + +import ( + "io/fs" + "os" + "path" + "path/filepath" + "regexp" + "strconv" + "testing" + "time" + + "github.com/spf13/cobra/doc" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +//nolint:errcheck +func TestGenManTree(t *testing.T) { + setup() + tmpdir := t.TempDir() + + epoch, err := time.Parse("2006-Jan-02", "2020-Jan-10") + require.NoError(t, err) + t.Setenv("SOURCE_DATE_EPOCH", strconv.FormatInt(epoch.Unix(), 10)) + + require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) + + c, err := New(Options{ + Root: buildxCmd, + SourceDir: tmpdir, + Plugin: true, + ManHeader: &doc.GenManHeader{ + Title: "DOCKER", + Section: "1", + Source: "Docker Community", + Manual: "Docker User Manuals", + }, + }) + require.NoError(t, err) + require.NoError(t, c.GenManTree(buildxCmd)) + + seen := make(map[string]struct{}) + remanpage := regexp.MustCompile(`\.\d+$`) + + filepath.Walk("fixtures", func(path string, info fs.FileInfo, err error) error { + fname := filepath.Base(path) + // ignore dirs and any file that is not a manpage + if info.IsDir() || !remanpage.MatchString(fname) { + return nil + } + t.Run(fname, func(t *testing.T) { + seen[fname] = struct{}{} + require.NoError(t, err) + + bres, err := os.ReadFile(filepath.Join(tmpdir, fname)) + require.NoError(t, err) + + bexc, err := os.ReadFile(path) + require.NoError(t, err) + assert.Equal(t, string(bexc), string(bres)) + }) + return nil + }) + + filepath.Walk(tmpdir, func(path string, info fs.FileInfo, err error) error { + fname := filepath.Base(path) + // ignore dirs and any file that is not a manpage + if info.IsDir() || !remanpage.MatchString(fname) { + return nil + } + t.Run("seen_"+fname, func(t *testing.T) { + if _, ok := seen[fname]; !ok { + t.Errorf("file %s not found in fixtures", fname) + } + }) + return nil + }) +} diff --git a/clidocstool_md_test.go b/clidocstool_md_test.go index 244611e..649248b 100644 --- a/clidocstool_md_test.go +++ b/clidocstool_md_test.go @@ -27,6 +27,7 @@ import ( //nolint:errcheck func TestGenMarkdownTree(t *testing.T) { + setup() tmpdir := t.TempDir() require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) diff --git a/clidocstool_test.go b/clidocstool_test.go index e89de99..16d3eaa 100644 --- a/clidocstool_test.go +++ b/clidocstool_test.go @@ -19,11 +19,14 @@ import ( "os" "path" "path/filepath" + "strconv" "strings" "testing" + "time" "github.com/docker/cli-docs-tool/annotation" "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -37,7 +40,7 @@ var ( ) //nolint:errcheck -func init() { +func setup() { dockerCmd = &cobra.Command{ Use: "docker [OPTIONS] COMMAND [ARG...]", Short: "A self-sufficient runtime for containers", @@ -192,14 +195,25 @@ format: "default|[=|[,]]"`) //nolint:errcheck func TestGenAllTree(t *testing.T) { + setup() tmpdir := t.TempDir() + epoch, err := time.Parse("2006-Jan-02", "2020-Jan-10") + require.NoError(t, err) + t.Setenv("SOURCE_DATE_EPOCH", strconv.FormatInt(epoch.Unix(), 10)) + require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) c, err := New(Options{ Root: buildxCmd, SourceDir: tmpdir, Plugin: true, + ManHeader: &doc.GenManHeader{ + Title: "DOCKER", + Section: "1", + Source: "Docker Community", + Manual: "Docker User Manuals", + }, }) require.NoError(t, err) require.NoError(t, c.GenAllTree()) diff --git a/clidocstool_yaml.go b/clidocstool_yaml.go index 5235242..acf5f2b 100644 --- a/clidocstool_yaml.go +++ b/clidocstool_yaml.go @@ -78,7 +78,7 @@ type cmdDoc struct { // it is undefined which help output will be in the file `cmd-sub-third.1`. func (c *Client) GenYamlTree(cmd *cobra.Command) error { emptyStr := func(s string) string { return "" } - if err := c.loadLongDescription(cmd); err != nil { + if err := c.loadLongDescription(cmd, "yaml"); err != nil { return err } return c.genYamlTreeCustom(cmd, emptyStr) @@ -371,63 +371,6 @@ func hasSeeAlso(cmd *cobra.Command) bool { return false } -// loadLongDescription gets long descriptions and examples from markdown. -func (c *Client) loadLongDescription(parentCmd *cobra.Command) error { - for _, cmd := range parentCmd.Commands() { - if cmd.HasSubCommands() { - if err := c.loadLongDescription(cmd); err != nil { - return err - } - } - name := cmd.CommandPath() - if i := strings.Index(name, " "); i >= 0 { - // remove root command / binary name - name = name[i+1:] - } - if name == "" { - continue - } - mdFile := strings.ReplaceAll(name, " ", "_") + ".md" - sourcePath := filepath.Join(c.source, mdFile) - content, err := os.ReadFile(sourcePath) - if os.IsNotExist(err) { - log.Printf("WARN: %s does not exist, skipping Markdown examples for YAML doc\n", mdFile) - continue - } - if err != nil { - return err - } - applyDescriptionAndExamples(cmd, string(content)) - } - return nil -} - -// applyDescriptionAndExamples fills in cmd.Long and cmd.Example with the -// "Description" and "Examples" H2 sections in mdString (if present). -func applyDescriptionAndExamples(cmd *cobra.Command, mdString string) { - sections := getSections(mdString) - var ( - anchors []string - md string - ) - if sections["description"] != "" { - md, anchors = cleanupMarkDown(sections["description"]) - cmd.Long = md - anchors = append(anchors, md) - } - if sections["examples"] != "" { - md, anchors = cleanupMarkDown(sections["examples"]) - cmd.Example = md - anchors = append(anchors, md) - } - if len(anchors) > 0 { - if cmd.Annotations == nil { - cmd.Annotations = make(map[string]string) - } - cmd.Annotations["anchors"] = strings.Join(anchors, ",") - } -} - type byName []*cobra.Command func (s byName) Len() int { return len(s) } diff --git a/clidocstool_yaml_test.go b/clidocstool_yaml_test.go index 4de872f..308d85f 100644 --- a/clidocstool_yaml_test.go +++ b/clidocstool_yaml_test.go @@ -27,6 +27,7 @@ import ( //nolint:errcheck func TestGenYamlTree(t *testing.T) { + setup() tmpdir := t.TempDir() c, err := New(Options{ diff --git a/example/go.mod b/example/go.mod index cd9561e..5b3d0ad 100644 --- a/example/go.mod +++ b/example/go.mod @@ -41,6 +41,7 @@ require ( github.com/containerd/continuity v0.4.1 // indirect github.com/containerd/ttrpc v1.2.2 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/distribution/distribution/v3 v3.0.0-20230214150026-36d8c594d7aa // indirect @@ -111,6 +112,7 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/secure-systems-lab/go-securesystemslib v0.4.0 // indirect github.com/serialx/hashring v0.0.0-20190422032157-8b2912629002 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect diff --git a/example/go.sum b/example/go.sum index ebf891c..e14ea27 100644 --- a/example/go.sum +++ b/example/go.sum @@ -136,6 +136,7 @@ github.com/containerd/ttrpc v1.2.2 h1:9vqZr0pxwOF5koz6N0N3kJ0zDHokrcPxIR/ZR2YFtO github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak= github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= @@ -437,6 +438,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= diff --git a/example/main.go b/example/main.go index 50caa6d..574476b 100644 --- a/example/main.go +++ b/example/main.go @@ -22,6 +22,7 @@ import ( clidocstool "github.com/docker/cli-docs-tool" "github.com/docker/cli/cli/command" "github.com/spf13/cobra" + "github.com/spf13/cobra/doc" "github.com/spf13/pflag" // import drivers otherwise factories are empty @@ -66,6 +67,12 @@ func gen(opts *options) error { SourceDir: opts.source, TargetDir: opts.target, Plugin: true, + ManHeader: &doc.GenManHeader{ + Title: "BUILDX", + Section: "1", + Source: "Docker Community", + Manual: "Docker User Manuals", + }, }) if err != nil { return err diff --git a/fixtures/docker-buildx-build.1 b/fixtures/docker-buildx-build.1 new file mode 100644 index 0000000..b69b6b5 --- /dev/null +++ b/fixtures/docker-buildx-build.1 @@ -0,0 +1,122 @@ +.nh +.TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" + +.SH NAME +.PP +docker-buildx-build - Start a build + + +.SH SYNOPSIS +.PP +\fBdocker buildx build [OPTIONS] PATH | URL | -\fP + + +.SH DESCRIPTION +.PP +Start a build + + +.SH OPTIONS +.PP +\fB--add-host\fP=[] + Add a custom host-to-IP mapping (format: 'host:ip') + +.PP +\fB--allow\fP=[] + Allow extra privileged entitlement (e.g., "network.host", "security.insecure") + +.PP +\fB--build-arg\fP=[] + Set build-time variables + +.PP +\fB--cache-from\fP=[] + External cache sources (e.g., "user/app:cache", "type=local,src=path/to/dir") + +.PP +\fB--cache-to\fP=[] + Cache export destinations (e.g., "user/app:cache", "type=local,dest=path/to/dir") + +.PP +\fB--cgroup-parent\fP="" + Optional parent cgroup for the container + +.PP +\fB--detach\fP[=true] + Dummy flag that tests boolean flags with true as default + +.PP +\fB-f\fP, \fB--file\fP="" + Name of the Dockerfile (default: "PATH/Dockerfile") + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for build + +.PP +\fB--iidfile\fP="" + Write the image ID to the file + +.PP +\fB--label\fP=[] + Set metadata for an image + +.PP +\fB--load\fP[=false] + Shorthand for "--output=type=docker" + +.PP +\fB--network\fP="default" + Set the networking mode for the "RUN" instructions during build + +.PP +\fB-o\fP, \fB--output\fP=[] + Output destination (format: "type=local,dest=path") + +.PP +\fB--platform\fP=[] + Set target platform for build + +.PP +\fB--push\fP[=false] + Shorthand for "--output=type=registry" + +.PP +\fB-q\fP, \fB--quiet\fP[=false] + Suppress the build output and print image ID on success + +.PP +\fB--secret\fP=[] + Secret file to expose to the build (format: "id=mysecret,src=/local/secret") + +.PP +\fB--shm-size\fP="" + Size of "/dev/shm" + +.PP +\fB--ssh\fP=[] + SSH agent socket or keys to expose to the build +format: "default|[=|[,]]" + +.PP +\fB-t\fP, \fB--tag\fP=[] + Name and optionally a tag (format: "name:tag") + +.PP +\fB--target\fP="" + Set the target build stage to build. + +.PP +\fB--ulimit\fP="" + Ulimit options + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB--builder\fP="" + Override the configured builder instance + + +.SH SEE ALSO +.PP +\fBdocker-buildx(1)\fP diff --git a/fixtures/docker-buildx-install.1 b/fixtures/docker-buildx-install.1 new file mode 100644 index 0000000..0f4a5df --- /dev/null +++ b/fixtures/docker-buildx-install.1 @@ -0,0 +1,33 @@ +.nh +.TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" + +.SH NAME +.PP +docker-buildx-install - Install buildx as a 'docker builder' alias + + +.SH SYNOPSIS +.PP +\fBdocker buildx install\fP + + +.SH DESCRIPTION +.PP +Install buildx as a 'docker builder' alias + + +.SH OPTIONS +.PP +\fB-h\fP, \fB--help\fP[=false] + help for install + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB--builder\fP="" + Override the configured builder instance + + +.SH SEE ALSO +.PP +\fBdocker-buildx(1)\fP diff --git a/fixtures/docker-buildx-stop.1 b/fixtures/docker-buildx-stop.1 new file mode 100644 index 0000000..a790e1f --- /dev/null +++ b/fixtures/docker-buildx-stop.1 @@ -0,0 +1,33 @@ +.nh +.TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" + +.SH NAME +.PP +docker-buildx-stop - Stop builder instance + + +.SH SYNOPSIS +.PP +\fBdocker buildx stop [NAME]\fP + + +.SH DESCRIPTION +.PP +Stop builder instance + + +.SH OPTIONS +.PP +\fB-h\fP, \fB--help\fP[=false] + help for stop + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB--builder\fP="" + Override the configured builder instance + + +.SH SEE ALSO +.PP +\fBdocker-buildx(1)\fP diff --git a/fixtures/docker-buildx.1 b/fixtures/docker-buildx.1 new file mode 100644 index 0000000..51910d3 --- /dev/null +++ b/fixtures/docker-buildx.1 @@ -0,0 +1,31 @@ +.nh +.TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" + +.SH NAME +.PP +docker-buildx - Docker Buildx + + +.SH SYNOPSIS +.PP +\fBdocker buildx\fP + + +.SH DESCRIPTION +.PP +Extended build capabilities with BuildKit + + +.SH OPTIONS +.PP +\fB--builder\fP="" + Override the configured builder instance + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for buildx + + +.SH SEE ALSO +.PP +\fBdocker(1)\fP, \fBdocker-buildx-build(1)\fP, \fBdocker-buildx-stop(1)\fP diff --git a/go.mod b/go.mod index cdaa8f7..220e79b 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,9 @@ require ( ) require ( + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect ) diff --git a/go.sum b/go.sum index 813cf7d..adad808 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -6,6 +7,7 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= diff --git a/man/generate.go b/man/generate.go deleted file mode 100644 index d21a646..0000000 --- a/man/generate.go +++ /dev/null @@ -1,123 +0,0 @@ -package main - -import ( - "fmt" - "io/ioutil" - "log" - "os" - "path/filepath" - "strconv" - "time" - - "github.com/docker/cli/cli/command" - "github.com/docker/cli/cli/command/commands" - "github.com/spf13/cobra" - "github.com/spf13/cobra/doc" - "github.com/spf13/pflag" -) - -const descriptionSourcePath = "man/src/" - -func generateManPages(opts *options) error { - header := &doc.GenManHeader{ - Title: "DOCKER", - Section: "1", - Source: "Docker Community", - Manual: "Docker User Manuals", - } - - // If SOURCE_DATE_EPOCH is set, in order to allow reproducible package - // builds, we explicitly set the build time to SOURCE_DATE_EPOCH. - if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" { - unixEpoch, err := strconv.ParseInt(epoch, 10, 64) - if err != nil { - return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err) - } - now := time.Unix(unixEpoch, 0) - header.Date = &now - } - - dockerCli, err := command.NewDockerCli() - if err != nil { - return err - } - cmd := &cobra.Command{Use: "docker"} - commands.AddCommands(cmd, dockerCli) - source := filepath.Join(opts.source, descriptionSourcePath) - if err := loadLongDescription(cmd, source); err != nil { - return err - } - - cmd.DisableAutoGenTag = true - cmd.DisableFlagsInUseLine = true - return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ - Header: header, - Path: opts.target, - CommandSeparator: "-", - }) -} - -func loadLongDescription(cmd *cobra.Command, path string) error { - for _, cmd := range cmd.Commands() { - cmd.DisableFlagsInUseLine = true - if cmd.Name() == "" { - continue - } - fullpath := filepath.Join(path, cmd.Name()+".md") - - if cmd.HasSubCommands() { - loadLongDescription(cmd, filepath.Join(path, cmd.Name())) - } - - if _, err := os.Stat(fullpath); err != nil { - log.Printf("WARN: %s does not exist, skipping\n", fullpath) - continue - } - - content, err := ioutil.ReadFile(fullpath) - if err != nil { - return err - } - cmd.Long = string(content) - - fullpath = filepath.Join(path, cmd.Name()+"-example.md") - if _, err := os.Stat(fullpath); err != nil { - continue - } - - content, err = ioutil.ReadFile(fullpath) - if err != nil { - return err - } - cmd.Example = string(content) - - } - return nil -} - -type options struct { - source string - target string -} - -func parseArgs() (*options, error) { - opts := &options{} - cwd, _ := os.Getwd() - flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError) - flags.StringVar(&opts.source, "root", cwd, "Path to project root") - flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated man pages") - err := flags.Parse(os.Args[1:]) - return opts, err -} - -func main() { - opts, err := parseArgs() - if err != nil { - fmt.Fprintln(os.Stderr, err.Error()) - } - fmt.Printf("Project root: %s\n", opts.source) - fmt.Printf("Generating man pages into %s\n", opts.target) - if err := generateManPages(opts); err != nil { - fmt.Fprintf(os.Stderr, "Failed to generate man pages: %s\n", err.Error()) - } -} From 4ecc2f24dfd58f92d2351097f32fe4c4dd5b6f46 Mon Sep 17 00:00:00 2001 From: CrazyMax <1951866+crazy-max@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:05:29 +0100 Subject: [PATCH 14/14] test: "attach" and "buildx dial-stdio" cmds for testing Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com> --- clidocstool_man_test.go | 4 +-- clidocstool_md_test.go | 4 +-- clidocstool_test.go | 42 +++++++++++++++++++---- clidocstool_yaml_test.go | 4 +-- fixtures/attach.md | 20 +++++++++++ fixtures/buildx.md | 9 ++--- fixtures/buildx_dial-stdio.md | 16 +++++++++ fixtures/docker-attach.1 | 39 ++++++++++++++++++++++ fixtures/docker-buildx-dial-stdio.1 | 41 +++++++++++++++++++++++ fixtures/docker-buildx.1 | 2 +- fixtures/docker_attach.yaml | 46 ++++++++++++++++++++++++++ fixtures/docker_buildx.yaml | 2 ++ fixtures/docker_buildx_dial-stdio.yaml | 43 ++++++++++++++++++++++++ 13 files changed, 255 insertions(+), 17 deletions(-) create mode 100644 fixtures/attach.md create mode 100644 fixtures/buildx_dial-stdio.md create mode 100644 fixtures/docker-attach.1 create mode 100644 fixtures/docker-buildx-dial-stdio.1 create mode 100644 fixtures/docker_attach.yaml create mode 100644 fixtures/docker_buildx_dial-stdio.yaml diff --git a/clidocstool_man_test.go b/clidocstool_man_test.go index 7c58860..336edf7 100644 --- a/clidocstool_man_test.go +++ b/clidocstool_man_test.go @@ -41,7 +41,7 @@ func TestGenManTree(t *testing.T) { require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) c, err := New(Options{ - Root: buildxCmd, + Root: dockerCmd, SourceDir: tmpdir, Plugin: true, ManHeader: &doc.GenManHeader{ @@ -52,7 +52,7 @@ func TestGenManTree(t *testing.T) { }, }) require.NoError(t, err) - require.NoError(t, c.GenManTree(buildxCmd)) + require.NoError(t, c.GenManTree(dockerCmd)) seen := make(map[string]struct{}) remanpage := regexp.MustCompile(`\.\d+$`) diff --git a/clidocstool_md_test.go b/clidocstool_md_test.go index 649248b..0900b51 100644 --- a/clidocstool_md_test.go +++ b/clidocstool_md_test.go @@ -33,12 +33,12 @@ func TestGenMarkdownTree(t *testing.T) { require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) c, err := New(Options{ - Root: buildxCmd, + Root: dockerCmd, SourceDir: tmpdir, Plugin: true, }) require.NoError(t, err) - require.NoError(t, c.GenMarkdownTree(buildxCmd)) + require.NoError(t, c.GenMarkdownTree(dockerCmd)) seen := make(map[string]struct{}) diff --git a/clidocstool_test.go b/clidocstool_test.go index 16d3eaa..0fece8e 100644 --- a/clidocstool_test.go +++ b/clidocstool_test.go @@ -32,11 +32,13 @@ import ( ) var ( - dockerCmd *cobra.Command - buildxCmd *cobra.Command - buildxBuildCmd *cobra.Command - buildxInstallCmd *cobra.Command - buildxStopCmd *cobra.Command + dockerCmd *cobra.Command + attachCmd *cobra.Command + buildxCmd *cobra.Command + buildxBuildCmd *cobra.Command + buildxDialStdioCmd *cobra.Command + buildxInstallCmd *cobra.Command + buildxStopCmd *cobra.Command ) //nolint:errcheck @@ -51,6 +53,22 @@ func setup() { Version: "20.10.8", DisableFlagsInUseLine: true, } + + attachCmd = &cobra.Command{ + Use: "attach [OPTIONS] CONTAINER", + Short: "Attach local standard input, output, and error streams to a running container", + Annotations: map[string]string{ + "aliases": "docker container attach, docker attach", + }, + Run: func(cmd *cobra.Command, args []string) {}, + } + + attachFlags := attachCmd.Flags() + attachFlags.Bool("no-stdin", false, "Do not attach STDIN") + attachFlags.Bool("sig-proxy", true, "Proxy all received signals to the process") + attachFlags.String("detach-keys", "", "Override the key sequence for detaching a container") + dockerCmd.AddCommand(attachCmd) + buildxCmd = &cobra.Command{ Use: "buildx", Short: "Docker Buildx", @@ -68,6 +86,12 @@ func setup() { "aliases": "docker image build, docker buildx build, docker buildx b, docker build", }, } + buildxDialStdioCmd = &cobra.Command{ + Use: "dial-stdio", + Short: "Proxy current stdio streams to builder instance", + Args: cobra.NoArgs, + Run: func(cmd *cobra.Command, args []string) {}, + } buildxInstallCmd = &cobra.Command{ Use: "install", Short: "Install buildx as a 'docker builder' alias", @@ -187,7 +211,13 @@ format: "default|[=|[,]]"`) buildxBuildFlags.BoolVar(&ignoreBool, "force-rm", false, "Always remove intermediate containers") buildxBuildFlags.MarkHidden("force-rm") + buildxDialStdioFlags := buildxDialStdioCmd.Flags() + + buildxDialStdioFlags.String("platform", os.Getenv("DOCKER_DEFAULT_PLATFORM"), "Target platform: this is used for node selection") + buildxDialStdioFlags.String("progress", "quiet", "Set type of progress output (auto, plain, tty).") + buildxCmd.AddCommand(buildxBuildCmd) + buildxCmd.AddCommand(buildxDialStdioCmd) buildxCmd.AddCommand(buildxInstallCmd) buildxCmd.AddCommand(buildxStopCmd) dockerCmd.AddCommand(buildxCmd) @@ -205,7 +235,7 @@ func TestGenAllTree(t *testing.T) { require.NoError(t, copyFile(path.Join("fixtures", "buildx_stop.pre.md"), path.Join(tmpdir, "buildx_stop.md"))) c, err := New(Options{ - Root: buildxCmd, + Root: dockerCmd, SourceDir: tmpdir, Plugin: true, ManHeader: &doc.GenManHeader{ diff --git a/clidocstool_yaml_test.go b/clidocstool_yaml_test.go index 308d85f..5554312 100644 --- a/clidocstool_yaml_test.go +++ b/clidocstool_yaml_test.go @@ -31,12 +31,12 @@ func TestGenYamlTree(t *testing.T) { tmpdir := t.TempDir() c, err := New(Options{ - Root: buildxCmd, + Root: dockerCmd, SourceDir: tmpdir, Plugin: true, }) require.NoError(t, err) - require.NoError(t, c.GenYamlTree(buildxCmd)) + require.NoError(t, c.GenYamlTree(dockerCmd)) seen := make(map[string]struct{}) diff --git a/fixtures/attach.md b/fixtures/attach.md new file mode 100644 index 0000000..09ceeea --- /dev/null +++ b/fixtures/attach.md @@ -0,0 +1,20 @@ +# docker attach + + +Attach local standard input, output, and error streams to a running container + +### Aliases + +`docker container attach`, `docker attach` + +### Options + +| Name | Type | Default | Description | +|:----------------|:---------|:--------|:----------------------------------------------------| +| `--detach-keys` | `string` | | Override the key sequence for detaching a container | +| `--no-stdin` | `bool` | | Do not attach STDIN | +| `--sig-proxy` | `bool` | `true` | Proxy all received signals to the process | + + + + diff --git a/fixtures/buildx.md b/fixtures/buildx.md index 607fec9..548a7e8 100644 --- a/fixtures/buildx.md +++ b/fixtures/buildx.md @@ -5,10 +5,11 @@ Extended build capabilities with BuildKit ### Subcommands -| Name | Description | -|:---------------------------|:----------------------| -| [`build`](buildx_build.md) | Start a build | -| [`stop`](buildx_stop.md) | Stop builder instance | +| Name | Description | +|:-------------------------------------|:------------------------------------------------| +| [`build`](buildx_build.md) | Start a build | +| [`dial-stdio`](buildx_dial-stdio.md) | Proxy current stdio streams to builder instance | +| [`stop`](buildx_stop.md) | Stop builder instance | ### Options diff --git a/fixtures/buildx_dial-stdio.md b/fixtures/buildx_dial-stdio.md new file mode 100644 index 0000000..2930248 --- /dev/null +++ b/fixtures/buildx_dial-stdio.md @@ -0,0 +1,16 @@ +# docker buildx dial-stdio + + +Proxy current stdio streams to builder instance + +### Options + +| Name | Type | Default | Description | +|:-------------|:---------|:--------|:-------------------------------------------------| +| `--builder` | `string` | | Override the configured builder instance | +| `--platform` | `string` | | Target platform: this is used for node selection | +| `--progress` | `string` | `quiet` | Set type of progress output (auto, plain, tty). | + + + + diff --git a/fixtures/docker-attach.1 b/fixtures/docker-attach.1 new file mode 100644 index 0000000..2cac572 --- /dev/null +++ b/fixtures/docker-attach.1 @@ -0,0 +1,39 @@ +.nh +.TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" + +.SH NAME +.PP +docker-attach - Attach local standard input, output, and error streams to a running container + + +.SH SYNOPSIS +.PP +\fBdocker attach [OPTIONS] CONTAINER\fP + + +.SH DESCRIPTION +.PP +Attach local standard input, output, and error streams to a running container + + +.SH OPTIONS +.PP +\fB--detach-keys\fP="" + Override the key sequence for detaching a container + +.PP +\fB-h\fP, \fB--help\fP[=false] + help for attach + +.PP +\fB--no-stdin\fP[=false] + Do not attach STDIN + +.PP +\fB--sig-proxy\fP[=true] + Proxy all received signals to the process + + +.SH SEE ALSO +.PP +\fBdocker(1)\fP diff --git a/fixtures/docker-buildx-dial-stdio.1 b/fixtures/docker-buildx-dial-stdio.1 new file mode 100644 index 0000000..5c28476 --- /dev/null +++ b/fixtures/docker-buildx-dial-stdio.1 @@ -0,0 +1,41 @@ +.nh +.TH "DOCKER" "1" "Jan 2020" "Docker Community" "Docker User Manuals" + +.SH NAME +.PP +docker-buildx-dial-stdio - Proxy current stdio streams to builder instance + + +.SH SYNOPSIS +.PP +\fBdocker buildx dial-stdio\fP + + +.SH DESCRIPTION +.PP +Proxy current stdio streams to builder instance + + +.SH OPTIONS +.PP +\fB-h\fP, \fB--help\fP[=false] + help for dial-stdio + +.PP +\fB--platform\fP="" + Target platform: this is used for node selection + +.PP +\fB--progress\fP="quiet" + Set type of progress output (auto, plain, tty). + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB--builder\fP="" + Override the configured builder instance + + +.SH SEE ALSO +.PP +\fBdocker-buildx(1)\fP diff --git a/fixtures/docker-buildx.1 b/fixtures/docker-buildx.1 index 51910d3..f7f4742 100644 --- a/fixtures/docker-buildx.1 +++ b/fixtures/docker-buildx.1 @@ -28,4 +28,4 @@ Extended build capabilities with BuildKit .SH SEE ALSO .PP -\fBdocker(1)\fP, \fBdocker-buildx-build(1)\fP, \fBdocker-buildx-stop(1)\fP +\fBdocker(1)\fP, \fBdocker-buildx-build(1)\fP, \fBdocker-buildx-dial-stdio(1)\fP, \fBdocker-buildx-stop(1)\fP diff --git a/fixtures/docker_attach.yaml b/fixtures/docker_attach.yaml new file mode 100644 index 0000000..e950296 --- /dev/null +++ b/fixtures/docker_attach.yaml @@ -0,0 +1,46 @@ +command: docker attach +aliases: docker container attach, docker attach +short: | + Attach local standard input, output, and error streams to a running container +long: | + Attach local standard input, output, and error streams to a running container +usage: docker attach [OPTIONS] CONTAINER +pname: docker +plink: docker.yaml +options: + - option: detach-keys + value_type: string + description: Override the key sequence for detaching a container + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false + - option: no-stdin + value_type: bool + default_value: "false" + description: Do not attach STDIN + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false + - option: sig-proxy + value_type: bool + default_value: "true" + description: Proxy all received signals to the process + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false +deprecated: false +hidden: false +experimental: false +experimentalcli: false +kubernetes: false +swarm: false + diff --git a/fixtures/docker_buildx.yaml b/fixtures/docker_buildx.yaml index c8b5cb5..93be880 100644 --- a/fixtures/docker_buildx.yaml +++ b/fixtures/docker_buildx.yaml @@ -5,9 +5,11 @@ pname: docker plink: docker.yaml cname: - docker buildx build + - docker buildx dial-stdio - docker buildx stop clink: - docker_buildx_build.yaml + - docker_buildx_dial-stdio.yaml - docker_buildx_stop.yaml options: - option: builder diff --git a/fixtures/docker_buildx_dial-stdio.yaml b/fixtures/docker_buildx_dial-stdio.yaml new file mode 100644 index 0000000..d9bc7e3 --- /dev/null +++ b/fixtures/docker_buildx_dial-stdio.yaml @@ -0,0 +1,43 @@ +command: docker buildx dial-stdio +short: Proxy current stdio streams to builder instance +long: Proxy current stdio streams to builder instance +usage: docker buildx dial-stdio +pname: docker buildx +plink: docker_buildx.yaml +options: + - option: platform + value_type: string + description: 'Target platform: this is used for node selection' + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false + - option: progress + value_type: string + default_value: quiet + description: Set type of progress output (auto, plain, tty). + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false +inherited_options: + - option: builder + value_type: string + description: Override the configured builder instance + deprecated: false + hidden: false + experimental: false + experimentalcli: false + kubernetes: false + swarm: false +deprecated: false +hidden: false +experimental: false +experimentalcli: false +kubernetes: false +swarm: false +