github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/cli/docs/yaml/generate.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/docker/cli/cli/command" 11 "github.com/docker/cli/cli/command/commands" 12 "github.com/spf13/cobra" 13 "github.com/spf13/pflag" 14 ) 15 16 const descriptionSourcePath = "docs/reference/commandline/" 17 18 func generateCliYaml(opts *options) error { 19 dockerCli, err := command.NewDockerCli() 20 if err != nil { 21 return err 22 } 23 cmd := &cobra.Command{ 24 Use: "docker [OPTIONS] COMMAND [ARG...]", 25 Short: "The base command for the Docker CLI.", 26 } 27 commands.AddCommands(cmd, dockerCli) 28 disableFlagsInUseLine(cmd) 29 source := filepath.Join(opts.source, descriptionSourcePath) 30 fmt.Println("Markdown source:", source) 31 if err := loadLongDescription(cmd, source); err != nil { 32 return err 33 } 34 35 cmd.DisableAutoGenTag = true 36 return GenYamlTree(cmd, opts.target) 37 } 38 39 func disableFlagsInUseLine(cmd *cobra.Command) { 40 visitAll(cmd, func(ccmd *cobra.Command) { 41 // do not add a `[flags]` to the end of the usage line. 42 ccmd.DisableFlagsInUseLine = true 43 }) 44 } 45 46 // visitAll will traverse all commands from the root. 47 // This is different from the VisitAll of cobra.Command where only parents 48 // are checked. 49 func visitAll(root *cobra.Command, fn func(*cobra.Command)) { 50 for _, cmd := range root.Commands() { 51 visitAll(cmd, fn) 52 } 53 fn(root) 54 } 55 56 func loadLongDescription(parentCmd *cobra.Command, path string) error { 57 for _, cmd := range parentCmd.Commands() { 58 if cmd.HasSubCommands() { 59 if err := loadLongDescription(cmd, path); err != nil { 60 return err 61 } 62 } 63 name := cmd.CommandPath() 64 log.Println("INFO: Generating docs for", name) 65 if i := strings.Index(name, " "); i >= 0 { 66 // remove root command / binary name 67 name = name[i+1:] 68 } 69 if name == "" { 70 continue 71 } 72 mdFile := strings.ReplaceAll(name, " ", "_") + ".md" 73 fullPath := filepath.Join(path, mdFile) 74 content, err := os.ReadFile(fullPath) 75 if os.IsNotExist(err) { 76 log.Printf("WARN: %s does not exist, skipping\n", mdFile) 77 continue 78 } 79 if err != nil { 80 return err 81 } 82 description, examples := parseMDContent(string(content)) 83 cmd.Long = description 84 cmd.Example = examples 85 } 86 return nil 87 } 88 89 type options struct { 90 source string 91 target string 92 } 93 94 func parseArgs() (*options, error) { 95 opts := &options{} 96 cwd, _ := os.Getwd() 97 flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError) 98 flags.StringVar(&opts.source, "root", cwd, "Path to project root") 99 flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated yaml files") 100 err := flags.Parse(os.Args[1:]) 101 return opts, err 102 } 103 104 func main() { 105 opts, err := parseArgs() 106 if err != nil { 107 log.Println(err) 108 } 109 fmt.Println("Project root: ", opts.source) 110 fmt.Println("YAML output dir:", opts.target) 111 if err := generateCliYaml(opts); err != nil { 112 log.Println("Failed to generate yaml files:", err) 113 } 114 }