
This removes indirect dependencies from go.mod, and includes the fix for the missing -help flag info. The latest cobra release is v1.2.1. Given that there were minor security-related dependency cleanup during v1.2 release, I was tempted to pick up the latest version, but that caused dependency updates in golang.org/x/sys and golang.org/x/tools which may be too recent (golang.org/x/* follow the go's release support policy, so recent versions may not be compatible with go versions beyond go's official version support policy). Verified that dlv still builds with go1.12.x. (go1.12 is the oldest version of go that can build the latest delve already). $ go get -d github.com/spf13/cobra@v1.1.3 $ go mod tidy $ go mod vendor $ go run _scripts/gen-usage-docs.go
246 lines
7.2 KiB
Go
246 lines
7.2 KiB
Go
// Copyright 2015 Red Hat Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package doc
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/cpuguy83/go-md2man/v2/md2man"
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/pflag"
|
|
)
|
|
|
|
// GenManTree will generate a man page for this command and all descendants
|
|
// in the directory given. The header may be nil. This function may not work
|
|
// correctly if your command names have `-` in them. If you have `cmd` with two
|
|
// subcmds, `sub` and `sub-third`, and `sub` has a subcommand called `third`
|
|
// it is undefined which help output will be in the file `cmd-sub-third.1`.
|
|
func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error {
|
|
return GenManTreeFromOpts(cmd, GenManTreeOptions{
|
|
Header: header,
|
|
Path: dir,
|
|
CommandSeparator: "-",
|
|
})
|
|
}
|
|
|
|
// GenManTreeFromOpts generates a man page for the command and all descendants.
|
|
// The pages are written to the opts.Path directory.
|
|
func GenManTreeFromOpts(cmd *cobra.Command, opts GenManTreeOptions) error {
|
|
header := opts.Header
|
|
if header == nil {
|
|
header = &GenManHeader{}
|
|
}
|
|
for _, c := range cmd.Commands() {
|
|
if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
|
|
continue
|
|
}
|
|
if err := GenManTreeFromOpts(c, opts); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
section := "1"
|
|
if header.Section != "" {
|
|
section = header.Section
|
|
}
|
|
|
|
separator := "_"
|
|
if opts.CommandSeparator != "" {
|
|
separator = opts.CommandSeparator
|
|
}
|
|
basename := strings.Replace(cmd.CommandPath(), " ", separator, -1)
|
|
filename := filepath.Join(opts.Path, basename+"."+section)
|
|
f, err := os.Create(filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
headerCopy := *header
|
|
return GenMan(cmd, &headerCopy, f)
|
|
}
|
|
|
|
// GenManTreeOptions is the options for generating the man pages.
|
|
// Used only in GenManTreeFromOpts.
|
|
type GenManTreeOptions struct {
|
|
Header *GenManHeader
|
|
Path string
|
|
CommandSeparator string
|
|
}
|
|
|
|
// GenManHeader is a lot like the .TH header at the start of man pages. These
|
|
// include the title, section, date, source, and manual. We will use the
|
|
// current time if Date is unset and will use "Auto generated by spf13/cobra"
|
|
// if the Source is unset.
|
|
type GenManHeader struct {
|
|
Title string
|
|
Section string
|
|
Date *time.Time
|
|
date string
|
|
Source string
|
|
Manual string
|
|
}
|
|
|
|
// GenMan will generate a man page for the given command and write it to
|
|
// w. The header argument may be nil, however obviously w may not.
|
|
func GenMan(cmd *cobra.Command, header *GenManHeader, w io.Writer) error {
|
|
if header == nil {
|
|
header = &GenManHeader{}
|
|
}
|
|
if err := fillHeader(header, cmd.CommandPath(), cmd.DisableAutoGenTag); err != nil {
|
|
return err
|
|
}
|
|
|
|
b := genMan(cmd, header)
|
|
_, err := w.Write(md2man.Render(b))
|
|
return err
|
|
}
|
|
|
|
func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error {
|
|
if header.Title == "" {
|
|
header.Title = strings.ToUpper(strings.Replace(name, " ", "\\-", -1))
|
|
}
|
|
if header.Section == "" {
|
|
header.Section = "1"
|
|
}
|
|
if header.Date == nil {
|
|
now := time.Now()
|
|
if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" {
|
|
unixEpoch, err := strconv.ParseInt(epoch, 10, 64)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid SOURCE_DATE_EPOCH: %v", err)
|
|
}
|
|
now = time.Unix(unixEpoch, 0)
|
|
}
|
|
header.Date = &now
|
|
}
|
|
header.date = (*header.Date).Format("Jan 2006")
|
|
if header.Source == "" && !disableAutoGen {
|
|
header.Source = "Auto generated by spf13/cobra"
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command, dashedName string) {
|
|
description := cmd.Long
|
|
if len(description) == 0 {
|
|
description = cmd.Short
|
|
}
|
|
|
|
cobra.WriteStringAndCheck(buf, fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s"
|
|
# NAME
|
|
`, header.Title, header.Section, header.date, header.Source, header.Manual))
|
|
cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
|
|
cobra.WriteStringAndCheck(buf, "# SYNOPSIS\n")
|
|
cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
|
|
cobra.WriteStringAndCheck(buf, "# DESCRIPTION\n")
|
|
cobra.WriteStringAndCheck(buf, description+"\n\n")
|
|
}
|
|
|
|
func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) {
|
|
flags.VisitAll(func(flag *pflag.Flag) {
|
|
if len(flag.Deprecated) > 0 || flag.Hidden {
|
|
return
|
|
}
|
|
format := ""
|
|
if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 {
|
|
format = fmt.Sprintf("**-%s**, **--%s**", flag.Shorthand, flag.Name)
|
|
} else {
|
|
format = fmt.Sprintf("**--%s**", flag.Name)
|
|
}
|
|
if len(flag.NoOptDefVal) > 0 {
|
|
format += "["
|
|
}
|
|
if flag.Value.Type() == "string" {
|
|
// put quotes on the value
|
|
format += "=%q"
|
|
} else {
|
|
format += "=%s"
|
|
}
|
|
if len(flag.NoOptDefVal) > 0 {
|
|
format += "]"
|
|
}
|
|
format += "\n\t%s\n\n"
|
|
cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, flag.Usage))
|
|
})
|
|
}
|
|
|
|
func manPrintOptions(buf io.StringWriter, command *cobra.Command) {
|
|
flags := command.NonInheritedFlags()
|
|
if flags.HasAvailableFlags() {
|
|
cobra.WriteStringAndCheck(buf, "# OPTIONS\n")
|
|
manPrintFlags(buf, flags)
|
|
cobra.WriteStringAndCheck(buf, "\n")
|
|
}
|
|
flags = command.InheritedFlags()
|
|
if flags.HasAvailableFlags() {
|
|
cobra.WriteStringAndCheck(buf, "# OPTIONS INHERITED FROM PARENT COMMANDS\n")
|
|
manPrintFlags(buf, flags)
|
|
cobra.WriteStringAndCheck(buf, "\n")
|
|
}
|
|
}
|
|
|
|
func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
|
|
cmd.InitDefaultHelpCmd()
|
|
cmd.InitDefaultHelpFlag()
|
|
|
|
// something like `rootcmd-subcmd1-subcmd2`
|
|
dashCommandName := strings.Replace(cmd.CommandPath(), " ", "-", -1)
|
|
|
|
buf := new(bytes.Buffer)
|
|
|
|
manPreamble(buf, header, cmd, dashCommandName)
|
|
manPrintOptions(buf, cmd)
|
|
if len(cmd.Example) > 0 {
|
|
buf.WriteString("# EXAMPLE\n")
|
|
buf.WriteString(fmt.Sprintf("```\n%s\n```\n", cmd.Example))
|
|
}
|
|
if hasSeeAlso(cmd) {
|
|
buf.WriteString("# SEE ALSO\n")
|
|
seealsos := make([]string, 0)
|
|
if cmd.HasParent() {
|
|
parentPath := cmd.Parent().CommandPath()
|
|
dashParentPath := strings.Replace(parentPath, " ", "-", -1)
|
|
seealso := fmt.Sprintf("**%s(%s)**", dashParentPath, header.Section)
|
|
seealsos = append(seealsos, seealso)
|
|
cmd.VisitParents(func(c *cobra.Command) {
|
|
if c.DisableAutoGenTag {
|
|
cmd.DisableAutoGenTag = c.DisableAutoGenTag
|
|
}
|
|
})
|
|
}
|
|
children := cmd.Commands()
|
|
sort.Sort(byName(children))
|
|
for _, c := range children {
|
|
if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
|
|
continue
|
|
}
|
|
seealso := fmt.Sprintf("**%s-%s(%s)**", dashCommandName, c.Name(), header.Section)
|
|
seealsos = append(seealsos, seealso)
|
|
}
|
|
buf.WriteString(strings.Join(seealsos, ", ") + "\n")
|
|
}
|
|
if !cmd.DisableAutoGenTag {
|
|
buf.WriteString(fmt.Sprintf("# HISTORY\n%s Auto generated by spf13/cobra\n", header.Date.Format("2-Jan-2006")))
|
|
}
|
|
return buf.Bytes()
|
|
}
|