From 7bc7e1680e79863f97bc60be44d9d0199fdda2a0 Mon Sep 17 00:00:00 2001
From: a <a@tuxpa.in>
Date: Tue, 18 Jun 2024 21:57:46 -0500
Subject: [PATCH] noot

---
 cmd/cobra.go          | 26 ++++++++++++---------
 cmd/commandfactory.go | 28 +++++++++++++++++++++++
 cmd/commands.go       | 53 +++++++++++++++++++++++--------------------
 cmd/main.go           | 15 ++++--------
 4 files changed, 76 insertions(+), 46 deletions(-)
 create mode 100644 cmd/commandfactory.go

diff --git a/cmd/cobra.go b/cmd/cobra.go
index 1a2509206..5323d2ec5 100644
--- a/cmd/cobra.go
+++ b/cmd/cobra.go
@@ -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
@@ -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(cmd *cobra.Command) {
+		cmd.SetVersionTemplate("{{.Version}}\n")
+		cmd.SetHelpTemplate(cmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
+	})
 }
 
 func onlyVersionText() string {
diff --git a/cmd/commandfactory.go b/cmd/commandfactory.go
new file mode 100644
index 000000000..49a38a4e1
--- /dev/null
+++ b/cmd/commandfactory.go
@@ -0,0 +1,28 @@
+package caddycmd
+
+import (
+	"github.com/spf13/cobra"
+)
+
+type RootCommandFactory struct {
+	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
+}
diff --git a/cmd/commands.go b/cmd/commands.go
index e5e1265e4..7e7af1c77 100644
--- a/cmd/commands.go
+++ b/cmd/commands.go
@@ -459,7 +459,8 @@ argument of --directory. If the directory does not exist, it will be created.
 				if err := os.MkdirAll(dir, 0o755); err != nil {
 					return caddy.ExitCodeFailedQuit, err
 				}
-				if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
+				ccmd := defaultFactory.Build()
+				if err := doc.GenManTree(ccmd, &doc.GenManHeader{
 					Title:   "Caddy",
 					Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
 				}, dir); err != nil {
@@ -471,10 +472,11 @@ argument of --directory. If the directory does not exist, it will be created.
 	})
 
 	// 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:
+	defaultFactory.Use(func(ccmd *cobra.Command) {
+		ccmd.AddCommand(&cobra.Command{
+			Use:   "completion [bash|zsh|fish|powershell]",
+			Short: "Generate completion script",
+			Long: fmt.Sprintf(`To load completions:
 
 	Bash:
 
@@ -512,24 +514,25 @@ argument of --directory. If the directory does not exist, it will be created.
 	  # To load completions for every new session, run:
 	  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])
-			}
-		},
+	`, defaultFactory.constructor().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])
+				}
+			},
+		})
 	})
 }
 
@@ -563,7 +566,9 @@ func RegisterCommand(cmd Command) {
 	if !commandNameRegex.MatchString(cmd.Name) {
 		panic("invalid command name")
 	}
-	rootCmd.AddCommand(caddyCmdToCobra(cmd))
+	defaultFactory.Use(func(ccmd *cobra.Command) {
+		ccmd.AddCommand(caddyCmdToCobra(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 4526b26b6..6defac756 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -34,7 +34,6 @@ import (
 	"time"
 
 	"github.com/caddyserver/certmagic"
-	"github.com/spf13/cobra"
 	"github.com/spf13/pflag"
 	"go.uber.org/automaxprocs/maxprocs"
 	"go.uber.org/zap"
@@ -72,7 +71,7 @@ func Main() {
 	if err != nil {
 		caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
 	}
-
+	rootCmd := defaultFactory.Build()
 	if err := rootCmd.Execute(); err != nil {
 		var exitError *exitError
 		if errors.As(err, &exitError) {
@@ -86,15 +85,9 @@ func Main() {
 func MainForTesting(args ...string) error {
 	// create a root command for testing which will not pollute the global namespace, and does not
 	// call os.Exit().
-	tmpRootCmp := cobra.Command{
-		Use:          rootCmd.Use,
-		Long:         rootCmd.Long,
-		Example:      rootCmd.Example,
-		SilenceUsage: rootCmd.SilenceUsage,
-		Version:      rootCmd.Version,
-	}
-	tmpRootCmp.SetArgs(args)
-	if err := tmpRootCmp.Execute(); err != nil {
+	rootCmd := defaultFactory.Build()
+	rootCmd.SetArgs(args)
+	if err := rootCmd.Execute(); err != nil {
 		return err
 	}
 	return nil