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
}