Skip to content

Commit

Permalink
Merge pull request cli#4939 from cli/man-refresh
Browse files Browse the repository at this point in the history
Refresh man pages
  • Loading branch information
mislav authored Dec 20, 2021
2 parents 17291d1 + 31c7181 commit 9f2639e
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 134 deletions.
2 changes: 1 addition & 1 deletion .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ release:
before:
hooks:
- go mod tidy
- make manpages
- make manpages GH_VERSION={{.Version}}

builds:
- <<: &build_defaults
Expand Down
8 changes: 1 addition & 7 deletions cmd/gen-docs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,7 @@ func run(args []string) error {
}

if *manPage {
header := &docs.GenManHeader{
Title: "gh",
Section: "1",
Source: "",
Manual: "",
}
if err := docs.GenManTree(rootCmd, header, *dir); err != nil {
if err := docs.GenManTree(rootCmd, *dir); err != nil {
return err
}
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/gen-docs/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func Test_run(t *testing.T) {
if err != nil {
t.Fatalf("error reading `gh-issue-create.1`: %v", err)
}
if !strings.Contains(string(manPage), `\fBgh issue create`) {
if !strings.Contains(string(manPage), `\fB\fCgh issue create`) {
t.Fatal("man page corrupted")
}

Expand Down
2 changes: 2 additions & 0 deletions internal/docs/docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,14 @@ var dummyCmd = &cobra.Command{
}

func checkStringContains(t *testing.T, got, expected string) {
t.Helper()
if !strings.Contains(got, expected) {
t.Errorf("Expected to contain: \n %v\nGot:\n %v\n", expected, got)
}
}

func checkStringOmits(t *testing.T, got, expected string) {
t.Helper()
if strings.Contains(got, expected) {
t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got)
}
Expand Down
129 changes: 41 additions & 88 deletions internal/docs/man.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"time"
Expand All @@ -21,9 +20,8 @@ import (
// 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 {
func GenManTree(cmd *cobra.Command, dir string) error {
return GenManTreeFromOpts(cmd, GenManTreeOptions{
Header: header,
Path: dir,
CommandSeparator: "-",
})
Expand All @@ -32,10 +30,6 @@ func GenManTree(cmd *cobra.Command, header *GenManHeader, dir string) error {
// 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
Expand All @@ -44,11 +38,8 @@ func GenManTreeFromOpts(cmd *cobra.Command, opts GenManTreeOptions) error {
return err
}
}
section := "1"
if header.Section != "" {
section = header.Section
}

section := "1"
separator := "_"
if opts.CommandSeparator != "" {
separator = opts.CommandSeparator
Expand All @@ -61,14 +52,21 @@ func GenManTreeFromOpts(cmd *cobra.Command, opts GenManTreeOptions) error {
}
defer f.Close()

headerCopy := *header
return GenMan(cmd, &headerCopy, f)
var versionString string
if v := os.Getenv("GH_VERSION"); v != "" {
versionString = "GitHub CLI " + v
}

return GenMan(cmd, &GenManHeader{
Section: section,
Source: versionString,
Manual: "GitHub CLI manual",
}, f)
}

// GenManTreeOptions is the options for generating the man pages.
// Used only in GenManTreeFromOpts.
type GenManTreeOptions struct {
Header *GenManHeader
Path string
CommandSeparator string
}
Expand All @@ -80,17 +78,13 @@ 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()); err != nil {
return err
}
Expand Down Expand Up @@ -119,57 +113,40 @@ func fillHeader(header *GenManHeader, name string) error {
}
header.Date = &now
}
header.date = (*header.Date).Format("Jan 2006")
return nil
}

func manPreamble(buf *bytes.Buffer, header *GenManHeader, cmd *cobra.Command, dashedName string) {
description := cmd.Long
if len(description) == 0 {
description = cmd.Short
}

buf.WriteString(fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s"
# NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual))
`, header.Title, header.Section, header.Date.Format("Jan 2006"), header.Source, header.Manual))
buf.WriteString(fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
buf.WriteString("# SYNOPSIS\n")
buf.WriteString(fmt.Sprintf("`%s`\n\n", cmd.UseLine()))

// "<>" is rendered as HTML in md
synopsis := cmd.UseLine()
escAngle := strings.Replace(synopsis, "<", "\\<", -1)
escAngle = strings.Replace(escAngle, ">", "\\>", -1)
buf.WriteString(fmt.Sprintf("**%s**\n\n", escAngle))

buf.WriteString("# DESCRIPTION\n")
buf.WriteString(description + "\n\n")
if cmd.Long != "" && cmd.Long != cmd.Short {
buf.WriteString("# DESCRIPTION\n")
buf.WriteString(cmd.Long + "\n\n")
}
}

func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
flags.VisitAll(func(flag *pflag.Flag) {
if len(flag.Deprecated) > 0 || flag.Hidden {
if len(flag.Deprecated) > 0 || flag.Hidden || flag.Name == "help" {
return
}
format := ""
varname, usage := pflag.UnquoteUsage(flag)
if len(flag.Shorthand) > 0 && len(flag.ShorthandDeprecated) == 0 {
format = fmt.Sprintf("**-%s**, **--%s**", flag.Shorthand, flag.Name)
buf.WriteString(fmt.Sprintf("`-%s`, `--%s`", flag.Shorthand, flag.Name))
} else {
format = fmt.Sprintf("**--%s**", flag.Name)
}
if len(flag.NoOptDefVal) > 0 {
format += "["
buf.WriteString(fmt.Sprintf("`--%s`", flag.Name))
}
if flag.Value.Type() == "string" {
// put quotes on the value
format += "=%q"
if varname == "" {
buf.WriteString("\n")
} else {
format += "=%s"
buf.WriteString(fmt.Sprintf(" `<%s>`\n", varname))
}
if len(flag.NoOptDefVal) > 0 {
format += "]"
}
format += "\n\t%s\n\n"
buf.WriteString(fmt.Sprintf(format, flag.DefValue, flag.Usage))
buf.WriteString(fmt.Sprintf(": %s\n\n", usage))
})
}

Expand All @@ -181,7 +158,7 @@ func manPrintOptions(buf *bytes.Buffer, command *cobra.Command) {
buf.WriteString("\n")
}
flags = command.InheritedFlags()
if flags.HasAvailableFlags() {
if hasNonHelpFlags(flags) {
buf.WriteString("# OPTIONS INHERITED FROM PARENT COMMANDS\n")
manPrintFlags(buf, flags)
buf.WriteString("\n")
Expand All @@ -198,52 +175,28 @@ func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
buf := new(bytes.Buffer)

manPreamble(buf, header, cmd, dashCommandName)
for _, g := range subcommandGroups(cmd) {
if len(g.Commands) == 0 {
continue
}
fmt.Fprintf(buf, "# %s\n", strings.ToUpper(g.Name))
for _, subcmd := range g.Commands {
fmt.Fprintf(buf, "`%s`\n: %s\n\n", manLink(subcmd), subcmd.Short)
}
}
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) {
if cmd.HasParent() {
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)
}
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")
buf.WriteString(fmt.Sprintf("`%s`\n", manLink(cmd.Parent())))
}
return buf.Bytes()
}

// Test to see if we have a reason to print See Also information in docs
// Basically this is a test for a parent command or a subcommand which is
// both not deprecated and not the autogenerated help command.
func hasSeeAlso(cmd *cobra.Command) bool {
if cmd.HasParent() {
return true
}
for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue
}
return true
}
return false
func manLink(cmd *cobra.Command) string {
p := cmd.CommandPath()
return fmt.Sprintf("%s(%d)", strings.Replace(p, " ", "-", -1), 1)
}

type byName []*cobra.Command

func (s byName) Len() int { return len(s) }
func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
46 changes: 9 additions & 37 deletions internal/docs/man_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func translate(in string) string {
func TestGenManDoc(t *testing.T) {
header := &GenManHeader{
Title: "Project",
Section: "2",
Section: "1",
}

// We generate on a subcommand so we have both subcommands and parents
Expand Down Expand Up @@ -49,7 +49,7 @@ func TestGenManDoc(t *testing.T) {
func TestGenManNoHiddenParents(t *testing.T) {
header := &GenManHeader{
Title: "Project",
Section: "2",
Section: "1",
}

// We generate on a subcommand so we have both subcommands and parents
Expand Down Expand Up @@ -94,15 +94,8 @@ func TestGenManSeeAlso(t *testing.T) {
t.Fatal(err)
}
scanner := bufio.NewScanner(buf)

if err := assertLineFound(scanner, ".SH SEE ALSO"); err != nil {
t.Fatalf("Couldn't find SEE ALSO section header: %v", err)
}
if err := assertNextLineEquals(scanner, ".PP"); err != nil {
t.Fatalf("First line after SEE ALSO wasn't break-indent: %v", err)
}
if err := assertNextLineEquals(scanner, `\fBroot-bbb(1)\fP, \fBroot-ccc(1)\fP`); err != nil {
t.Fatalf("Second line after SEE ALSO wasn't correct: %v", err)
if err := assertLineFound(scanner, ".SH SEE ALSO"); err == nil {
t.Fatalf("Did not expect SEE ALSO section header")
}
}

Expand All @@ -115,31 +108,26 @@ func TestManPrintFlagsHidesShortDeprecated(t *testing.T) {
manPrintFlags(buf, c.Flags())

got := buf.String()
expected := "**--foo**=\"default\"\n\tFoo flag\n\n"
expected := "`--foo` `<string>`\n: Foo flag\n\n"
if got != expected {
t.Errorf("Expected %v, got %v", expected, got)
t.Errorf("Expected %q, got %q", expected, got)
}
}

func TestGenManTree(t *testing.T) {
c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
header := &GenManHeader{Section: "2"}
tmpdir, err := ioutil.TempDir("", "test-gen-man-tree")
if err != nil {
t.Fatalf("Failed to create tmpdir: %s", err.Error())
}
defer os.RemoveAll(tmpdir)

if err := GenManTree(c, header, tmpdir); err != nil {
if err := GenManTree(c, tmpdir); err != nil {
t.Fatalf("GenManTree failed: %s", err.Error())
}

if _, err := os.Stat(filepath.Join(tmpdir, "do.2")); err != nil {
t.Fatalf("Expected file 'do.2' to exist")
}

if header.Title != "" {
t.Fatalf("Expected header.Title to be unmodified")
if _, err := os.Stat(filepath.Join(tmpdir, "do.1")); err != nil {
t.Fatalf("Expected file 'do.1' to exist")
}
}

Expand All @@ -158,22 +146,6 @@ func assertLineFound(scanner *bufio.Scanner, expectedLine string) error {
return fmt.Errorf("hit EOF before finding %v", expectedLine)
}

func assertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error {
if scanner.Scan() {
line := scanner.Text()
if line == expectedLine {
return nil
}
return fmt.Errorf("got %v, not %v", line, expectedLine)
}

if err := scanner.Err(); err != nil {
return fmt.Errorf("scan failed: %v", err)
}

return fmt.Errorf("hit EOF before finding %v", expectedLine)
}

func BenchmarkGenManToFile(b *testing.B) {
file, err := ioutil.TempFile(b.TempDir(), "")
if err != nil {
Expand Down

0 comments on commit 9f2639e

Please sign in to comment.