From 8203968ed9d4d244e87eb9f7eae3446cea4ae8d3 Mon Sep 17 00:00:00 2001 From: Brandon Croft Date: Tue, 14 Apr 2026 16:41:15 -0600 Subject: [PATCH 1/3] global error codes 0 = OK 1 = Usage error 2 = Not Found error (or not authorized) 3 = Authentication error 4 = Network error 5 = API server error (after retries failed) --- README.md | 11 ++++ go.mod | 12 +---- go.sum | 53 +------------------ internal/pkg/cmd/command_internal.go | 77 +++++++++++++++++++--------- internal/pkg/cmd/command_test.go | 43 ++++++++++++++++ 5 files changed, 109 insertions(+), 87 deletions(-) diff --git a/README.md b/README.md index 550208e..609b7ee 100644 --- a/README.md +++ b/README.md @@ -83,3 +83,14 @@ Perform an API request. See `tfcloud api --help` for usage and examples. **`tfcloud variable import [tfvars-file] [flags]`** Import variables from a tfvars file or the process environment into the current workspace or a variable set. See `tfcloud variable import --help` for usage and examples. + +#### Exit Codes + +| Exit | Meaning | Solution | +|------|----------------------------------|-----------------------------| +| 0 | OK | &emdash; | +| 1 | Usage error | Read `tfcloud --help` | +| 2 | Not Found or Authorization Error | Verify URL/ID | +| 3 | Authentication Error | `tfcloud auth login` | +| 4 | Network error | Check connectivity | +| 5 | API Server Error Persists | Try again later | diff --git a/go.mod b/go.mod index 93270bf..ca94e8c 100644 --- a/go.mod +++ b/go.mod @@ -5,11 +5,10 @@ go 1.25.5 require ( github.com/MakeNowJust/heredoc/v2 v2.0.1 github.com/dustin/go-humanize v1.0.1 - github.com/go-openapi/runtime v0.29.3 github.com/hashicorp/cli v1.1.7 github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-multierror v1.1.1 - github.com/hashicorp/go-tfe v1.78.1-0.20260401171829-7a49f0cf5cb4 + github.com/hashicorp/go-tfe v1.78.1-0.20260414223040-554d4064682a github.com/hashicorp/go-version v1.9.0 github.com/hashicorp/hcl/v2 v2.24.0 github.com/lithammer/dedent v1.1.0 @@ -43,14 +42,6 @@ require ( github.com/fatih/color v1.18.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/errors v0.22.7 // indirect - github.com/go-openapi/strfmt v0.26.0 // indirect - github.com/go-openapi/swag/conv v0.25.5 // indirect - github.com/go-openapi/swag/fileutils v0.25.5 // indirect - github.com/go-openapi/swag/jsonutils v0.25.5 // indirect - github.com/go-openapi/swag/stringutils v0.25.5 // indirect - github.com/go-openapi/swag/typeutils v0.25.5 // indirect - github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect @@ -67,7 +58,6 @@ require ( github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/oklog/ulid/v2 v2.1.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/shopspring/decimal v1.4.0 // indirect diff --git a/go.sum b/go.sum index 2e7b633..045509c 100644 --- a/go.sum +++ b/go.sum @@ -36,52 +36,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.24.3 h1:a1hrvMr8X0Xt69KP5uVTu5jH62DscmDifrLzNglAayk= -github.com/go-openapi/analysis v0.24.3/go.mod h1:Nc+dWJ/FxZbhSow5Yh3ozg5CLJioB+XXT6MdLvJUsUw= -github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA= -github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w= -github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= -github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= -github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= -github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= -github.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ= -github.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA= -github.com/go-openapi/runtime v0.29.3 h1:h5twGaEqxtQg40ePiYm9vFFH1q06Czd7Ot6ufdK0w/Y= -github.com/go-openapi/runtime v0.29.3/go.mod h1:8A1W0/L5eyNJvKciqZtvIVQvYO66NlB7INMSZ9bw/oI= -github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ= -github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ= -github.com/go-openapi/strfmt v0.26.0 h1:SDdQLyOEqu8W96rO1FRG1fuCtVyzmukky0zcD6gMGLU= -github.com/go-openapi/strfmt v0.26.0/go.mod h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y= -github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= -github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= -github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk= -github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc= -github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= -github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= -github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= -github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= -github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= -github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= -github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= -github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY= -github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= -github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= -github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= -github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= -github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= -github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.1 h1:NZOrZmIb6PTv5LTFxr5/mKV/FjbUzGE7E6gLz7vFoOQ= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.1/go.mod h1:r7dwsujEHawapMsxA69i+XMGZrQ5tRauhLAjV/sxg3Q= -github.com/go-openapi/testify/v2 v2.4.1 h1:zB34HDKj4tHwyUQHrUkpV0Q0iXQ6dUCOQtIqn8hE6Iw= -github.com/go-openapi/testify/v2 v2.4.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= -github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0= -github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY= github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= -github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= -github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -96,8 +52,8 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-tfe v1.78.1-0.20260401171829-7a49f0cf5cb4 h1:+jBVHPQheF/zwa5Yi2A7zby+2jl6zV+IiteiOwA36ls= -github.com/hashicorp/go-tfe v1.78.1-0.20260401171829-7a49f0cf5cb4/go.mod h1:NCc9n8HN05g6Bu5v0a3JhkOoWN42DF/Jk0nDQSaZwFI= +github.com/hashicorp/go-tfe v1.78.1-0.20260414223040-554d4064682a h1:K5t3L7CDC3gtbLI28Zwlb8DwBn0+ojUEKVBmzZhQ0ck= +github.com/hashicorp/go-tfe v1.78.1-0.20260414223040-554d4064682a/go.mod h1:NCc9n8HN05g6Bu5v0a3JhkOoWN42DF/Jk0nDQSaZwFI= github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE= @@ -152,9 +108,6 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= -github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= -github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= -github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -191,8 +144,6 @@ go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWv go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo= diff --git a/internal/pkg/cmd/command_internal.go b/internal/pkg/cmd/command_internal.go index 7543c93..578b46d 100644 --- a/internal/pkg/cmd/command_internal.go +++ b/internal/pkg/cmd/command_internal.go @@ -8,14 +8,15 @@ import ( "errors" "fmt" "io" + "net" "net/http" "os" "slices" "strconv" "strings" - "github.com/go-openapi/runtime" "github.com/hashicorp/cli" + "github.com/hashicorp/go-tfe" "github.com/muesli/reflow/indent" "github.com/muesli/reflow/wordwrap" "github.com/posener/complete" @@ -26,21 +27,57 @@ import ( "github.com/hashicorp/tfcloud/internal/pkg/ld" ) +func (c *Command) errorToExitCode(args []string, err error) int { + io := c.io + cs := io.ColorScheme() + + exitCode := 1 + var exitCodeErr *ExitCodeError + var netErr *net.OpError + var apiErr *tfe.APIError + + if errors.Is(err, ErrDisplayHelp) { + return cli.RunResultHelp + } else if errors.Is(err, ErrDisplayUsage) { + fmt.Fprint(io.Err(), c.usageHelp()) + return 1 + } else if errors.Is(err, tfe.ErrNotFound) { + fmt.Fprintf(io.Err(), "%s %s\n\n", cs.ErrorLabel(), notFoundErrorHelp(io)) + return 2 + } else if errors.Is(err, tfe.ErrUnauthorized) { + fmt.Fprintf(io.Err(), "%s %s\n\n", cs.ErrorLabel(), authErrorHelp(io, c.commandPath(), args)) + return 3 + } else if errors.As(err, &netErr) { + fmt.Fprintf(io.Err(), "%s Network error: %s\n", cs.ErrorLabel(), netErr) + return 4 + } else if errors.As(err, &apiErr) { + if apiErr.StatusCode >= http.StatusInternalServerError { + fmt.Fprintf(io.Err(), "%s Server error: %s\n", cs.ErrorLabel(), apiErr) + return 5 + } + } else if errors.As(err, &exitCodeErr) { + exitCode = exitCodeErr.Code + } + + fmt.Fprintf(io.Err(), "%s %s\n", cs.ErrorLabel(), wordWrap(err.Error(), 120)) + return exitCode +} + // Run runs the given command. func (c *Command) Run(args []string) int { + // Get the colorscheme + io := c.getIO() + cs := c.getIO().ColorScheme() + if c.RunF == nil { if len(c.children) != 0 { return cli.RunResultHelp } - fmt.Println("Command has no run function or children. This is an invalid command") + fmt.Fprintln(io.Err(), "Command has no run function or children. This is an invalid command") return 1 } - // Get the colorscheme - io := c.getIO() - cs := c.getIO().ColorScheme() - // Parse the flags if err := c.parseFlags(args); err != nil { fmt.Fprintf(io.Err(), "%s %s\n", cs.ErrorLabel(), err) @@ -101,30 +138,20 @@ func (c *Command) Run(args []string) int { // Run the command if err := c.RunF(c, parsedArgs); err != nil { - exitCode := 1 - var runtimeErr runtime.ClientResponseStatus - var exitCodeErr *ExitCodeError - if errors.Is(err, ErrDisplayHelp) { - return cli.RunResultHelp - } else if errors.Is(err, ErrDisplayUsage) { - fmt.Fprint(io.Err(), c.usageHelp()) - return 1 - } else if errors.As(err, &runtimeErr) && runtimeErr.IsCode(http.StatusUnauthorized) { - // TODO: This runtimeErr is inaccurate for HCPTF - // Request failed because of authentication issues. - fmt.Fprintf(io.Err(), "%s %s\n\n", cs.ErrorLabel(), authErrorHelp(io, c.commandPath(), args)) - return 1 - } else if errors.As(err, &exitCodeErr) { - exitCode = exitCodeErr.Code - } - - fmt.Fprintf(io.Err(), "%s %s\n", cs.ErrorLabel(), wordWrap(err.Error(), 120)) - return exitCode + return c.errorToExitCode(args, err) } return 0 } +func notFoundErrorHelp(io iostreams.IOStreams) string { + return heredoc.New(io, heredoc.WithPreserveNewlines(), heredoc.WithWidth(0)).Must(` + Resource not found or you are unauthorized to this action. Check your account permissions. + + {{ Bold "$ tfcloud auth info" }} + `) +} + // authErrorHelp returns a help message for recovering from authentication errors. func authErrorHelp(io iostreams.IOStreams, commandPath string, args []string) string { // Build the original command diff --git a/internal/pkg/cmd/command_test.go b/internal/pkg/cmd/command_test.go index e69fd92..24c7d43 100644 --- a/internal/pkg/cmd/command_test.go +++ b/internal/pkg/cmd/command_test.go @@ -5,11 +5,14 @@ package cmd import ( "fmt" + "net" "testing" "github.com/spf13/pflag" "github.com/stretchr/testify/require" + "github.com/hashicorp/cli" + "github.com/hashicorp/go-tfe" "github.com/hashicorp/tfcloud/internal/pkg/iostreams" ) @@ -132,3 +135,43 @@ func TestCommand_ExitCode(t *testing.T) { r.Equal(code, root.Run([]string{})) r.Contains(io.Error.String(), err.Error()) } + +func TestCommand_GlobalExitCode(t *testing.T) { + t.Parallel() + + opErr := &net.OpError{Err: fmt.Errorf("some network error")} + + tests := []struct { + err error + expected int + errContains string + }{ + {err: ErrDisplayHelp, expected: cli.RunResultHelp}, + {err: ErrDisplayUsage, expected: 1}, + {err: tfe.ErrNotFound, expected: 2, errContains: "Resource not found or you are unauthorized to this action"}, + {err: tfe.ErrUnauthorized, expected: 3, errContains: "tfcloud auth login"}, + {err: opErr, expected: 4, errContains: "network error"}, + {err: tfe.ErrInternalServer, expected: 5, errContains: "Internal Server Error"}, + {err: fmt.Errorf("some other error"), expected: 1, errContains: "ERROR: some other error"}, + } + + for _, tt := range tests { + t.Run(fmt.Sprintf("err %T exits with code %d", tt.err, tt.expected), func(t *testing.T) { + r := require.New(t) + + // Create the command tree + io := iostreams.Test() + root := &Command{ + Name: "root", + io: io, + RunF: func(c *Command, args []string) error { + return tt.err + }, + } + r.Equal(tt.expected, root.Run([]string{})) + if tt.errContains != "" { + r.Contains(io.Error.String(), tt.errContains) + } + }) + } +} From 0a72323023b6c53086f52de416104d8945953bde Mon Sep 17 00:00:00 2001 From: Brandon Croft Date: Tue, 14 Apr 2026 16:43:34 -0600 Subject: [PATCH 2/3] Update command_test.go --- internal/pkg/cmd/command_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/pkg/cmd/command_test.go b/internal/pkg/cmd/command_test.go index 24c7d43..c97a7fb 100644 --- a/internal/pkg/cmd/command_test.go +++ b/internal/pkg/cmd/command_test.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/cli" "github.com/hashicorp/go-tfe" + "github.com/hashicorp/tfcloud/internal/pkg/iostreams" ) From 984c37564b21ff9dfd5491447b8424980654699a Mon Sep 17 00:00:00 2001 From: Brandon Croft Date: Tue, 14 Apr 2026 17:02:43 -0600 Subject: [PATCH 3/3] fix "mdash" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 609b7ee..8e9a52b 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,7 @@ Import variables from a tfvars file or the process environment into the current | Exit | Meaning | Solution | |------|----------------------------------|-----------------------------| -| 0 | OK | &emdash; | +| 0 | OK | — | | 1 | Usage error | Read `tfcloud --help` | | 2 | Not Found or Authorization Error | Verify URL/ID | | 3 | Authentication Error | `tfcloud auth login` |