Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd: Use a factory to create the caddy root command #6533

Merged
merged 3 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions cmd/cobra.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (
"github.com/caddyserver/caddy/v2"
)

var rootCmd = &cobra.Command{
Use: "caddy",
Long: `Caddy is an extensible server platform written in Go.
var defaultFactory = NewRootCommandFactory(func() *cobra.Command {
return &cobra.Command{
Use: "caddy",
Long: `Caddy is an extensible server platform written in Go.

At its core, Caddy merely manages configuration. Modules are plugged
in statically at compile-time to provide useful functionality. Caddy's
Expand Down Expand Up @@ -91,23 +92,26 @@ package installers: https://caddyserver.com/docs/install
Instructions for running Caddy in production are also available:
https://caddyserver.com/docs/running
`,
Example: ` $ caddy run
Example: ` $ caddy run
$ caddy run --config caddy.json
$ caddy reload --config caddy.json
$ caddy stop`,

// kind of annoying to have all the help text printed out if
// caddy has an error provisioning its modules, for instance...
SilenceUsage: true,
Version: onlyVersionText(),
}
// kind of annoying to have all the help text printed out if
// caddy has an error provisioning its modules, for instance...
SilenceUsage: true,
Version: onlyVersionText(),
}
})

const fullDocsFooter = `Full documentation is available at:
https://caddyserver.com/docs/command-line`

func init() {
rootCmd.SetVersionTemplate("{{.Version}}\n")
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
defaultFactory.Use(func(rootCmd *cobra.Command) {
rootCmd.SetVersionTemplate("{{.Version}}\n")
rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
})
}

func onlyVersionText() string {
Expand Down
28 changes: 28 additions & 0 deletions cmd/commandfactory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package caddycmd

import (
"github.com/spf13/cobra"
)

type RootCommandFactory struct {
francislavoie marked this conversation as resolved.
Show resolved Hide resolved
constructor func() *cobra.Command
options []func(*cobra.Command)
}

func NewRootCommandFactory(fn func() *cobra.Command) *RootCommandFactory {
return &RootCommandFactory{
constructor: fn,
}
}

func (f *RootCommandFactory) Use(fn func(cmd *cobra.Command)) {
f.options = append(f.options, fn)
}

func (f *RootCommandFactory) Build() *cobra.Command {
o := f.constructor()
for _, v := range f.options {
v(o)
}
return o
}
103 changes: 54 additions & 49 deletions cmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,43 +438,45 @@ EXPERIMENTAL: May be changed or removed.
},
})

RegisterCommand(Command{
Name: "manpage",
Usage: "--directory <path>",
Short: "Generates the manual pages for Caddy commands",
Long: `
defaultFactory.Use(func(rootCmd *cobra.Command) {

francislavoie marked this conversation as resolved.
Show resolved Hide resolved
RegisterCommand(Command{
Name: "manpage",
Usage: "--directory <path>",
Short: "Generates the manual pages for Caddy commands",
Long: `
Generates the manual pages for Caddy commands into the designated directory
tagged into section 8 (System Administration).

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.
`,
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated")
cmd.RunE = WrapCommandFuncForCobra(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, 0o755); err != nil {
return caddy.ExitCodeFailedQuit, err
}
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
Title: "Caddy",
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
}, dir); err != nil {
return caddy.ExitCodeFailedQuit, err
}
return caddy.ExitCodeSuccess, nil
})
},
})

// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
rootCmd.AddCommand(&cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: fmt.Sprintf(`To load completions:
CobraFunc: func(cmd *cobra.Command) {
cmd.Flags().StringP("directory", "o", "", "The output directory where the manpages are generated")
cmd.RunE = WrapCommandFuncForCobra(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, 0o755); err != nil {
return caddy.ExitCodeFailedQuit, err
}
if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
Title: "Caddy",
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
}, dir); err != nil {
return caddy.ExitCodeFailedQuit, err
}
return caddy.ExitCodeSuccess, nil
})
},
})

// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
rootCmd.AddCommand(&cobra.Command{
Use: "completion [bash|zsh|fish|powershell]",
Short: "Generate completion script",
Long: fmt.Sprintf(`To load completions:

Bash:

Expand Down Expand Up @@ -513,23 +515,24 @@ argument of --directory. If the directory does not exist, it will be created.
PS> %[1]s completion powershell > %[1]s.ps1
# and source this file from your PowerShell profile.
`, rootCmd.Root().Name()),
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] {
case "bash":
return cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
default:
return fmt.Errorf("unrecognized shell: %s", args[0])
}
},
DisableFlagsInUseLine: true,
ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
RunE: func(cmd *cobra.Command, args []string) error {
switch args[0] {
case "bash":
return cmd.Root().GenBashCompletion(os.Stdout)
case "zsh":
return cmd.Root().GenZshCompletion(os.Stdout)
case "fish":
return cmd.Root().GenFishCompletion(os.Stdout, true)
case "powershell":
return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
default:
return fmt.Errorf("unrecognized shell: %s", args[0])
}
},
})
})
}

Expand Down Expand Up @@ -563,7 +566,9 @@ func RegisterCommand(cmd Command) {
if !commandNameRegex.MatchString(cmd.Name) {
panic("invalid command name")
}
rootCmd.AddCommand(caddyCmdToCobra(cmd))
defaultFactory.Use(func(rootCmd *cobra.Command) {
rootCmd.AddCommand(caddyCmdToCobra(cmd))
})
}

var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func Main() {
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
}

if err := rootCmd.Execute(); err != nil {
if err := defaultFactory.Build().Execute(); err != nil {
var exitError *exitError
if errors.As(err, &exitError) {
os.Exit(exitError.ExitCode)
Expand Down
Loading