diff --git a/cmd/headscale/cli/api_key.go b/cmd/headscale/cli/api_key.go index 5504bbf9..5c684814 100644 --- a/cmd/headscale/cli/api_key.go +++ b/cmd/headscale/cli/api_key.go @@ -7,7 +7,6 @@ import ( v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/juanfont/headscale/hscontrol/util" - "github.com/pterm/pterm" "github.com/spf13/cobra" ) @@ -51,9 +50,7 @@ var listAPIKeys = &cobra.Command{ } return printListOutput(cmd, response.GetApiKeys(), func() error { - tableData := make(pterm.TableData, 1, 1+len(response.GetApiKeys())) - tableData[0] = []string{"ID", "Prefix", colExpiration, colCreated} - + rows := make([][]string, 0, len(response.GetApiKeys())) for _, key := range response.GetApiKeys() { expiration := "-" @@ -61,7 +58,7 @@ var listAPIKeys = &cobra.Command{ expiration = ColourTime(key.GetExpiration().AsTime()) } - tableData = append(tableData, []string{ + rows = append(rows, []string{ strconv.FormatUint(key.GetId(), util.Base10), key.GetPrefix(), expiration, @@ -69,7 +66,7 @@ var listAPIKeys = &cobra.Command{ }) } - return pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() + return renderTable([]string{"ID", "Prefix", colExpiration, colCreated}, rows) }) }), } diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go index 7eb8cf51..06e2f07f 100644 --- a/cmd/headscale/cli/preauthkeys.go +++ b/cmd/headscale/cli/preauthkeys.go @@ -8,7 +8,6 @@ import ( v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/juanfont/headscale/hscontrol/util" - "github.com/pterm/pterm" "github.com/spf13/cobra" ) @@ -52,18 +51,7 @@ var listPreAuthKeys = &cobra.Command{ } return printListOutput(cmd, response.GetPreAuthKeys(), func() error { - tableData := make(pterm.TableData, 1, 1+len(response.GetPreAuthKeys())) - tableData[0] = []string{ - "ID", - "Key/Prefix", - "Reusable", - "Ephemeral", - "Used", - colExpiration, - colCreated, - "Owner", - } - + rows := make([][]string, 0, len(response.GetPreAuthKeys())) for _, key := range response.GetPreAuthKeys() { expiration := "-" if key.GetExpiration() != nil { @@ -79,7 +67,7 @@ var listPreAuthKeys = &cobra.Command{ owner = "-" } - tableData = append(tableData, []string{ + rows = append(rows, []string{ strconv.FormatUint(key.GetId(), util.Base10), key.GetKey(), strconv.FormatBool(key.GetReusable()), @@ -91,7 +79,16 @@ var listPreAuthKeys = &cobra.Command{ }) } - return pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() + return renderTable([]string{ + "ID", + "Key/Prefix", + "Reusable", + "Ephemeral", + "Used", + colExpiration, + colCreated, + "Owner", + }, rows) }) }), } diff --git a/cmd/headscale/cli/root.go b/cmd/headscale/cli/root.go index 36967ed5..212645ce 100644 --- a/cmd/headscale/cli/root.go +++ b/cmd/headscale/cli/root.go @@ -120,13 +120,7 @@ func filterPreReleasesIfStable(versionFunc func() string) func(string) bool { } // If we are on a stable release, filter out pre-releases. - for _, ignore := range prereleases { - if strings.Contains(tag, ignore) { - return true - } - } - - return false + return isPreReleaseVersion(tag) } } diff --git a/cmd/headscale/cli/users.go b/cmd/headscale/cli/users.go index 6cac0051..5639aa82 100644 --- a/cmd/headscale/cli/users.go +++ b/cmd/headscale/cli/users.go @@ -10,7 +10,6 @@ import ( v1 "github.com/juanfont/headscale/gen/go/headscale/v1" "github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/hscontrol/util/zlog/zf" - "github.com/pterm/pterm" "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) @@ -194,12 +193,10 @@ var listUsersCmd = &cobra.Command{ } return printListOutput(cmd, response.GetUsers(), func() error { - tableData := make(pterm.TableData, 1, 1+len(response.GetUsers())) - - tableData[0] = []string{"ID", "Name", "Username", "Email", colCreated} + rows := make([][]string, 0, len(response.GetUsers())) for _, user := range response.GetUsers() { - tableData = append( - tableData, + rows = append( + rows, []string{ strconv.FormatUint(user.GetId(), util.Base10), user.GetDisplayName(), @@ -210,7 +207,7 @@ var listUsersCmd = &cobra.Command{ ) } - return pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() + return renderTable([]string{"ID", "Name", "Username", "Email", colCreated}, rows) }) }), } diff --git a/cmd/headscale/cli/utils.go b/cmd/headscale/cli/utils.go index 002147b3..53ae4513 100644 --- a/cmd/headscale/cli/utils.go +++ b/cmd/headscale/cli/utils.go @@ -15,6 +15,7 @@ import ( "github.com/juanfont/headscale/hscontrol/util" "github.com/juanfont/headscale/hscontrol/util/zlog/zf" "github.com/prometheus/common/model" + "github.com/pterm/pterm" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "google.golang.org/grpc" @@ -161,7 +162,8 @@ func newHeadscaleCLIWithConfig() (context.Context, v1.HeadscaleServiceClient, *g return nil, nil, nil, nil, errAPIKeyNotSet } - grpcOptions = append(grpcOptions, + grpcOptions = append( + grpcOptions, grpc.WithPerRPCCredentials(tokenAuth{ token: apiKey, }), @@ -175,11 +177,13 @@ func newHeadscaleCLIWithConfig() (context.Context, v1.HeadscaleServiceClient, *g InsecureSkipVerify: true, } - grpcOptions = append(grpcOptions, + grpcOptions = append( + grpcOptions, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), ) } else { - grpcOptions = append(grpcOptions, + grpcOptions = append( + grpcOptions, grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), ) } @@ -268,9 +272,19 @@ func confirmAction(cmd *cobra.Command, prompt string) bool { return util.YesNo(prompt) } +// renderTable prints a human-readable pterm table with the given header row +// and data rows, using the shared header styling. +func renderTable(header []string, rows [][]string) error { + tableData := make(pterm.TableData, 0, 1+len(rows)) + tableData = append(tableData, header) + tableData = append(tableData, rows...) + + return pterm.DefaultTable.WithHasHeader().WithData(tableData).Render() +} + // printListOutput checks the --output flag: when a machine-readable format is -// requested it serialises data as JSON/YAML; otherwise it calls renderTable -// to produce the human-readable pterm table. +// requested it serialises data as JSON/YAML; otherwise it calls the render +// callback to produce the human-readable pterm table. func printListOutput( cmd *cobra.Command, data any, @@ -292,24 +306,15 @@ func printError(err error, outputFormat string) { Error string `json:"error"` } - e := errOutput{Error: err.Error()} - - var formatted []byte - - switch outputFormat { - case outputFormatJSON: - formatted, _ = json.MarshalIndent(e, "", "\t") //nolint:errchkjson // errOutput contains only a string field - case outputFormatJSONLine: - formatted, _ = json.Marshal(e) //nolint:errchkjson // errOutput contains only a string field - case outputFormatYAML: - formatted, _ = yaml.Marshal(e) - default: + if outputFormat == "" { fmt.Fprintf(os.Stderr, "Error: %s\n", err) return } - fmt.Fprintf(os.Stderr, "%s\n", formatted) + // formatOutput cannot fail here: errOutput is a single string field. + out, _ := formatOutput(errOutput{Error: err.Error()}, "", outputFormat) + fmt.Fprintf(os.Stderr, "%s\n", out) } func hasMachineOutputFlag() bool {