1
0
mirror of https://github.com/regclient/regclient.git synced 2025-04-18 22:44:00 +03:00

Feat: Add cobra command for documentation

This will be used for generating website content.

Signed-off-by: Brandon Mitchell <git@bmitch.net>
This commit is contained in:
Brandon Mitchell 2025-01-19 14:47:42 -05:00
parent 16973da2a2
commit bbb902ba69
No known key found for this signature in database
GPG Key ID: 6E0FF28C767A8BEE
7 changed files with 132 additions and 69 deletions

View File

@ -14,6 +14,7 @@ import (
"github.com/regclient/regclient"
"github.com/regclient/regclient/cmd/regbot/sandbox"
"github.com/regclient/regclient/config"
"github.com/regclient/regclient/internal/cobradoc"
"github.com/regclient/regclient/internal/pqueue"
"github.com/regclient/regclient/internal/version"
"github.com/regclient/regclient/pkg/template"
@ -23,7 +24,7 @@ import (
const (
usageDesc = `Utility for automating repository actions
More details at https://github.com/regclient/regclient`
More details at <https://github.com/regclient/regclient>`
// UserAgent sets the header on http requests
UserAgent = "regclient/regbot"
)
@ -88,6 +89,7 @@ returns after the last script completes.`,
rootTopCmd.AddCommand(serverCmd)
rootTopCmd.AddCommand(onceCmd)
rootTopCmd.AddCommand(versionCmd)
rootTopCmd.AddCommand(cobradoc.NewCmd(rootTopCmd.Name(), "cli-doc"))
rootTopCmd.PersistentPreRunE = rootOpts.rootPreRun
return rootTopCmd, &rootOpts

View File

@ -2,8 +2,6 @@ package main
import (
"context"
"fmt"
"os"
"strings"
"github.com/spf13/cobra"
@ -12,69 +10,6 @@ import (
"github.com/regclient/regclient/types/ref"
)
func NewCompletionCmd(rootOpts *rootCmd) *cobra.Command {
var completionTopCmd = &cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: fmt.Sprintf(`To load completions:
Bash:
$ source <(%[1]s completion bash)
# To load completions for each session, execute once:
# Linux:
$ %[1]s completion bash > /etc/bash_completion.d/%[1]s
# macOS:
$ %[1]s completion bash > $(brew --prefix)/etc/bash_completion.d/%[1]s
Zsh:
# If shell completion is not already enabled in your environment,
# you will need to enable it. You can execute the following once:
$ echo "autoload -U compinit; compinit" >> ~/.zshrc
# To load completions for each session, execute once:
$ %[1]s completion zsh > "${fpath[1]}/_%[1]s"
# You will need to start a new shell for this setup to take effect.
fish:
$ %[1]s completion fish | source
# To load completions for each session, execute once:
$ %[1]s completion fish > ~/.config/fish/completions/%[1]s.fish
PowerShell:
PS> %[1]s completion powershell | Out-String | Invoke-Expression
# To load completions for every new session, run:
PS> %[1]s completion powershell > %[1]s.ps1
# and source this file from your PowerShell profile.
`, rootOpts.name),
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
Run: func(cmd *cobra.Command, args []string) {
switch args[0] {
case "bash":
_ = cmd.Root().GenBashCompletionV2(os.Stdout, true)
case "zsh":
_ = cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
_ = cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
_ = cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
}
},
}
return completionTopCmd
}
type completeFunc func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)
// completeArgList takes a list of completion functions and completes each arg separately

View File

@ -10,6 +10,7 @@ import (
"github.com/regclient/regclient"
"github.com/regclient/regclient/config"
"github.com/regclient/regclient/internal/cobradoc"
"github.com/regclient/regclient/internal/strparse"
"github.com/regclient/regclient/internal/version"
"github.com/regclient/regclient/pkg/template"
@ -39,7 +40,7 @@ func NewRootCmd() (*cobra.Command, *rootCmd) {
Use: "regctl <cmd>",
Short: "Utility for accessing docker registries",
Long: `Utility for accessing docker registries
More details at https://github.com/regclient/regclient`,
More details at <https://github.com/regclient/regclient>`,
Example: `
# login to ghcr.io
regctl registry login ghcr.io
@ -94,10 +95,10 @@ regctl version --format '{{.VCSTag}}'`,
rootTopCmd.PersistentPreRunE = rootOpts.rootPreRun
rootTopCmd.AddCommand(versionCmd)
rootTopCmd.AddCommand(cobradoc.NewCmd(rootOpts.name, "cli-doc"))
rootTopCmd.AddCommand(
NewArtifactCmd(&rootOpts),
NewBlobCmd(&rootOpts),
NewCompletionCmd(&rootOpts),
NewConfigCmd(&rootOpts),
NewDigestCmd(&rootOpts),
NewImageCmd(&rootOpts),

View File

@ -21,6 +21,7 @@ import (
"github.com/regclient/regclient"
"github.com/regclient/regclient/config"
"github.com/regclient/regclient/internal/cobradoc"
"github.com/regclient/regclient/internal/pqueue"
"github.com/regclient/regclient/internal/version"
"github.com/regclient/regclient/pkg/template"
@ -68,7 +69,7 @@ func NewRootCmd() (*cobra.Command, *rootCmd) {
Use: "regsync <cmd>",
Short: "Utility for mirroring docker repositories",
Long: `Utility for mirroring docker repositories
More details at https://github.com/regclient/regclient`,
More details at <https://github.com/regclient/regclient>`,
SilenceUsage: true,
SilenceErrors: true,
}
@ -135,6 +136,7 @@ sync step is finished.`,
rootTopCmd.AddCommand(onceCmd)
rootTopCmd.AddCommand(configCmd)
rootTopCmd.AddCommand(versionCmd)
rootTopCmd.AddCommand(cobradoc.NewCmd(rootTopCmd.Name(), "cli-doc"))
rootTopCmd.PersistentPreRunE = rootOpts.rootPreRun
return rootTopCmd, &rootOpts

View File

@ -59,6 +59,8 @@ The following functions have been added in addition to the defaults available in
Returns current time object, e.g. `{{ $t := time.Now }}{{printf "%d%d%d" $t.Year $t.Month $t.Day}}`.
- `time.Parse`:
Parses string using layout into time object, e.g. `{{ $t := time.Parse "2006-01-02" "2020-06-07"}}`.
- `trimSpace`:
Trim whitespace from the start and end of a string.
- `upper`:
Converts a string to uppercase.

View File

@ -0,0 +1,120 @@
// Package cobradoc is used to generate documentation from cobra commands.
package cobradoc
import (
"bytes"
"fmt"
"io"
"strings"
"github.com/spf13/cobra"
"github.com/regclient/regclient/pkg/template"
)
// List outputs a list of cobra commands.
func List(cmd *cobra.Command, hidden bool, out io.Writer) {
var recurse func(cmd *cobra.Command, out io.Writer)
recurse = func(cmd *cobra.Command, out io.Writer) {
fmt.Fprintf(out, "%s\n", cmd.CommandPath())
for _, child := range cmd.Commands() {
if !hidden && child.Hidden {
continue
}
recurse(child, out)
}
}
recurse(cmd, out)
}
// Markdown outputs docs on a cobra command in Markdown format.
func Markdown(cmd *cobra.Command, out io.Writer) error {
cmd.InitDefaultHelpCmd()
cmd.InitDefaultHelpFlag()
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(strings.TrimSpace(cmd.Long) + "\n\n")
}
if cmd.Runnable() {
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", cmd.UseLine()))
}
if len(cmd.Example) > 0 {
buf.WriteString("### Examples\n\n")
buf.WriteString(fmt.Sprintf("```\n%s\n```\n\n", strings.TrimSpace(cmd.Example)))
}
if err := printOptions(buf, cmd); err != nil {
return err
}
_, err := buf.WriteTo(out)
return err
}
func printOptions(buf *bytes.Buffer, cmd *cobra.Command) error {
flags := cmd.NonInheritedFlags()
flags.SetOutput(buf)
if flags.HasAvailableFlags() {
buf.WriteString("### Options\n\n```\n")
flags.PrintDefaults()
buf.WriteString("```\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")
}
return nil
}
type docOpts struct {
format string
hidden bool
list bool
}
// NewCmd generates a new cobra command for generating docs.
func NewCmd(rootName, newName string) *cobra.Command {
opts := docOpts{}
var docCmd = &cobra.Command{
Hidden: true,
Use: newName,
Short: "Document CLI",
Long: `Document Cobra CLI`,
Example: fmt.Sprintf(`
# list all commands
%[1]s %[2]s --list
# output documentation for the "version" command
%[1]s %[2]s version`, rootName, newName),
Args: cobra.ArbitraryArgs,
RunE: opts.runCLIDoc,
}
docCmd.Flags().BoolVar(&opts.hidden, "hidden", false, "Include hidden commands in the list")
docCmd.Flags().BoolVar(&opts.list, "list", false, "List all commands")
docCmd.Flags().StringVar(&opts.format, "format", "", "Format output with go template syntax")
return docCmd
}
func (opts *docOpts) runCLIDoc(cmd *cobra.Command, args []string) error {
if opts.list {
List(cmd.Parent(), opts.hidden, cmd.OutOrStdout())
return nil
}
docCmd, _, err := cmd.Parent().Find(args)
if err != nil {
return err
}
if opts.format != "" {
return template.Writer(cmd.OutOrStdout(), opts.format, docCmd)
}
return Markdown(docCmd, cmd.OutOrStdout())
}

View File

@ -49,6 +49,7 @@ var tmplFuncs = gotemplate.FuncMap{
"lower": strings.ToLower,
"split": strings.Split,
"time": func() *TimeFuncs { return &TimeFuncs{} },
"trimSpace": strings.TrimSpace,
"upper": strings.ToUpper,
}