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 != "" { pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy