github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/cli/man/generate.go (about)

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