Merge pull request #4675 from cli/docs-web-formatting
Overhaul manual pages for the web
This commit is contained in:
commit
dae77fd398
5 changed files with 181 additions and 68 deletions
|
|
@ -1,35 +1,83 @@
|
|||
package docs
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
func printOptions(buf *bytes.Buffer, cmd *cobra.Command, name string) error {
|
||||
func printOptions(w io.Writer, cmd *cobra.Command) error {
|
||||
flags := cmd.NonInheritedFlags()
|
||||
flags.SetOutput(buf)
|
||||
flags.SetOutput(w)
|
||||
if flags.HasAvailableFlags() {
|
||||
buf.WriteString("### Options\n\n```\n")
|
||||
flags.PrintDefaults()
|
||||
buf.WriteString("```\n\n")
|
||||
fmt.Fprint(w, "### Options\n\n")
|
||||
if err := printFlagsHTML(w, flags); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(w, "\n\n")
|
||||
}
|
||||
|
||||
parentFlags := cmd.InheritedFlags()
|
||||
parentFlags.SetOutput(buf)
|
||||
if parentFlags.HasAvailableFlags() {
|
||||
buf.WriteString("### Options inherited from parent commands\n\n```\n")
|
||||
parentFlags.PrintDefaults()
|
||||
buf.WriteString("```\n\n")
|
||||
parentFlags.SetOutput(w)
|
||||
if hasNonHelpFlags(parentFlags) {
|
||||
fmt.Fprint(w, "### Options inherited from parent commands\n\n")
|
||||
if err := printFlagsHTML(w, parentFlags); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprint(w, "\n\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasNonHelpFlags(fs *pflag.FlagSet) (found bool) {
|
||||
fs.VisitAll(func(f *pflag.Flag) {
|
||||
if !f.Hidden && f.Name != "help" {
|
||||
found = true
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
type flagView struct {
|
||||
Name string
|
||||
Varname string
|
||||
Shorthand string
|
||||
Usage string
|
||||
}
|
||||
|
||||
var flagsTemplate = `
|
||||
<dl class="flags">{{ range . }}
|
||||
<dt>{{ if .Shorthand }}<code>-{{.Shorthand}}</code>, {{ end -}}
|
||||
<code>--{{.Name}}{{ if .Varname }} <{{.Varname}}>{{ end }}</code></dt>
|
||||
<dd>{{.Usage}}</dd>
|
||||
{{ end }}</dl>
|
||||
`
|
||||
|
||||
var tpl = template.Must(template.New("flags").Parse(flagsTemplate))
|
||||
|
||||
func printFlagsHTML(w io.Writer, fs *pflag.FlagSet) error {
|
||||
var flags []flagView
|
||||
fs.VisitAll(func(f *pflag.Flag) {
|
||||
if f.Hidden || f.Name == "help" {
|
||||
return
|
||||
}
|
||||
varname, usage := pflag.UnquoteUsage(f)
|
||||
flags = append(flags, flagView{
|
||||
Name: f.Name,
|
||||
Varname: varname,
|
||||
Shorthand: f.Shorthand,
|
||||
Usage: usage,
|
||||
})
|
||||
})
|
||||
return tpl.Execute(w, flags)
|
||||
}
|
||||
|
||||
// GenMarkdown creates markdown output.
|
||||
func GenMarkdown(cmd *cobra.Command, w io.Writer) error {
|
||||
return GenMarkdownCustom(cmd, w, func(s string) string { return s })
|
||||
|
|
@ -37,33 +85,97 @@ func GenMarkdown(cmd *cobra.Command, w io.Writer) error {
|
|||
|
||||
// GenMarkdownCustom creates custom markdown output.
|
||||
func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string) string) error {
|
||||
cmd.InitDefaultHelpCmd()
|
||||
cmd.InitDefaultHelpFlag()
|
||||
fmt.Fprintf(w, "## %s\n\n", cmd.CommandPath())
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
name := cmd.CommandPath()
|
||||
|
||||
buf.WriteString("## " + name + "\n\n")
|
||||
buf.WriteString(cmd.Short + "\n\n")
|
||||
if len(cmd.Long) > 0 {
|
||||
buf.WriteString("### Synopsis\n\n")
|
||||
buf.WriteString(cmd.Long + "\n\n")
|
||||
hasLong := cmd.Long != ""
|
||||
if !hasLong {
|
||||
fmt.Fprintf(w, "%s\n\n", cmd.Short)
|
||||
}
|
||||
if cmd.Runnable() {
|
||||
fmt.Fprintf(w, "```\n%s\n```\n\n", cmd.UseLine())
|
||||
}
|
||||
if hasLong {
|
||||
fmt.Fprintf(w, "%s\n\n", cmd.Long)
|
||||
}
|
||||
|
||||
if cmd.Runnable() {
|
||||
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.UseLine()))
|
||||
for _, g := range subcommandGroups(cmd) {
|
||||
if len(g.Commands) == 0 {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "### %s\n\n", g.Name)
|
||||
for _, subcmd := range g.Commands {
|
||||
fmt.Fprintf(w, "* [%s](%s)\n", subcmd.CommandPath(), linkHandler(cmdManualPath(subcmd)))
|
||||
}
|
||||
fmt.Fprint(w, "\n\n")
|
||||
}
|
||||
|
||||
if err := printOptions(w, cmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cmd.Example) > 0 {
|
||||
buf.WriteString("### Examples\n\n")
|
||||
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.Example))
|
||||
fmt.Fprint(w, "### Examples\n\n{% highlight bash %}{% raw %}\n")
|
||||
fmt.Fprint(w, cmd.Example)
|
||||
fmt.Fprint(w, "{% endraw %}{% endhighlight %}\n\n")
|
||||
}
|
||||
|
||||
if err := printOptions(buf, cmd, name); err != nil {
|
||||
return err
|
||||
if cmd.HasParent() {
|
||||
p := cmd.Parent()
|
||||
fmt.Fprint(w, "### See also\n\n")
|
||||
fmt.Fprintf(w, "* [%s](%s)\n", p.CommandPath(), linkHandler(cmdManualPath(p)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type commandGroup struct {
|
||||
Name string
|
||||
Commands []*cobra.Command
|
||||
}
|
||||
|
||||
// subcommandGroups lists child commands of a Cobra command split into groups.
|
||||
// TODO: have rootHelpFunc use this instead of repeating the same logic.
|
||||
func subcommandGroups(c *cobra.Command) []commandGroup {
|
||||
var rest []*cobra.Command
|
||||
var core []*cobra.Command
|
||||
var actions []*cobra.Command
|
||||
|
||||
for _, subcmd := range c.Commands() {
|
||||
if !subcmd.IsAvailableCommand() {
|
||||
continue
|
||||
}
|
||||
if _, ok := subcmd.Annotations["IsCore"]; ok {
|
||||
core = append(core, subcmd)
|
||||
} else if _, ok := subcmd.Annotations["IsActions"]; ok {
|
||||
actions = append(actions, subcmd)
|
||||
} else {
|
||||
rest = append(rest, subcmd)
|
||||
}
|
||||
}
|
||||
|
||||
if len(core) > 0 {
|
||||
return []commandGroup{
|
||||
{
|
||||
Name: "Core commands",
|
||||
Commands: core,
|
||||
},
|
||||
{
|
||||
Name: "Actions commands",
|
||||
Commands: actions,
|
||||
},
|
||||
{
|
||||
Name: "Additional commands",
|
||||
Commands: rest,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return []commandGroup{
|
||||
{
|
||||
Name: "Commands",
|
||||
Commands: rest,
|
||||
},
|
||||
}
|
||||
_, err := buf.WriteTo(w)
|
||||
return err
|
||||
}
|
||||
|
||||
// GenMarkdownTree will generate a markdown page for this command and all
|
||||
|
|
@ -92,12 +204,7 @@ func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHa
|
|||
}
|
||||
}
|
||||
|
||||
basename := strings.Replace(cmd.CommandPath(), " ", "_", -1) + ".md"
|
||||
if basenameOverride, found := cmd.Annotations["markdown:basename"]; found {
|
||||
basename = basenameOverride + ".md"
|
||||
}
|
||||
|
||||
filename := filepath.Join(dir, basename)
|
||||
filename := filepath.Join(dir, cmdManualPath(cmd))
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -112,3 +219,10 @@ func GenMarkdownTreeCustom(cmd *cobra.Command, dir string, filePrepender, linkHa
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cmdManualPath(c *cobra.Command) string {
|
||||
if basenameOverride, found := c.Annotations["markdown:basename"]; found {
|
||||
return basenameOverride + ".md"
|
||||
}
|
||||
return strings.ReplaceAll(c.CommandPath(), " ", "_") + ".md"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package actions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
|
|
@ -14,11 +12,8 @@ func NewCmdActions(f *cmdutil.Factory) *cobra.Command {
|
|||
|
||||
cmd := &cobra.Command{
|
||||
Use: "actions",
|
||||
Short: "Learn about working with GitHub actions",
|
||||
Short: "Learn about working with GitHub Actions",
|
||||
Long: actionsExplainer(cs),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Fprintln(f.IOStreams.Out, actionsExplainer(cs))
|
||||
},
|
||||
Annotations: map[string]string{
|
||||
"IsActions": "true",
|
||||
},
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/MakeNowJust/heredoc"
|
||||
"github.com/cli/cli/v2/internal/codespaces"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/liveshare"
|
||||
|
|
@ -156,36 +157,36 @@ func newCpCmd(app *App) *cobra.Command {
|
|||
var opts cpOptions
|
||||
|
||||
cpCmd := &cobra.Command{
|
||||
Use: "cp [-e] [-r] srcs... dest",
|
||||
Use: "cp [-e] [-r] <sources>... <dest>",
|
||||
Short: "Copy files between local and remote file systems",
|
||||
Long: `
|
||||
The cp command copies files between the local and remote file systems.
|
||||
Long: heredoc.Docf(`
|
||||
The cp command copies files between the local and remote file systems.
|
||||
|
||||
As with the UNIX cp command, the first argument specifies the source and the last
|
||||
specifies the destination; additional sources may be specified after the first,
|
||||
if the destination is a directory.
|
||||
As with the UNIX %[1]scp%[1]s command, the first argument specifies the source and the last
|
||||
specifies the destination; additional sources may be specified after the first,
|
||||
if the destination is a directory.
|
||||
|
||||
The -r (recursive) flag is required if any source is a directory.
|
||||
The %[1]s--recursive%[1]s flag is required if any source is a directory.
|
||||
|
||||
A 'remote:' prefix on any file name argument indicates that it refers to
|
||||
the file system of the remote (Codespace) machine. It is resolved relative
|
||||
to the home directory of the remote user.
|
||||
A "remote:" prefix on any file name argument indicates that it refers to
|
||||
the file system of the remote (Codespace) machine. It is resolved relative
|
||||
to the home directory of the remote user.
|
||||
|
||||
By default, remote file names are interpreted literally. With the -e flag,
|
||||
each such argument is treated in the manner of scp, as a Bash expression to
|
||||
be evaluated on the remote machine, subject to expansion of tildes, braces,
|
||||
globs, environment variables, and backticks, as in these examples:
|
||||
|
||||
$ gh codespace cp -e README.md 'remote:/workspace/$RepositoryName/'
|
||||
$ gh codespace cp -e 'remote:~/*.go' ./gofiles/
|
||||
$ gh codespace cp -e 'remote:/workspace/myproj/go.{mod,sum}' ./gofiles/
|
||||
|
||||
For security, do not use the -e flag with arguments provided by untrusted
|
||||
users; see https://lwn.net/Articles/835962/ for discussion.
|
||||
`,
|
||||
By default, remote file names are interpreted literally. With the %[1]s--expand%[1]s flag,
|
||||
each such argument is treated in the manner of %[1]sscp%[1]s, as a Bash expression to
|
||||
be evaluated on the remote machine, subject to expansion of tildes, braces, globs,
|
||||
environment variables, and backticks. For security, do not use this flag with arguments
|
||||
provided by untrusted users; see <https://lwn.net/Articles/835962/> for discussion.
|
||||
`, "`"),
|
||||
Example: heredoc.Doc(`
|
||||
$ gh codespace cp -e README.md 'remote:/workspace/$RepositoryName/'
|
||||
$ gh codespace cp -e 'remote:~/*.go' ./gofiles/
|
||||
$ gh codespace cp -e 'remote:/workspace/myproj/go.{mod,sum}' ./gofiles/
|
||||
`),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return app.Copy(cmd.Context(), args, opts)
|
||||
},
|
||||
DisableFlagsInUseLine: true,
|
||||
}
|
||||
|
||||
// We don't expose all sshOptions.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ var HelpTopics = map[string]map[string]string{
|
|||
to, e.g. "less".
|
||||
|
||||
GLAMOUR_STYLE: the style to use for rendering Markdown. See
|
||||
https://github.com/charmbracelet/glamour#styles
|
||||
<https://github.com/charmbracelet/glamour#styles>
|
||||
|
||||
NO_COLOR: set to any value to avoid printing ANSI escape sequences for color output.
|
||||
|
||||
|
|
@ -90,14 +90,14 @@ var HelpTopics = map[string]map[string]string{
|
|||
The %[1]s--jq%[1]s option accepts a query in jq syntax and will print only the resulting
|
||||
values that match the query. This is equivalent to piping the output to %[1]sjq -r%[1]s,
|
||||
but does not require the jq utility to be installed on the system. To learn more
|
||||
about the query syntax, see: https://stedolan.github.io/jq/manual/v1.6/
|
||||
about the query syntax, see: <https://stedolan.github.io/jq/manual/v1.6/>
|
||||
|
||||
With %[1]s--template%[1]s, the provided Go template is rendered using the JSON data as input.
|
||||
For the syntax of Go templates, see: https://golang.org/pkg/text/template/
|
||||
For the syntax of Go templates, see: <https://golang.org/pkg/text/template/>
|
||||
|
||||
The following functions are available in templates:
|
||||
- %[1]sautocolor%[1]s: like %[1]scolor%[1]s, but only emits color to terminals
|
||||
- %[1]scolor <style> <input>%[1]s: colorize input using https://github.com/mgutz/ansi
|
||||
- %[1]scolor <style> <input>%[1]s: colorize input using <https://github.com/mgutz/ansi>
|
||||
- %[1]sjoin <sep> <list>%[1]s: joins values in the list using a separator
|
||||
- %[1]spluck <field> <list>%[1]s: collects values of a field from all items in the input
|
||||
- %[1]stablerow <fields>...%[1]s: aligns fields in output vertically as a table
|
||||
|
|
|
|||
|
|
@ -16,7 +16,10 @@ func NewCmdSecret(f *cmdutil.Factory) *cobra.Command {
|
|||
Long: heredoc.Doc(`
|
||||
Secrets can be set at the repository, environment, or organization level for use in
|
||||
GitHub Actions. Run "gh help secret set" to learn how to get started.
|
||||
`),
|
||||
`),
|
||||
Annotations: map[string]string{
|
||||
"IsActions": "true",
|
||||
},
|
||||
}
|
||||
|
||||
cmdutil.EnableRepoOverride(cmd, f)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue