From 8b30f9498afdb627f656a2bac16c419011be6ae3 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 14:17:54 -0600 Subject: [PATCH 01/11] improve some cli errors --- cli/root.go | 30 ++++++++++++++++++++---------- codersdk/client.go | 17 +++++++++++++---- codersdk/deployment.go | 2 +- enterprise/wsproxy/wsproxy.go | 6 +++++- 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/cli/root.go b/cli/root.go index cef7df19b366d..a2c9b6c5a9728 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1035,8 +1035,10 @@ func cliHumanFormatError(err error, opts *formatOpts) string { if opts == nil { opts = &formatOpts{} } + if err == nil { + return "" + } - //nolint:errorlint if multi, ok := err.(interface{ Unwrap() []error }); ok { multiErrors := multi.Unwrap() if len(multiErrors) == 1 { @@ -1048,16 +1050,28 @@ func cliHumanFormatError(err error, opts *formatOpts) string { // First check for sentinel errors that we want to handle specially. // Order does matter! We want to check for the most specific errors first. - var sdkError *codersdk.Error - if errors.As(err, &sdkError) { + //var sdkError *codersdk.Error + //if errors.As(err, &sdkError) { + if sdkError, ok := err.(*codersdk.Error); ok { return formatCoderSDKError(sdkError, opts) } - var cmdErr *clibase.RunCommandError - if errors.As(err, &cmdErr) { + //var cmdErr *clibase.RunCommandError + //if errors.As(err, &cmdErr) { + if cmdErr, ok := err.(*clibase.RunCommandError); ok { return formatRunCommandError(cmdErr, opts) } + uw, ok := err.(interface{ Unwrap() error }) + if ok { + msg := cliHumanFormatError(uw.Unwrap(), opts) + if msg != "" { + return msg + } + } + // If we got here, that means the error is not anything special. Just format + // it as is. + // Default just printing the error. Use +v for verbose to handle stack // traces of xerrors. if opts.Verbose { @@ -1112,11 +1126,7 @@ func formatRunCommandError(err *clibase.RunCommandError, opts *formatOpts) strin var str strings.Builder _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Encountered an error running %q", err.Cmd.FullName()))) - msgString := fmt.Sprintf("%v", err.Err) - if opts.Verbose { - // '%+v' includes stack traces - msgString = fmt.Sprintf("%+v", err.Err) - } + msgString := cliHumanFormatError(err.Err, opts) _, _ = str.WriteString("\n") _, _ = str.WriteString(pretty.Sprint(tailLineStyle(), msgString)) return str.String() diff --git a/codersdk/client.go b/codersdk/client.go index 05ca69fba64f8..d7ca661adff4c 100644 --- a/codersdk/client.go +++ b/codersdk/client.go @@ -323,6 +323,17 @@ func (c *Client) Request(ctx context.Context, method, path string, body interfac return resp, err } +// ExpectJSONMime is a helper function that will assert the content type +// of the response is application/json. +func ExpectJSONMime(res *http.Response) error { + contentType := res.Header.Get("Content-Type") + mimeType := parseMimeType(contentType) + if mimeType != "application/json" { + return xerrors.Errorf("unexpected non-JSON response %q", contentType) + } + return nil +} + // ReadBodyAsError reads the response as a codersdk.Response, and // wraps it in a codersdk.Error type for easy marshaling. func ReadBodyAsError(res *http.Response) error { @@ -330,7 +341,6 @@ func ReadBodyAsError(res *http.Response) error { return xerrors.Errorf("no body returned") } defer res.Body.Close() - contentType := res.Header.Get("Content-Type") var requestMethod, requestURL string if res.Request != nil { @@ -352,8 +362,7 @@ func ReadBodyAsError(res *http.Response) error { return xerrors.Errorf("read body: %w", err) } - mimeType := parseMimeType(contentType) - if mimeType != "application/json" { + if mimeErr := ExpectJSONMime(res); mimeErr != nil { if len(resp) > 2048 { resp = append(resp[:2048], []byte("...")...) } @@ -365,7 +374,7 @@ func ReadBodyAsError(res *http.Response) error { method: requestMethod, url: requestURL, Response: Response{ - Message: fmt.Sprintf("unexpected non-JSON response %q", contentType), + Message: mimeErr.Error(), Detail: string(resp), }, Helper: helpMessage, diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 29d5a0cd3cf89..5559454dba051 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -2065,7 +2065,7 @@ func (c *Client) BuildInfo(ctx context.Context) (BuildInfoResponse, error) { } defer res.Body.Close() - if res.StatusCode != http.StatusOK { + if res.StatusCode != http.StatusBadRequest || ExpectJSONMime(res) != nil { return BuildInfoResponse{}, ReadBodyAsError(res) } diff --git a/enterprise/wsproxy/wsproxy.go b/enterprise/wsproxy/wsproxy.go index 9ad8f16764c28..052d5a21d0e8a 100644 --- a/enterprise/wsproxy/wsproxy.go +++ b/enterprise/wsproxy/wsproxy.go @@ -4,6 +4,7 @@ import ( "context" "crypto/tls" "crypto/x509" + "errors" "fmt" "net/http" "net/url" @@ -157,7 +158,10 @@ func New(ctx context.Context, opts *Options) (*Server, error) { // TODO: Probably do some version checking here info, err := client.SDKClient.BuildInfo(ctx) if err != nil { - return nil, xerrors.Errorf("failed to fetch build info from %q: %w", opts.DashboardURL, err) + return nil, errors.Join( + fmt.Errorf("unable to fetch build info from primary coderd. Are you sure %q is a coderd instance?", opts.DashboardURL), + err, + ) } if info.WorkspaceProxy { return nil, xerrors.Errorf("%q is a workspace proxy, not a primary coderd instance", opts.DashboardURL) From 32397175e24924dcf7c2c023c6eb92c3e3b5be41 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 14:27:27 -0600 Subject: [PATCH 02/11] improve custom error cmd --- cli/errors.go | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/cli/errors.go b/cli/errors.go index 12567e0400ac5..6fd83fd0c42a0 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "fmt" "net/http" "net/http/httptest" @@ -65,6 +66,17 @@ func (RootCmd) errorExample() *clibase.Cmd { // A multi-error { Use: "multi-error", + Handler: func(inv *clibase.Invocation) error { + return errors.Join( + xerrors.Errorf("first error: %w", errorWithStackTrace()), + xerrors.Errorf("second error: %w", errorWithStackTrace()), + ) + + }, + }, + { + Use: "multi-multi-error", + Short: "This is a multi error inside a multi error", Handler: func(inv *clibase.Invocation) error { // Closing the stdin file descriptor will cause the next close // to fail. This is joined to the returned Command error. @@ -72,7 +84,11 @@ func (RootCmd) errorExample() *clibase.Cmd { _ = f.Close() } - return xerrors.Errorf("some error: %w", errorWithStackTrace()) + return errors.Join( + xerrors.Errorf("first error: %w", errorWithStackTrace()), + xerrors.Errorf("second error: %w", errorWithStackTrace()), + ) + }, }, From f2d978b6e0e3a751d50866a22a7979b31d646a18 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 14:31:10 -0600 Subject: [PATCH 03/11] Fix error message printing --- cli/root.go | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/cli/root.go b/cli/root.go index a2c9b6c5a9728..e4f2d53beb534 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1016,7 +1016,7 @@ type prettyErrorFormatter struct { // format formats the error to the console. This error should be human // readable. func (p *prettyErrorFormatter) format(err error) { - output := cliHumanFormatError(err, &formatOpts{ + output, _ := cliHumanFormatError(err, &formatOpts{ Verbose: p.verbose, }) // always trail with a newline @@ -1030,13 +1030,23 @@ type formatOpts struct { const indent = " " // cliHumanFormatError formats an error for the CLI. Newlines and styling are -// included. -func cliHumanFormatError(err error, opts *formatOpts) string { +// included. The second return value is true if the error is special and the error +// chain has custom formatting applied. +// +// If you change this code, you can use the cli "example-errors" tool to +// verify all errors still look ok. +// +// go run main.go exp example-error +// go run main.go exp example-error api +// go run main.go exp example-error cmd +// go run main.go exp example-error multi-error +// go run main.go exp example-error validation +func cliHumanFormatError(err error, opts *formatOpts) (string, bool) { if opts == nil { opts = &formatOpts{} } if err == nil { - return "" + return "", true } if multi, ok := err.(interface{ Unwrap() []error }); ok { @@ -1045,7 +1055,7 @@ func cliHumanFormatError(err error, opts *formatOpts) string { // Format as a single error return cliHumanFormatError(multiErrors[0], opts) } - return formatMultiError(multiErrors, opts) + return formatMultiError(multiErrors, opts), false } // First check for sentinel errors that we want to handle specially. @@ -1053,32 +1063,33 @@ func cliHumanFormatError(err error, opts *formatOpts) string { //var sdkError *codersdk.Error //if errors.As(err, &sdkError) { if sdkError, ok := err.(*codersdk.Error); ok { - return formatCoderSDKError(sdkError, opts) + return formatCoderSDKError(sdkError, opts), false } //var cmdErr *clibase.RunCommandError //if errors.As(err, &cmdErr) { if cmdErr, ok := err.(*clibase.RunCommandError); ok { - return formatRunCommandError(cmdErr, opts) + return formatRunCommandError(cmdErr, opts), false } uw, ok := err.(interface{ Unwrap() error }) if ok { - msg := cliHumanFormatError(uw.Unwrap(), opts) - if msg != "" { - return msg + msg, special := cliHumanFormatError(uw.Unwrap(), opts) + if special { + return msg, special } } - // If we got here, that means the error is not anything special. Just format - // it as is. + // If we got here, that means that the wrapped error chain does not have + // any special formatting below it. So we want to return the topmost non-special + // error (which is 'err') // Default just printing the error. Use +v for verbose to handle stack // traces of xerrors. if opts.Verbose { - return pretty.Sprint(headLineStyle(), fmt.Sprintf("%+v", err)) + return pretty.Sprint(headLineStyle(), fmt.Sprintf("%+v", err)), false } - return pretty.Sprint(headLineStyle(), fmt.Sprintf("%v", err)) + return pretty.Sprint(headLineStyle(), fmt.Sprintf("%v", err)), false } // formatMultiError formats a multi-error. It formats it as a list of errors. @@ -1092,7 +1103,8 @@ func cliHumanFormatError(err error, opts *formatOpts) string { func formatMultiError(multi []error, opts *formatOpts) string { var errorStrings []string for _, err := range multi { - errorStrings = append(errorStrings, cliHumanFormatError(err, opts)) + msg, _ := cliHumanFormatError(err, opts) + errorStrings = append(errorStrings, msg) } // Write errors out @@ -1126,7 +1138,7 @@ func formatRunCommandError(err *clibase.RunCommandError, opts *formatOpts) strin var str strings.Builder _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Encountered an error running %q", err.Cmd.FullName()))) - msgString := cliHumanFormatError(err.Err, opts) + msgString, _ := cliHumanFormatError(err.Err, opts) _, _ = str.WriteString("\n") _, _ = str.WriteString(pretty.Sprint(tailLineStyle(), msgString)) return str.String() From 1347d7f6a0628ced15b158a7acbbebc80bcd4539 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 14:45:41 -0600 Subject: [PATCH 04/11] added nohelper error --- cli/errors.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cli/errors.go b/cli/errors.go index 6fd83fd0c42a0..17468bc6c488f 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -44,6 +44,9 @@ func (RootCmd) errorExample() *clibase.Cmd { //nolint:errorlint,forcetypeassert apiError.(*codersdk.Error).Helper = "Have you tried turning it off and on again?" + apiErrorNoHelper := apiError.(*codersdk.Error) + apiErrorNoHelper.Helper = "" + // Some flags var magicWord clibase.String @@ -70,6 +73,7 @@ func (RootCmd) errorExample() *clibase.Cmd { return errors.Join( xerrors.Errorf("first error: %w", errorWithStackTrace()), xerrors.Errorf("second error: %w", errorWithStackTrace()), + apiErrorNoHelper, ) }, From 789d3df9f90f16d98209a38aa619273ef632c926 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 14:53:49 -0600 Subject: [PATCH 05/11] example with wrapped --- cli/errors.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cli/errors.go b/cli/errors.go index 17468bc6c488f..c2dc91c4b7fef 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -70,12 +70,11 @@ func (RootCmd) errorExample() *clibase.Cmd { { Use: "multi-error", Handler: func(inv *clibase.Invocation) error { - return errors.Join( + return xerrors.Errorf("wrapped %w", errors.Join( xerrors.Errorf("first error: %w", errorWithStackTrace()), xerrors.Errorf("second error: %w", errorWithStackTrace()), apiErrorNoHelper, - ) - + )) }, }, { From a84f368b3938c6a20a9c16235b9b7948c4da1dbc Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 15:26:37 -0600 Subject: [PATCH 06/11] Correct return boolean --- cli/root.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cli/root.go b/cli/root.go index e4f2d53beb534..c52ac2e3fa1e1 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1055,7 +1055,7 @@ func cliHumanFormatError(err error, opts *formatOpts) (string, bool) { // Format as a single error return cliHumanFormatError(multiErrors[0], opts) } - return formatMultiError(multiErrors, opts), false + return formatMultiError(multiErrors, opts), true } // First check for sentinel errors that we want to handle specially. @@ -1063,13 +1063,13 @@ func cliHumanFormatError(err error, opts *formatOpts) (string, bool) { //var sdkError *codersdk.Error //if errors.As(err, &sdkError) { if sdkError, ok := err.(*codersdk.Error); ok { - return formatCoderSDKError(sdkError, opts), false + return formatCoderSDKError(sdkError, opts), true } //var cmdErr *clibase.RunCommandError //if errors.As(err, &cmdErr) { if cmdErr, ok := err.(*clibase.RunCommandError); ok { - return formatRunCommandError(cmdErr, opts), false + return formatRunCommandError(cmdErr, opts), true } uw, ok := err.(interface{ Unwrap() error }) @@ -1138,9 +1138,14 @@ func formatRunCommandError(err *clibase.RunCommandError, opts *formatOpts) strin var str strings.Builder _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Encountered an error running %q", err.Cmd.FullName()))) - msgString, _ := cliHumanFormatError(err.Err, opts) + msgString, special := cliHumanFormatError(err.Err, opts) _, _ = str.WriteString("\n") - _, _ = str.WriteString(pretty.Sprint(tailLineStyle(), msgString)) + if special { + _, _ = str.WriteString(msgString) + } else { + _, _ = str.WriteString(pretty.Sprint(tailLineStyle(), msgString)) + } + return str.String() } From 96506382ef73290f42db14c37eaba049fd4b74b1 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 16:02:28 -0600 Subject: [PATCH 07/11] add tracing as well for better error debugging --- cli/errors.go | 4 +-- cli/root.go | 49 ++++++++++++++++++++++++----------- enterprise/wsproxy/wsproxy.go | 4 +-- 3 files changed, 38 insertions(+), 19 deletions(-) diff --git a/cli/errors.go b/cli/errors.go index c2dc91c4b7fef..90ffc814f6263 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -70,10 +70,10 @@ func (RootCmd) errorExample() *clibase.Cmd { { Use: "multi-error", Handler: func(inv *clibase.Invocation) error { - return xerrors.Errorf("wrapped %w", errors.Join( + return xerrors.Errorf("wrapped: %w", errors.Join( xerrors.Errorf("first error: %w", errorWithStackTrace()), xerrors.Errorf("second error: %w", errorWithStackTrace()), - apiErrorNoHelper, + xerrors.Errorf("wrapped api error: %w", apiErrorNoHelper), )) }, }, diff --git a/cli/root.go b/cli/root.go index c52ac2e3fa1e1..231aa1909c758 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1016,7 +1016,7 @@ type prettyErrorFormatter struct { // format formats the error to the console. This error should be human // readable. func (p *prettyErrorFormatter) format(err error) { - output, _ := cliHumanFormatError(err, &formatOpts{ + output, _ := cliHumanFormatError("", err, &formatOpts{ Verbose: p.verbose, }) // always trail with a newline @@ -1041,7 +1041,7 @@ const indent = " " // go run main.go exp example-error cmd // go run main.go exp example-error multi-error // go run main.go exp example-error validation -func cliHumanFormatError(err error, opts *formatOpts) (string, bool) { +func cliHumanFormatError(from string, err error, opts *formatOpts) (string, bool) { if opts == nil { opts = &formatOpts{} } @@ -1053,28 +1053,26 @@ func cliHumanFormatError(err error, opts *formatOpts) (string, bool) { multiErrors := multi.Unwrap() if len(multiErrors) == 1 { // Format as a single error - return cliHumanFormatError(multiErrors[0], opts) + return cliHumanFormatError(from, multiErrors[0], opts) } - return formatMultiError(multiErrors, opts), true + return formatMultiError(from, multiErrors, opts), true } // First check for sentinel errors that we want to handle specially. // Order does matter! We want to check for the most specific errors first. - //var sdkError *codersdk.Error - //if errors.As(err, &sdkError) { if sdkError, ok := err.(*codersdk.Error); ok { - return formatCoderSDKError(sdkError, opts), true + return formatCoderSDKError(from, sdkError, opts), true } - //var cmdErr *clibase.RunCommandError - //if errors.As(err, &cmdErr) { if cmdErr, ok := err.(*clibase.RunCommandError); ok { + // no need to pass the "from" context to this since it is always + // top level. We care about what is below this. return formatRunCommandError(cmdErr, opts), true } uw, ok := err.(interface{ Unwrap() error }) if ok { - msg, special := cliHumanFormatError(uw.Unwrap(), opts) + msg, special := cliHumanFormatError(from+traceError(err), uw.Unwrap(), opts) if special { return msg, special } @@ -1100,16 +1098,20 @@ func cliHumanFormatError(err error, opts *formatOpts) (string, bool) { // // 2. // -func formatMultiError(multi []error, opts *formatOpts) string { +func formatMultiError(from string, multi []error, opts *formatOpts) string { var errorStrings []string for _, err := range multi { - msg, _ := cliHumanFormatError(err, opts) + msg, _ := cliHumanFormatError("", err, opts) errorStrings = append(errorStrings, msg) } // Write errors out var str strings.Builder - _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("%d errors encountered:", len(multi)))) + var traceMsg string + if opts.Verbose { + traceMsg = fmt.Sprintf("Trace=[%s])", from) + } + _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("%d errors encountered: %s", len(multi), traceMsg))) for i, errStr := range errorStrings { // Indent each error errStr = strings.ReplaceAll(errStr, "\n", "\n"+indent) @@ -1138,7 +1140,7 @@ func formatRunCommandError(err *clibase.RunCommandError, opts *formatOpts) strin var str strings.Builder _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Encountered an error running %q", err.Cmd.FullName()))) - msgString, special := cliHumanFormatError(err.Err, opts) + msgString, special := cliHumanFormatError("", err.Err, opts) _, _ = str.WriteString("\n") if special { _, _ = str.WriteString(msgString) @@ -1151,11 +1153,15 @@ func formatRunCommandError(err *clibase.RunCommandError, opts *formatOpts) strin // formatCoderSDKError come from API requests. In verbose mode, add the // request debug information. -func formatCoderSDKError(err *codersdk.Error, opts *formatOpts) string { +func formatCoderSDKError(from string, err *codersdk.Error, opts *formatOpts) string { var str strings.Builder if opts.Verbose { _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("API request error to \"%s:%s\". Status code %d", err.Method(), err.URL(), err.StatusCode()))) _, _ = str.WriteString("\n") + if from != "" { + _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Trace=[%s]", from))) + _, _ = str.WriteString("\n") + } } _, _ = str.WriteString(pretty.Sprint(headLineStyle(), err.Message)) @@ -1171,6 +1177,19 @@ func formatCoderSDKError(err *codersdk.Error, opts *formatOpts) string { return str.String() } +// traceError is a helper function that aides developers debugging failed cli +// commands. When we pretty print errors, we lose the context in which they came. +// This function adds the context back. Unfortunately there is no easy way to get +// the prefix to: "error string: %w", so we do a bit of string manipulation. +func traceError(err error) string { + if uw, ok := err.(interface{ Unwrap() error }); ok { + a, b := err.Error(), uw.Unwrap().Error() + c := strings.TrimSuffix(a, b) + return c + } + return err.Error() +} + // These styles are arbitrary. func headLineStyle() pretty.Style { return cliui.DefaultStyles.Error diff --git a/enterprise/wsproxy/wsproxy.go b/enterprise/wsproxy/wsproxy.go index 052d5a21d0e8a..438ed664d2bc2 100644 --- a/enterprise/wsproxy/wsproxy.go +++ b/enterprise/wsproxy/wsproxy.go @@ -158,10 +158,10 @@ func New(ctx context.Context, opts *Options) (*Server, error) { // TODO: Probably do some version checking here info, err := client.SDKClient.BuildInfo(ctx) if err != nil { - return nil, errors.Join( + return nil, fmt.Errorf("buildinfo: %w", errors.Join( fmt.Errorf("unable to fetch build info from primary coderd. Are you sure %q is a coderd instance?", opts.DashboardURL), err, - ) + )) } if info.WorkspaceProxy { return nil, xerrors.Errorf("%q is a workspace proxy, not a primary coderd instance", opts.DashboardURL) From 865477ba044c40aa0e2b48894a9dc9a6942fb2fd Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 16:03:21 -0600 Subject: [PATCH 08/11] linting --- cli/errors.go | 1 + cli/root.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/cli/errors.go b/cli/errors.go index 90ffc814f6263..762bb485059d2 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -44,6 +44,7 @@ func (RootCmd) errorExample() *clibase.Cmd { //nolint:errorlint,forcetypeassert apiError.(*codersdk.Error).Helper = "Have you tried turning it off and on again?" + //nolint:errorlint,forcetypeassert apiErrorNoHelper := apiError.(*codersdk.Error) apiErrorNoHelper.Helper = "" diff --git a/cli/root.go b/cli/root.go index 231aa1909c758..11177405afa24 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1041,6 +1041,8 @@ const indent = " " // go run main.go exp example-error cmd // go run main.go exp example-error multi-error // go run main.go exp example-error validation +// +//nolint:errorlint func cliHumanFormatError(from string, err error, opts *formatOpts) (string, bool) { if opts == nil { opts = &formatOpts{} From 4066e2c670110e3e5098a0f2e687a5bf32ca186c Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 16:08:35 -0600 Subject: [PATCH 09/11] linting --- cli/errors.go | 1 - cli/root.go | 2 ++ codersdk/deployment.go | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/errors.go b/cli/errors.go index 762bb485059d2..ee12ca036af24 100644 --- a/cli/errors.go +++ b/cli/errors.go @@ -92,7 +92,6 @@ func (RootCmd) errorExample() *clibase.Cmd { xerrors.Errorf("first error: %w", errorWithStackTrace()), xerrors.Errorf("second error: %w", errorWithStackTrace()), ) - }, }, diff --git a/cli/root.go b/cli/root.go index 11177405afa24..6abf84c2a5a57 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1183,6 +1183,8 @@ func formatCoderSDKError(from string, err *codersdk.Error, opts *formatOpts) str // commands. When we pretty print errors, we lose the context in which they came. // This function adds the context back. Unfortunately there is no easy way to get // the prefix to: "error string: %w", so we do a bit of string manipulation. +// +//nolint:errorlint func traceError(err error) string { if uw, ok := err.(interface{ Unwrap() error }); ok { a, b := err.Error(), uw.Unwrap().Error() diff --git a/codersdk/deployment.go b/codersdk/deployment.go index 5559454dba051..0f210bacd2d36 100644 --- a/codersdk/deployment.go +++ b/codersdk/deployment.go @@ -2065,7 +2065,7 @@ func (c *Client) BuildInfo(ctx context.Context) (BuildInfoResponse, error) { } defer res.Body.Close() - if res.StatusCode != http.StatusBadRequest || ExpectJSONMime(res) != nil { + if res.StatusCode != http.StatusOK || ExpectJSONMime(res) != nil { return BuildInfoResponse{}, ReadBodyAsError(res) } From 2ac5c176f127b5eb31f0506c97ab5c96a7db36e7 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 16:27:19 -0600 Subject: [PATCH 10/11] always print traec --- cli/root.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cli/root.go b/cli/root.go index 6abf84c2a5a57..a5bccc1ba55ea 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1109,10 +1109,7 @@ func formatMultiError(from string, multi []error, opts *formatOpts) string { // Write errors out var str strings.Builder - var traceMsg string - if opts.Verbose { - traceMsg = fmt.Sprintf("Trace=[%s])", from) - } + traceMsg := fmt.Sprintf("Trace=[%s])", from) _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("%d errors encountered: %s", len(multi), traceMsg))) for i, errStr := range errorStrings { // Indent each error @@ -1160,11 +1157,10 @@ func formatCoderSDKError(from string, err *codersdk.Error, opts *formatOpts) str if opts.Verbose { _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("API request error to \"%s:%s\". Status code %d", err.Method(), err.URL(), err.StatusCode()))) _, _ = str.WriteString("\n") - if from != "" { - _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Trace=[%s]", from))) - _, _ = str.WriteString("\n") - } } + // Always include this trace. Users can ignore this. + _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Trace=[%s]", from))) + _, _ = str.WriteString("\n") _, _ = str.WriteString(pretty.Sprint(headLineStyle(), err.Message)) if err.Helper != "" { From d754fd8a30e566dc21ed8ac34647b51a2265ad66 Mon Sep 17 00:00:00 2001 From: Steven Masley Date: Thu, 11 Jan 2024 16:29:26 -0600 Subject: [PATCH 11/11] conditional, do not print empty string --- cli/root.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/cli/root.go b/cli/root.go index a5bccc1ba55ea..8b8784735d96d 100644 --- a/cli/root.go +++ b/cli/root.go @@ -1109,7 +1109,10 @@ func formatMultiError(from string, multi []error, opts *formatOpts) string { // Write errors out var str strings.Builder - traceMsg := fmt.Sprintf("Trace=[%s])", from) + var traceMsg string + if from != "" { + traceMsg = fmt.Sprintf("Trace=[%s])", from) + } _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("%d errors encountered: %s", len(multi), traceMsg))) for i, errStr := range errorStrings { // Indent each error @@ -1159,8 +1162,10 @@ func formatCoderSDKError(from string, err *codersdk.Error, opts *formatOpts) str _, _ = str.WriteString("\n") } // Always include this trace. Users can ignore this. - _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Trace=[%s]", from))) - _, _ = str.WriteString("\n") + if from != "" { + _, _ = str.WriteString(pretty.Sprint(headLineStyle(), fmt.Sprintf("Trace=[%s]", from))) + _, _ = str.WriteString("\n") + } _, _ = str.WriteString(pretty.Sprint(headLineStyle(), err.Message)) if err.Helper != "" {