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