@marcel91200 / caddy Mirror · read-only / commits / 9c419f1e
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 12, 22:00 UTC

cmd: Fix exiting with custom status code, add `caddy -v` (#5874)

* Simplify variables for commands * Add --envfile support for adapt command * Carry custom status code for commands to os.Exit() * cmd: add `-v` and `--version` to root caddy command * Add `--envfile` to `caddy environ`, extract flag parsing to func --------- Co-authored-by: Mohammed Al Sahaf <msaa1990@gmail.com>

Francis Lavoie committed Oct 11, 2023 at 11:46 UTC 9c419f1e1a4a82a8ed49bac3d54050890cb3e58e
5 files changed +164 -106
.gitignore
+1
@@ -12,6 +12,7 @@ Caddyfile.*
12 cmd/caddy/caddy
13 cmd/caddy/caddy.exe
14 cmd/caddy/tmp/*.exe
15 +cmd/caddy/.env
16
17 # mac specific
18 .DS_Store
cmd/cobra.go
+29 -1
@@ -1,7 +1,11 @@
1 package caddycmd
2
3 import (
4 + "fmt"
5 +
6 "github.com/spf13/cobra"
7 +
8 + "github.com/caddyserver/caddy/v2"
9 )
10
11 var rootCmd = &cobra.Command{
@@ -95,15 +99,22 @@ https://caddyserver.com/docs/running
99 // kind of annoying to have all the help text printed out if
100 // caddy has an error provisioning its modules, for instance...
101 SilenceUsage: true,
102 + Version: onlyVersionText(),
103 }
104
105 const fullDocsFooter = `Full documentation is available at:
106 https://caddyserver.com/docs/command-line`
107
108 func init() {
109 + rootCmd.SetVersionTemplate("{{.Version}}")
110 rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
111 }
112
113 +func onlyVersionText() string {
114 + _, f := caddy.Version()
115 + return f
116 +}
117 +
118 func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
119 cmd := &cobra.Command{
120 Use: caddyCmd.Name,
@@ -123,7 +134,24 @@ func caddyCmdToCobra(caddyCmd Command) *cobra.Command {
134 // in a cobra command's RunE field.
135 func WrapCommandFuncForCobra(f CommandFunc) func(cmd *cobra.Command, _ []string) error {
136 return func(cmd *cobra.Command, _ []string) error {
126 - _, err := f(Flags{cmd.Flags()})
137 + status, err := f(Flags{cmd.Flags()})
138 + if status > 1 {
139 + cmd.SilenceErrors = true
140 + return &exitError{ExitCode: status, Err: err}
141 + }
142 return err
143 }
144 }
145 +
146 +// exitError carries the exit code from CommandFunc to Main()
147 +type exitError struct {
148 + ExitCode int
149 + Err error
150 +}
151 +
152 +func (e *exitError) Error() string {
153 + if e.Err == nil {
154 + return fmt.Sprintf("exiting with status %d", e.ExitCode)
155 + }
156 + return e.Err.Error()
157 +}
cmd/commandfuncs.go
+110 -97
@@ -42,14 +42,14 @@ import (
42 )
43
44 func cmdStart(fl Flags) (int, error) {
45 - startCmdConfigFlag := fl.String("config")
46 - startCmdConfigAdapterFlag := fl.String("adapter")
47 - startCmdPidfileFlag := fl.String("pidfile")
48 - startCmdWatchFlag := fl.Bool("watch")
45 + configFlag := fl.String("config")
46 + configAdapterFlag := fl.String("adapter")
47 + pidfileFlag := fl.String("pidfile")
48 + watchFlag := fl.Bool("watch")
49
50 var err error
51 - var startCmdEnvfileFlag []string
52 - startCmdEnvfileFlag, err = fl.GetStringSlice("envfile")
51 + var envfileFlag []string
52 + envfileFlag, err = fl.GetStringSlice("envfile")
53 if err != nil {
54 return caddy.ExitCodeFailedStartup,
55 fmt.Errorf("reading envfile flag: %v", err)
@@ -74,23 +74,23 @@ func cmdStart(fl Flags) (int, error) {
74 // sure by giving it some random bytes and having it echo
75 // them back to us)
76 cmd := exec.Command(os.Args[0], "run", "--pingback", ln.Addr().String())
77 - if startCmdConfigFlag != "" {
78 - cmd.Args = append(cmd.Args, "--config", startCmdConfigFlag)
77 + if configFlag != "" {
78 + cmd.Args = append(cmd.Args, "--config", configFlag)
79 }
80
81 - for _, envFile := range startCmdEnvfileFlag {
82 - cmd.Args = append(cmd.Args, "--envfile", envFile)
81 + for _, envfile := range envfileFlag {
82 + cmd.Args = append(cmd.Args, "--envfile", envfile)
83 }
84 - if startCmdConfigAdapterFlag != "" {
85 - cmd.Args = append(cmd.Args, "--adapter", startCmdConfigAdapterFlag)
84 + if configAdapterFlag != "" {
85 + cmd.Args = append(cmd.Args, "--adapter", configAdapterFlag)
86 }
87 - if startCmdWatchFlag {
87 + if watchFlag {
88 cmd.Args = append(cmd.Args, "--watch")
89 }
90 - if startCmdPidfileFlag != "" {
91 - cmd.Args = append(cmd.Args, "--pidfile", startCmdPidfileFlag)
90 + if pidfileFlag != "" {
91 + cmd.Args = append(cmd.Args, "--pidfile", pidfileFlag)
92 }
93 - stdinpipe, err := cmd.StdinPipe()
93 + stdinPipe, err := cmd.StdinPipe()
94 if err != nil {
95 return caddy.ExitCodeFailedStartup,
96 fmt.Errorf("creating stdin pipe: %v", err)
@@ -102,7 +102,8 @@ func cmdStart(fl Flags) (int, error) {
102 expect := make([]byte, 32)
103 _, err = rand.Read(expect)
104 if err != nil {
105 - return caddy.ExitCodeFailedStartup, fmt.Errorf("generating random confirmation bytes: %v", err)
105 + return caddy.ExitCodeFailedStartup,
106 + fmt.Errorf("generating random confirmation bytes: %v", err)
107 }
108
109 // begin writing the confirmation bytes to the child's
@@ -110,14 +111,15 @@ func cmdStart(fl Flags) (int, error) {
111 // started yet, and writing synchronously would result
112 // in a deadlock
113 go func() {
113 - _, _ = stdinpipe.Write(expect)
114 - stdinpipe.Close()
114 + _, _ = stdinPipe.Write(expect)
115 + stdinPipe.Close()
116 }()
117
118 // start the process
119 err = cmd.Start()
120 if err != nil {
120 - return caddy.ExitCodeFailedStartup, fmt.Errorf("starting caddy process: %v", err)
121 + return caddy.ExitCodeFailedStartup,
122 + fmt.Errorf("starting caddy process: %v", err)
123 }
124
125 // there are two ways we know we're done: either
@@ -165,47 +167,37 @@ func cmdStart(fl Flags) (int, error) {
167 func cmdRun(fl Flags) (int, error) {
168 caddy.TrapSignals()
169
168 - runCmdConfigFlag := fl.String("config")
169 - runCmdConfigAdapterFlag := fl.String("adapter")
170 - runCmdResumeFlag := fl.Bool("resume")
171 - runCmdPrintEnvFlag := fl.Bool("environ")
172 - runCmdWatchFlag := fl.Bool("watch")
173 - runCmdPidfileFlag := fl.String("pidfile")
174 - runCmdPingbackFlag := fl.String("pingback")
175 -
176 - var err error
177 - var runCmdLoadEnvfileFlag []string
178 - runCmdLoadEnvfileFlag, err = fl.GetStringSlice("envfile")
179 - if err != nil {
180 - return caddy.ExitCodeFailedStartup,
181 - fmt.Errorf("reading envfile flag: %v", err)
182 - }
170 + configFlag := fl.String("config")
171 + configAdapterFlag := fl.String("adapter")
172 + resumeFlag := fl.Bool("resume")
173 + printEnvFlag := fl.Bool("environ")
174 + watchFlag := fl.Bool("watch")
175 + pidfileFlag := fl.String("pidfile")
176 + pingbackFlag := fl.String("pingback")
177
178 // load all additional envs as soon as possible
185 - for _, envFile := range runCmdLoadEnvfileFlag {
186 - if err := loadEnvFromFile(envFile); err != nil {
187 - return caddy.ExitCodeFailedStartup,
188 - fmt.Errorf("loading additional environment variables: %v", err)
189 - }
179 + err := handleEnvFileFlag(fl)
180 + if err != nil {
181 + return caddy.ExitCodeFailedStartup, err
182 }
183
184 // if we are supposed to print the environment, do that first
193 - if runCmdPrintEnvFlag {
185 + if printEnvFlag {
186 printEnvironment()
187 }
188
189 // load the config, depending on flags
190 var config []byte
199 - if runCmdResumeFlag {
191 + if resumeFlag {
192 config, err = os.ReadFile(caddy.ConfigAutosavePath)
193 if os.IsNotExist(err) {
194 // not a bad error; just can't resume if autosave file doesn't exist
195 caddy.Log().Info("no autosave file exists", zap.String("autosave_file", caddy.ConfigAutosavePath))
204 - runCmdResumeFlag = false
196 + resumeFlag = false
197 } else if err != nil {
198 return caddy.ExitCodeFailedStartup, err
199 } else {
208 - if runCmdConfigFlag == "" {
200 + if configFlag == "" {
201 caddy.Log().Info("resuming from last configuration",
202 zap.String("autosave_file", caddy.ConfigAutosavePath))
203 } else {
@@ -218,19 +210,19 @@ func cmdRun(fl Flags) (int, error) {
210 }
211 // we don't use 'else' here since this value might have been changed in 'if' block; i.e. not mutually exclusive
212 var configFile string
221 - if !runCmdResumeFlag {
222 - config, configFile, err = LoadConfig(runCmdConfigFlag, runCmdConfigAdapterFlag)
213 + if !resumeFlag {
214 + config, configFile, err = LoadConfig(configFlag, configAdapterFlag)
215 if err != nil {
216 return caddy.ExitCodeFailedStartup, err
217 }
218 }
219
220 // create pidfile now, in case loading config takes a while (issue #5477)
229 - if runCmdPidfileFlag != "" {
230 - err := caddy.PIDFile(runCmdPidfileFlag)
221 + if pidfileFlag != "" {
222 + err := caddy.PIDFile(pidfileFlag)
223 if err != nil {
224 caddy.Log().Error("unable to write PID file",
233 - zap.String("pidfile", runCmdPidfileFlag),
225 + zap.String("pidfile", pidfileFlag),
226 zap.Error(err))
227 }
228 }
@@ -244,13 +236,13 @@ func cmdRun(fl Flags) (int, error) {
236
237 // if we are to report to another process the successful start
238 // of the server, do so now by echoing back contents of stdin
247 - if runCmdPingbackFlag != "" {
239 + if pingbackFlag != "" {
240 confirmationBytes, err := io.ReadAll(os.Stdin)
241 if err != nil {
242 return caddy.ExitCodeFailedStartup,
243 fmt.Errorf("reading confirmation bytes from stdin: %v", err)
244 }
253 - conn, err := net.Dial("tcp", runCmdPingbackFlag)
245 + conn, err := net.Dial("tcp", pingbackFlag)
246 if err != nil {
247 return caddy.ExitCodeFailedStartup,
248 fmt.Errorf("dialing confirmation address: %v", err)
@@ -259,14 +251,14 @@ func cmdRun(fl Flags) (int, error) {
251 _, err = conn.Write(confirmationBytes)
252 if err != nil {
253 return caddy.ExitCodeFailedStartup,
262 - fmt.Errorf("writing confirmation bytes to %s: %v", runCmdPingbackFlag, err)
254 + fmt.Errorf("writing confirmation bytes to %s: %v", pingbackFlag, err)
255 }
256 }
257
258 // if enabled, reload config file automatically on changes
259 // (this better only be used in dev!)
268 - if runCmdWatchFlag {
269 - go watchConfigFile(configFile, runCmdConfigAdapterFlag)
260 + if watchFlag {
261 + go watchConfigFile(configFile, configAdapterFlag)
262 }
263
264 // warn if the environment does not provide enough information about the disk
@@ -292,11 +284,11 @@ func cmdRun(fl Flags) (int, error) {
284 }
285
286 func cmdStop(fl Flags) (int, error) {
295 - addrFlag := fl.String("address")
287 + addressFlag := fl.String("address")
288 configFlag := fl.String("config")
289 configAdapterFlag := fl.String("adapter")
290
299 - adminAddr, err := DetermineAdminAPIAddress(addrFlag, nil, configFlag, configAdapterFlag)
291 + adminAddr, err := DetermineAdminAPIAddress(addressFlag, nil, configFlag, configAdapterFlag)
292 if err != nil {
293 return caddy.ExitCodeFailedStartup, fmt.Errorf("couldn't determine admin API address: %v", err)
294 }
@@ -314,7 +306,7 @@ func cmdStop(fl Flags) (int, error) {
306 func cmdReload(fl Flags) (int, error) {
307 configFlag := fl.String("config")
308 configAdapterFlag := fl.String("adapter")
317 - addrFlag := fl.String("address")
309 + addressFlag := fl.String("address")
310 forceFlag := fl.Bool("force")
311
312 // get the config in caddy's native format
@@ -326,7 +318,7 @@ func cmdReload(fl Flags) (int, error) {
318 return caddy.ExitCodeFailedStartup, fmt.Errorf("no config file to load")
319 }
320
329 - adminAddr, err := DetermineAdminAPIAddress(addrFlag, config, configFlag, configAdapterFlag)
321 + adminAddr, err := DetermineAdminAPIAddress(addressFlag, config, configFlag, configAdapterFlag)
322 if err != nil {
323 return caddy.ExitCodeFailedStartup, fmt.Errorf("couldn't determine admin API address: %v", err)
324 }
@@ -428,48 +420,60 @@ func cmdListModules(fl Flags) (int, error) {
420 return caddy.ExitCodeSuccess, nil
421 }
422
431 -func cmdEnviron(_ Flags) (int, error) {
423 +func cmdEnviron(fl Flags) (int, error) {
424 + // load all additional envs as soon as possible
425 + err := handleEnvFileFlag(fl)
426 + if err != nil {
427 + return caddy.ExitCodeFailedStartup, err
428 + }
429 +
430 printEnvironment()
431 return caddy.ExitCodeSuccess, nil
432 }
433
434 func cmdAdaptConfig(fl Flags) (int, error) {
437 - adaptCmdInputFlag := fl.String("config")
438 - adaptCmdAdapterFlag := fl.String("adapter")
439 - adaptCmdPrettyFlag := fl.Bool("pretty")
440 - adaptCmdValidateFlag := fl.Bool("validate")
435 + inputFlag := fl.String("config")
436 + adapterFlag := fl.String("adapter")
437 + prettyFlag := fl.Bool("pretty")
438 + validateFlag := fl.Bool("validate")
439
440 var err error
443 - adaptCmdInputFlag, err = configFileWithRespectToDefault(caddy.Log(), adaptCmdInputFlag)
441 + inputFlag, err = configFileWithRespectToDefault(caddy.Log(), inputFlag)
442 if err != nil {
443 return caddy.ExitCodeFailedStartup, err
444 }
445
448 - if adaptCmdAdapterFlag == "" {
446 + // load all additional envs as soon as possible
447 + err = handleEnvFileFlag(fl)
448 + if err != nil {
449 + return caddy.ExitCodeFailedStartup, err
450 + }
451 +
452 + if adapterFlag == "" {
453 return caddy.ExitCodeFailedStartup,
454 fmt.Errorf("adapter name is required (use --adapt flag or leave unspecified for default)")
455 }
456
453 - cfgAdapter := caddyconfig.GetAdapter(adaptCmdAdapterFlag)
457 + cfgAdapter := caddyconfig.GetAdapter(adapterFlag)
458 if cfgAdapter == nil {
459 return caddy.ExitCodeFailedStartup,
456 - fmt.Errorf("unrecognized config adapter: %s", adaptCmdAdapterFlag)
460 + fmt.Errorf("unrecognized config adapter: %s", adapterFlag)
461 }
462
459 - input, err := os.ReadFile(adaptCmdInputFlag)
463 + input, err := os.ReadFile(inputFlag)
464 if err != nil {
465 return caddy.ExitCodeFailedStartup,
466 fmt.Errorf("reading input file: %v", err)
467 }
468
465 - opts := map[string]any{"filename": adaptCmdInputFlag}
469 + opts := map[string]any{"filename": inputFlag}
470
471 adaptedConfig, warnings, err := cfgAdapter.Adapt(input, opts)
472 if err != nil {
473 return caddy.ExitCodeFailedStartup, err
474 }
475
472 - if adaptCmdPrettyFlag {
476 + if prettyFlag {
477 var prettyBuf bytes.Buffer
478 err = json.Indent(&prettyBuf, adaptedConfig, "", "\t")
479 if err != nil {
@@ -487,13 +491,13 @@ func cmdAdaptConfig(fl Flags) (int, error) {
491 if warn.Directive != "" {
492 msg = fmt.Sprintf("%s: %s", warn.Directive, warn.Message)
493 }
490 - caddy.Log().Named(adaptCmdAdapterFlag).Warn(msg,
494 + caddy.Log().Named(adapterFlag).Warn(msg,
495 zap.String("file", warn.File),
496 zap.Int("line", warn.Line))
497 }
498
499 // validate output if requested
496 - if adaptCmdValidateFlag {
500 + if validateFlag {
501 var cfg *caddy.Config
502 err = caddy.StrictUnmarshalJSON(adaptedConfig, &cfg)
503 if err != nil {
@@ -509,36 +513,26 @@ func cmdAdaptConfig(fl Flags) (int, error) {
513 }
514
515 func cmdValidateConfig(fl Flags) (int, error) {
512 - validateCmdConfigFlag := fl.String("config")
513 - validateCmdAdapterFlag := fl.String("adapter")
514 -
515 - var err error
516 - var runCmdLoadEnvfileFlag []string
517 - runCmdLoadEnvfileFlag, err = fl.GetStringSlice("envfile")
518 - if err != nil {
519 - return caddy.ExitCodeFailedStartup,
520 - fmt.Errorf("reading envfile flag: %v", err)
521 - }
516 + configFlag := fl.String("config")
517 + adapterFlag := fl.String("adapter")
518
519 // load all additional envs as soon as possible
524 - for _, envFile := range runCmdLoadEnvfileFlag {
525 - if err := loadEnvFromFile(envFile); err != nil {
526 - return caddy.ExitCodeFailedStartup,
527 - fmt.Errorf("loading additional environment variables: %v", err)
528 - }
520 + err := handleEnvFileFlag(fl)
521 + if err != nil {
522 + return caddy.ExitCodeFailedStartup, err
523 }
524
525 // use default config and ensure a config file is specified
532 - validateCmdConfigFlag, err = configFileWithRespectToDefault(caddy.Log(), validateCmdConfigFlag)
526 + configFlag, err = configFileWithRespectToDefault(caddy.Log(), configFlag)
527 if err != nil {
528 return caddy.ExitCodeFailedStartup, err
529 }
536 - if validateCmdConfigFlag == "" {
530 + if configFlag == "" {
531 return caddy.ExitCodeFailedStartup,
532 fmt.Errorf("input file required when there is no Caddyfile in current directory (use --config flag)")
533 }
534
541 - input, _, err := LoadConfig(validateCmdConfigFlag, validateCmdAdapterFlag)
535 + input, _, err := LoadConfig(configFlag, adapterFlag)
536 if err != nil {
537 return caddy.ExitCodeFailedStartup, err
538 }
@@ -561,13 +555,13 @@ func cmdValidateConfig(fl Flags) (int, error) {
555 }
556
557 func cmdFmt(fl Flags) (int, error) {
564 - formatCmdConfigFile := fl.Arg(0)
565 - if formatCmdConfigFile == "" {
566 - formatCmdConfigFile = "Caddyfile"
558 + configFile := fl.Arg(0)
559 + if configFile == "" {
560 + configFile = "Caddyfile"
561 }
562
563 // as a special case, read from stdin if the file name is "-"
570 - if formatCmdConfigFile == "-" {
564 + if configFile == "-" {
565 input, err := io.ReadAll(os.Stdin)
566 if err != nil {
567 return caddy.ExitCodeFailedStartup,
@@ -577,7 +571,7 @@ func cmdFmt(fl Flags) (int, error) {
571 return caddy.ExitCodeSuccess, nil
572 }
573
580 - input, err := os.ReadFile(formatCmdConfigFile)
574 + input, err := os.ReadFile(configFile)
575 if err != nil {
576 return caddy.ExitCodeFailedStartup,
577 fmt.Errorf("reading input file: %v", err)
@@ -586,7 +580,7 @@ func cmdFmt(fl Flags) (int, error) {
580 output := caddyfile.Format(input)
581
582 if fl.Bool("overwrite") {
589 - if err := os.WriteFile(formatCmdConfigFile, output, 0o600); err != nil {
583 + if err := os.WriteFile(configFile, output, 0o600); err != nil {
584 return caddy.ExitCodeFailedStartup, fmt.Errorf("overwriting formatted file: %v", err)
585 }
586 return caddy.ExitCodeSuccess, nil
@@ -610,7 +604,7 @@ func cmdFmt(fl Flags) (int, error) {
604 fmt.Print(string(output))
605 }
606
613 - if warning, diff := caddyfile.FormattingDifference(formatCmdConfigFile, input); diff {
607 + if warning, diff := caddyfile.FormattingDifference(configFile, input); diff {
608 return caddy.ExitCodeFailedStartup, fmt.Errorf(`%s:%d: Caddyfile input is not formatted; Tip: use '--overwrite' to update your Caddyfile in-place instead of previewing it. Consult '--help' for more options`,
609 warning.File,
610 warning.Line,
@@ -620,6 +614,25 @@ func cmdFmt(fl Flags) (int, error) {
614 return caddy.ExitCodeSuccess, nil
615 }
616
617 +// handleEnvFileFlag loads the environment variables from the given --envfile
618 +// flag if specified. This should be called as early in the command function.
619 +func handleEnvFileFlag(fl Flags) error {
620 + var err error
621 + var envfileFlag []string
622 + envfileFlag, err = fl.GetStringSlice("envfile")
623 + if err != nil {
624 + return fmt.Errorf("reading envfile flag: %v", err)
625 + }
626 +
627 + for _, envfile := range envfileFlag {
628 + if err := loadEnvFromFile(envfile); err != nil {
629 + return fmt.Errorf("loading additional environment variables: %v", err)
630 + }
631 + }
632 +
633 + return nil
634 +}
635 +
636 // AdminAPIRequest makes an API request according to the CLI flags given,
637 // with the given HTTP method and request URI. If body is non-nil, it will
638 // be assumed to be Content-Type application/json. The caller should close
cmd/commands.go
+19 -8
@@ -94,8 +94,8 @@ func init() {
94 Starts the Caddy process, optionally bootstrapped with an initial config file.
95 This command unblocks after the server starts running or fails to run.
96
97 -If --envfile is specified, an environment file with environment variables in
98 -the KEY=VALUE format will be loaded into the Caddy process.
97 +If --envfile is specified, an environment file with environment variables
98 +in the KEY=VALUE format will be loaded into the Caddy process.
99
100 On Windows, the spawned child process will remain attached to the terminal, so
101 closing the window will forcefully stop Caddy; to avoid forgetting this, try
@@ -133,8 +133,8 @@ As a special case, if the current working directory has a file called
133 that file will be loaded and used to configure Caddy, even without any command
134 line flags.
135
136 -If --envfile is specified, an environment file with environment variables in
137 -the KEY=VALUE format will be loaded into the Caddy process.
136 +If --envfile is specified, an environment file with environment variables
137 +in the KEY=VALUE format will be loaded into the Caddy process.
138
139 If --environ is specified, the environment as seen by the Caddy process will
140 be printed before starting. This is the same as the environ command but does
@@ -240,6 +240,7 @@ documentation: https://go.dev/doc/modules/version-numbers
240
241 RegisterCommand(Command{
242 Name: "environ",
243 + Usage: "[--envfile <path>]",
244 Short: "Prints the environment",
245 Long: `
246 Prints the environment as seen by this Caddy process.
@@ -249,6 +250,9 @@ configuration uses environment variables (e.g. "{env.VARIABLE}") then
250 this command can be useful for verifying that the variables will have
251 the values you expect in your config.
252
253 +If --envfile is specified, an environment file with environment variables
254 +in the KEY=VALUE format will be loaded into the Caddy process.
255 +
256 Note that environments may be different depending on how you run Caddy.
257 Environments for Caddy instances started by service managers such as
258 systemd are often different than the environment inherited from your
@@ -259,12 +263,15 @@ by adding the "--environ" flag.
263
264 Environments may contain sensitive data.
265 `,
262 - Func: cmdEnviron,
266 + CobraFunc: func(cmd *cobra.Command) {
267 + cmd.Flags().StringSliceP("envfile", "", []string{}, "Environment file(s) to load")
268 + cmd.RunE = WrapCommandFuncForCobra(cmdEnviron)
269 + },
270 })
271
272 RegisterCommand(Command{
273 Name: "adapt",
267 - Usage: "--config <path> [--adapter <name>] [--pretty] [--validate]",
274 + Usage: "--config <path> [--adapter <name>] [--pretty] [--validate] [--envfile <path>]",
275 Short: "Adapts a configuration to Caddy's native JSON",
276 Long: `
277 Adapts a configuration to Caddy's native JSON format and writes the
@@ -276,12 +283,16 @@ for human readability.
283 If --validate is used, the adapted config will be checked for validity.
284 If the config is invalid, an error will be printed to stderr and a non-
285 zero exit status will be returned.
286 +
287 +If --envfile is specified, an environment file with environment variables
288 +in the KEY=VALUE format will be loaded into the Caddy process.
289 `,
290 CobraFunc: func(cmd *cobra.Command) {
291 cmd.Flags().StringP("config", "c", "", "Configuration file to adapt (required)")
292 cmd.Flags().StringP("adapter", "a", "caddyfile", "Name of config adapter")
293 cmd.Flags().BoolP("pretty", "p", false, "Format the output for human readability")
294 cmd.Flags().BoolP("validate", "", false, "Validate the output")
295 + cmd.Flags().StringSliceP("envfile", "", []string{}, "Environment file(s) to load")
296 cmd.RunE = WrapCommandFuncForCobra(cmdAdaptConfig)
297 },
298 })
@@ -295,8 +306,8 @@ Loads and provisions the provided config, but does not start running it.
306 This reveals any errors with the configuration through the loading and
307 provisioning stages.
308
298 -If --envfile is specified, an environment file with environment variables in
299 -the KEY=VALUE format will be loaded into the Caddy process.
309 +If --envfile is specified, an environment file with environment variables
310 +in the KEY=VALUE format will be loaded into the Caddy process.
311 `,
312 CobraFunc: func(cmd *cobra.Command) {
313 cmd.Flags().StringP("config", "c", "", "Input configuration file")
cmd/main.go
+5
@@ -17,6 +17,7 @@ package caddycmd
17 import (
18 "bufio"
19 "bytes"
20 + "errors"
21 "flag"
22 "fmt"
23 "io"
@@ -63,6 +64,10 @@ func Main() {
64 }
65
66 if err := rootCmd.Execute(); err != nil {
67 + var exitError *exitError
68 + if errors.As(err, &exitError) {
69 + os.Exit(exitError.ExitCode)
70 + }
71 os.Exit(1)
72 }
73 }