aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd
diff options
context:
space:
mode:
authorMohammed Al Sahaf <[email protected]>2022-08-31 01:38:38 +0300
committerGitHub <[email protected]>2022-08-30 22:38:38 +0000
commit258bc82b69ccb0f514fc62ec8ecd7273458ab2e4 (patch)
tree201b76cd74ce0809f6b3061af4c145759a6e483f /cmd
parent8cb3cf540c2d083721a1717b9ddf2657b7eb5102 (diff)
downloadcaddy-258bc82b69ccb0f514fc62ec8ecd7273458ab2e4.tar.gz
caddy-258bc82b69ccb0f514fc62ec8ecd7273458ab2e4.zip
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
Diffstat (limited to 'cmd')
-rw-r--r--cmd/cobra.go33
-rw-r--r--cmd/commandfuncs.go65
-rw-r--r--cmd/commands.go57
-rw-r--r--cmd/main.go49
4 files changed, 82 insertions, 122 deletions
diff --git a/cmd/cobra.go b/cmd/cobra.go
new file mode 100644
index 000000000..ad95ec0d3
--- /dev/null
+++ b/cmd/cobra.go
@@ -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
+}
diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go
index 67015f78d..874cc6ff2 100644
--- a/cmd/commandfuncs.go
+++ b/cmd/commandfuncs.go
@@ -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
diff --git a/cmd/commands.go b/cmd/commands.go
index e454f7b62..2085dfcd0 100644
--- a/cmd/commands.go
+++ b/cmd/commands.go
@@ -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,
@@ -71,13 +77,6 @@ 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,
Usage: "[--config <path> [--adapter <name>]] [--envfile <path>] [--watch] [--pidfile <file>]",
@@ -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]$`)
diff --git a/cmd/main.go b/cmd/main.go
index 09246f4b4..44e339afe 100644
--- a/cmd/main.go
+++ b/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)
- }
-
- 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)
+ if err := rootCmd.Execute(); err != nil {
+ os.Exit(1)
}
-
- 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 {