github.com/justincormack/cli@v0.0.0-20201215022714-831ebeae9675/man/generate.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "log" 7 "os" 8 "path/filepath" 9 "strconv" 10 "time" 11 12 "github.com/docker/cli/cli/command" 13 "github.com/docker/cli/cli/command/commands" 14 "github.com/spf13/cobra" 15 "github.com/spf13/cobra/doc" 16 "github.com/spf13/pflag" 17 ) 18 19 const descriptionSourcePath = "man/src/" 20 21 func generateManPages(opts *options) error { 22 header := &doc.GenManHeader{ 23 Title: "DOCKER", 24 Section: "1", 25 Source: "Docker Community", 26 Manual: "Docker User Manuals", 27 } 28 29 // If SOURCE_DATE_EPOCH is set, in order to allow reproducible package 30 // builds, we explicitly set the build time to SOURCE_DATE_EPOCH. 31 if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" { 32 unixEpoch, err := strconv.ParseInt(epoch, 10, 64) 33 if err != nil { 34 return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err) 35 } 36 now := time.Unix(unixEpoch, 0) 37 header.Date = &now 38 } 39 40 dockerCli, err := command.NewDockerCli() 41 if err != nil { 42 return err 43 } 44 cmd := &cobra.Command{Use: "docker"} 45 commands.AddCommands(cmd, dockerCli) 46 source := filepath.Join(opts.source, descriptionSourcePath) 47 if err := loadLongDescription(cmd, source); err != nil { 48 return err 49 } 50 51 cmd.DisableAutoGenTag = true 52 cmd.DisableFlagsInUseLine = true 53 return doc.GenManTreeFromOpts(cmd, doc.GenManTreeOptions{ 54 Header: header, 55 Path: opts.target, 56 CommandSeparator: "-", 57 }) 58 } 59 60 func loadLongDescription(cmd *cobra.Command, path string) error { 61 for _, cmd := range cmd.Commands() { 62 cmd.DisableFlagsInUseLine = true 63 if cmd.Name() == "" { 64 continue 65 } 66 fullpath := filepath.Join(path, cmd.Name()+".md") 67 68 if cmd.HasSubCommands() { 69 loadLongDescription(cmd, filepath.Join(path, cmd.Name())) 70 } 71 72 if _, err := os.Stat(fullpath); err != nil { 73 log.Printf("WARN: %s does not exist, skipping\n", fullpath) 74 continue 75 } 76 77 content, err := ioutil.ReadFile(fullpath) 78 if err != nil { 79 return err 80 } 81 cmd.Long = string(content) 82 83 fullpath = filepath.Join(path, cmd.Name()+"-example.md") 84 if _, err := os.Stat(fullpath); err != nil { 85 continue 86 } 87 88 content, err = ioutil.ReadFile(fullpath) 89 if err != nil { 90 return err 91 } 92 cmd.Example = string(content) 93 94 } 95 return nil 96 } 97 98 type options struct { 99 source string 100 target string 101 } 102 103 func parseArgs() (*options, error) { 104 opts := &options{} 105 cwd, _ := os.Getwd() 106 flags := pflag.NewFlagSet(os.Args[0], pflag.ContinueOnError) 107 flags.StringVar(&opts.source, "root", cwd, "Path to project root") 108 flags.StringVar(&opts.target, "target", "/tmp", "Target path for generated man pages") 109 err := flags.Parse(os.Args[1:]) 110 return opts, err 111 } 112 113 func main() { 114 opts, err := parseArgs() 115 if err != nil { 116 fmt.Fprintln(os.Stderr, err.Error()) 117 } 118 fmt.Printf("Project root: %s\n", opts.source) 119 fmt.Printf("Generating man pages into %s\n", opts.target) 120 if err := generateManPages(opts); err != nil { 121 fmt.Fprintf(os.Stderr, "Failed to generate man pages: %s\n", err.Error()) 122 } 123 }