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  }