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  }