mirror of
https://github.com/caddyserver/caddy.git
synced 2026-09-21 10:27:59 +00:00
cmd: Migrate to spf13/cobra, remove single-dash arg support (#4565)
* cmd: migrate to spf13/cobra * add `manpage` command * limit Caddy tagline to root `help` only * hard-code the manpage section to 8
This commit is contained in:
parent
8cb3cf540c
commit
258bc82b69
6 changed files with 123 additions and 126 deletions
33
cmd/cobra.go
Normal file
33
cmd/cobra.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package caddycmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "caddy",
|
||||
}
|
||||
|
||||
const docsHeader = "{{if not .HasParent}} Caddy is an extensible server platform.\n\n{{end}}"
|
||||
const fullDocsFooter = `Full documentation is available at:
|
||||
https://caddyserver.com/docs/command-line
|
||||
`
|
||||
|
||||
func init() {
|
||||
rootCmd.SetHelpTemplate(docsHeader + rootCmd.HelpTemplate() + "\n" + fullDocsFooter)
|
||||
}
|
||||
|
||||
func caddyCmdToCoral(caddyCmd Command) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: caddyCmd.Name,
|
||||
Short: caddyCmd.Short,
|
||||
Long: caddyCmd.Long,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
fls := cmd.Flags()
|
||||
_, err := caddyCmd.Func(Flags{fls})
|
||||
return err
|
||||
},
|
||||
}
|
||||
cmd.Flags().AddGoFlagSet(caddyCmd.Flags)
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -29,7 +29,6 @@ import (
|
|||
"os/exec"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/aryann/difflib"
|
||||
|
|
@ -580,70 +579,6 @@ func cmdFmt(fl Flags) (int, error) {
|
|||
return caddy.ExitCodeSuccess, nil
|
||||
}
|
||||
|
||||
func cmdHelp(fl Flags) (int, error) {
|
||||
const fullDocs = `Full documentation is available at:
|
||||
https://caddyserver.com/docs/command-line`
|
||||
|
||||
args := fl.Args()
|
||||
if len(args) == 0 {
|
||||
s := `Caddy is an extensible server platform.
|
||||
|
||||
usage:
|
||||
caddy <command> [<args...>]
|
||||
|
||||
commands:
|
||||
`
|
||||
keys := make([]string, 0, len(commands))
|
||||
for k := range commands {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
cmd := commands[k]
|
||||
short := strings.TrimSuffix(cmd.Short, ".")
|
||||
s += fmt.Sprintf(" %-15s %s\n", cmd.Name, short)
|
||||
}
|
||||
|
||||
s += "\nUse 'caddy help <command>' for more information about a command.\n"
|
||||
s += "\n" + fullDocs + "\n"
|
||||
|
||||
fmt.Print(s)
|
||||
|
||||
return caddy.ExitCodeSuccess, nil
|
||||
} else if len(args) > 1 {
|
||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("can only give help with one command")
|
||||
}
|
||||
|
||||
subcommand, ok := commands[args[0]]
|
||||
if !ok {
|
||||
return caddy.ExitCodeFailedStartup, fmt.Errorf("unknown command: %s", args[0])
|
||||
}
|
||||
|
||||
helpText := strings.TrimSpace(subcommand.Long)
|
||||
if helpText == "" {
|
||||
helpText = subcommand.Short
|
||||
if !strings.HasSuffix(helpText, ".") {
|
||||
helpText += "."
|
||||
}
|
||||
}
|
||||
|
||||
result := fmt.Sprintf("%s\n\nusage:\n caddy %s %s\n",
|
||||
helpText,
|
||||
subcommand.Name,
|
||||
strings.TrimSpace(subcommand.Usage),
|
||||
)
|
||||
|
||||
if help := flagHelp(subcommand.Flags); help != "" {
|
||||
result += fmt.Sprintf("\nflags: (NOTE: prefix flags with `--` instead of `-`)\n%s", help)
|
||||
}
|
||||
|
||||
result += "\n" + fullDocs + "\n"
|
||||
|
||||
fmt.Print(result)
|
||||
|
||||
return caddy.ExitCodeSuccess, nil
|
||||
}
|
||||
|
||||
// AdminAPIRequest makes an API request according to the CLI flags given,
|
||||
// with the given HTTP method and request URI. If body is non-nil, it will
|
||||
// be assumed to be Content-Type application/json. The caller should close
|
||||
|
|
|
|||
|
|
@ -16,7 +16,13 @@ package caddycmd
|
|||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/spf13/cobra/doc"
|
||||
)
|
||||
|
||||
// Command represents a subcommand. Name, Func,
|
||||
|
|
@ -70,13 +76,6 @@ func Commands() map[string]Command {
|
|||
var commands = make(map[string]Command)
|
||||
|
||||
func init() {
|
||||
RegisterCommand(Command{
|
||||
Name: "help",
|
||||
Func: cmdHelp,
|
||||
Usage: "<command>",
|
||||
Short: "Shows help for a Caddy subcommand",
|
||||
})
|
||||
|
||||
RegisterCommand(Command{
|
||||
Name: "start",
|
||||
Func: cmdStart,
|
||||
|
|
@ -346,16 +345,50 @@ EXPERIMENTAL: May be changed or removed.
|
|||
}(),
|
||||
})
|
||||
|
||||
RegisterCommand(Command{
|
||||
Name: "manpage",
|
||||
Func: func(fl Flags) (int, error) {
|
||||
dir := strings.TrimSpace(fl.String("directory"))
|
||||
if dir == "" {
|
||||
return caddy.ExitCodeFailedQuit, fmt.Errorf("designated output directory and specified section are required")
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return caddy.ExitCodeFailedQuit, err
|
||||
}
|
||||
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
|
||||
Title: "Caddy",
|
||||
Section: "8",
|
||||
}, dir); err != nil {
|
||||
return caddy.ExitCodeFailedQuit, err
|
||||
}
|
||||
return caddy.ExitCodeSuccess, nil
|
||||
},
|
||||
Usage: "--directory <directory path>",
|
||||
Short: "Generates the manual pages of Caddy commands",
|
||||
Long: `
|
||||
Generates the manual pages of Caddy commands into the designated directory tagged into the specified section.
|
||||
|
||||
The manual page files are generated into the directory specified by the argument of --directory. If the directory does not exist, it will be created.
|
||||
|
||||
The manual pages are sorted into the section specified by the argument of --section.
|
||||
`,
|
||||
Flags: func() *flag.FlagSet {
|
||||
fs := flag.NewFlagSet("manpage", flag.ExitOnError)
|
||||
fs.String("directory", "", "The output directory where the manpages are generated")
|
||||
fs.String("section", "", "The section number of the generated manual pages")
|
||||
return fs
|
||||
}(),
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterCommand registers the command cmd.
|
||||
// cmd.Name must be unique and conform to the
|
||||
// following format:
|
||||
//
|
||||
// - lowercase
|
||||
// - alphanumeric and hyphen characters only
|
||||
// - cannot start or end with a hyphen
|
||||
// - hyphen cannot be adjacent to another hyphen
|
||||
// - lowercase
|
||||
// - alphanumeric and hyphen characters only
|
||||
// - cannot start or end with a hyphen
|
||||
// - hyphen cannot be adjacent to another hyphen
|
||||
//
|
||||
// This function panics if the name is already registered,
|
||||
// if the name does not meet the described format, or if
|
||||
|
|
@ -378,7 +411,7 @@ func RegisterCommand(cmd Command) {
|
|||
if !commandNameRegex.MatchString(cmd.Name) {
|
||||
panic("invalid command name")
|
||||
}
|
||||
commands[cmd.Name] = cmd
|
||||
rootCmd.AddCommand(caddyCmdToCoral(cmd))
|
||||
}
|
||||
|
||||
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
|
||||
|
|
|
|||
49
cmd/main.go
49
cmd/main.go
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
||||
"github.com/caddyserver/certmagic"
|
||||
"github.com/spf13/pflag"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
|
@ -58,35 +59,9 @@ func Main() {
|
|||
os.Args = append(os.Args, "help")
|
||||
}
|
||||
|
||||
subcommandName := os.Args[1]
|
||||
subcommand, ok := commands[subcommandName]
|
||||
if !ok {
|
||||
if strings.HasPrefix(os.Args[1], "-") {
|
||||
// user probably forgot to type the subcommand
|
||||
fmt.Println("[ERROR] first argument must be a subcommand; see 'caddy help'")
|
||||
} else {
|
||||
fmt.Printf("[ERROR] '%s' is not a recognized subcommand; see 'caddy help'\n", os.Args[1])
|
||||
}
|
||||
os.Exit(caddy.ExitCodeFailedStartup)
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fs := subcommand.Flags
|
||||
if fs == nil {
|
||||
fs = flag.NewFlagSet(subcommand.Name, flag.ExitOnError)
|
||||
}
|
||||
|
||||
err := fs.Parse(os.Args[2:])
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(caddy.ExitCodeFailedStartup)
|
||||
}
|
||||
|
||||
exitCode, err := subcommand.Func(Flags{fs})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s: %v\n", subcommand.Name, err)
|
||||
}
|
||||
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
// handlePingbackConn reads from conn and ensures it matches
|
||||
|
|
@ -280,7 +255,7 @@ func watchConfigFile(filename, adapterName string) {
|
|||
// Flags wraps a FlagSet so that typed values
|
||||
// from flags can be easily retrieved.
|
||||
type Flags struct {
|
||||
*flag.FlagSet
|
||||
*pflag.FlagSet
|
||||
}
|
||||
|
||||
// String returns the string representation of the
|
||||
|
|
@ -326,22 +301,6 @@ func (f Flags) Duration(name string) time.Duration {
|
|||
return val
|
||||
}
|
||||
|
||||
// flagHelp returns the help text for fs.
|
||||
func flagHelp(fs *flag.FlagSet) string {
|
||||
if fs == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// temporarily redirect output
|
||||
out := fs.Output()
|
||||
defer fs.SetOutput(out)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
fs.SetOutput(buf)
|
||||
fs.PrintDefaults()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func loadEnvFromFile(envFile string) error {
|
||||
file, err := os.Open(envFile)
|
||||
if err != nil {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue