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  }