mirror of
https://github.com/juanfont/headscale.git
synced 2026-04-17 14:29:57 +02:00
cmd/headscale/cli: silence cobra error/usage output and centralise error formatting
Set SilenceErrors and SilenceUsage on the root command so that cobra never prints usage text for runtime errors. A SetFlagErrorFunc callback re-enables usage output specifically for flag-parsing errors (the kubectl pattern). Add printError to utils.go and switch Execute() to ExecuteC() so the returned error is formatted as JSON/YAML when --output requests machine-readable output.
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"slices"
|
"slices"
|
||||||
@@ -39,6 +38,14 @@ func init() {
|
|||||||
StringP("output", "o", "", "Output format. Empty for human-readable, 'json', 'json-line' or 'yaml'")
|
StringP("output", "o", "", "Output format. Empty for human-readable, 'json', 'json-line' or 'yaml'")
|
||||||
rootCmd.PersistentFlags().
|
rootCmd.PersistentFlags().
|
||||||
Bool("force", false, "Disable prompts and forces the execution")
|
Bool("force", false, "Disable prompts and forces the execution")
|
||||||
|
|
||||||
|
// Re-enable usage output only for flag-parsing errors; runtime errors
|
||||||
|
// from RunE should never dump usage text.
|
||||||
|
rootCmd.SetFlagErrorFunc(func(cmd *cobra.Command, err error) error {
|
||||||
|
cmd.SilenceUsage = false
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func initConfig() {
|
func initConfig() {
|
||||||
@@ -140,12 +147,15 @@ var rootCmd = &cobra.Command{
|
|||||||
headscale is an open source implementation of the Tailscale control server
|
headscale is an open source implementation of the Tailscale control server
|
||||||
|
|
||||||
https://github.com/juanfont/headscale`,
|
https://github.com/juanfont/headscale`,
|
||||||
|
SilenceErrors: true,
|
||||||
|
SilenceUsage: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
func Execute() {
|
func Execute() {
|
||||||
err := rootCmd.Execute()
|
cmd, err := rootCmd.ExecuteC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
outputFormat, _ := cmd.Flags().GetString("output")
|
||||||
|
printError(err, outputFormat)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,34 @@ func ErrorOutput(errResult error, override string, outputFormat string) {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// printError writes err to stderr, formatting it as JSON/YAML when the
|
||||||
|
// --output flag requests machine-readable output. Used exclusively by
|
||||||
|
// Execute() so that every error surfaces in the format the caller asked for.
|
||||||
|
func printError(err error, outputFormat string) {
|
||||||
|
type errOutput struct {
|
||||||
|
Error string `json:"error"`
|
||||||
|
}
|
||||||
|
|
||||||
|
e := errOutput{Error: err.Error()}
|
||||||
|
|
||||||
|
var formatted []byte
|
||||||
|
|
||||||
|
switch outputFormat {
|
||||||
|
case "json":
|
||||||
|
formatted, _ = json.MarshalIndent(e, "", "\t") //nolint:errchkjson // errOutput contains only a string field
|
||||||
|
case "json-line":
|
||||||
|
formatted, _ = json.Marshal(e) //nolint:errchkjson // errOutput contains only a string field
|
||||||
|
case "yaml":
|
||||||
|
formatted, _ = yaml.Marshal(e)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %s\n", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(os.Stderr, "%s\n", formatted)
|
||||||
|
}
|
||||||
|
|
||||||
func HasMachineOutputFlag() bool {
|
func HasMachineOutputFlag() bool {
|
||||||
for _, arg := range os.Args {
|
for _, arg := range os.Args {
|
||||||
if arg == "json" || arg == "json-line" || arg == "yaml" {
|
if arg == "json" || arg == "json-line" || arg == "yaml" {
|
||||||
|
|||||||
Reference in New Issue
Block a user