From a098d39c77b9f818a526de7c7445ceea7e05ba01 Mon Sep 17 00:00:00 2001 From: Bo-Yi Wu Date: Tue, 24 Feb 2026 17:01:53 +0800 Subject: [PATCH] refactor: modernize codebase with idiomatic Go patterns and linter updates - Update linter configuration with new linters and settings, and remove deprecated ones - Add detailed linter rules and formatter settings to the configuration - Change error creation from fmt.Errorf to errors.New throughout the codebase - Replace map[string]interface{} with map[string]any for improved type clarity - Replace for loops of the form for i := 0; i < n; i++ with for i := range n for better idiomatic Go - Simplify timer and polling interval calculations using min/max functions - Use strconv.Itoa instead of fmt.Sprintf for integer to string conversion - Use t.Setenv in tests to manage environment variables instead of os.Setenv - Remove manual environment restoration in tests for improved reliability - Concatenate strings directly instead of using fmt.Sprintf where possible - Minor improvements to test comments for clarity Signed-off-by: Bo-Yi Wu --- .golangci.yml | 109 ++++++++++++++++++++++++++++++++-------- Makefile | 2 +- browser_flow.go | 6 +-- callback.go | 3 +- callback_test.go | 20 ++++---- config.go | 8 +-- device_flow.go | 32 ++++++------ filelock.go | 2 +- filelock_test.go | 2 +- main_test.go | 8 +-- pkce_test.go | 4 +- polling_test.go | 6 +-- tokens.go | 7 +-- tui/browser_view.go | 4 +- tui/components/timer.go | 12 ++--- tui/device_model.go | 6 +-- tui/device_view.go | 2 +- tui/manager.go | 2 +- tui/manager_test.go | 22 +------- tui/messages.go | 10 ++-- userinfo.go | 3 +- 21 files changed, 161 insertions(+), 109 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index a4a6a77..2206dac 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,29 +1,93 @@ version: "2" +output: + sort-order: + - file linters: default: none enable: + - bidichk - bodyclose - - dogsled - - dupl + - depguard - errcheck - - exhaustive - - goconst + - forbidigo + - gocheckcompilerdirectives - gocritic - - gocyclo - - goprintffuncname - - gosec - govet - ineffassign - - misspell + - mirror + - modernize - nakedret - - noctx + - nilnil - nolintlint - - rowserrcheck + - perfsprint + - revive - staticcheck + - testifylint - unconvert - unparam - unused - - whitespace + - usestdlibvars + - usetesting + - wastedassign + settings: + depguard: + rules: + main: + deny: + - pkg: io/ioutil + desc: use os or io instead + - pkg: golang.org/x/exp + desc: it's experimental and unreliable + - pkg: github.com/pkg/errors + desc: use builtin errors package instead + nolintlint: + allow-unused: false + require-explanation: true + require-specific: true + gocritic: + enabled-checks: + - equalFold + disabled-checks: [] + revive: + severity: error + rules: + - name: blank-imports + - name: constant-logical-expr + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: empty-lines + - name: error-return + - name: error-strings + - name: exported + - name: identical-branches + - name: if-return + - name: increment-decrement + - name: modifies-value-receiver + - name: package-comments + - name: redefines-builtin-id + - name: superfluous-else + - name: time-naming + - name: unexported-return + - name: var-declaration + - name: var-naming + disabled: true + staticcheck: + checks: + - all + testifylint: {} + usetesting: + os-temp-dir: true + forbidigo: + forbid: + - pattern: "^(print|println)$" + msg: "use fmt.Print* instead of built-in print/println" + perfsprint: + concat-loop: false + govet: + enable: + - nilness + - unusedwrite exclusions: generated: lax presets: @@ -31,19 +95,24 @@ linters: - common-false-positives - legacy - std-error-handling - paths: - - third_party$ - - builtin$ - - examples$ + rules: + - linters: + - errcheck + - staticcheck + - unparam + path: _test\.go +issues: + max-issues-per-linter: 0 + max-same-issues: 0 formatters: enable: - gofmt - gofumpt - - goimports - golines + settings: + gofumpt: + extra-rules: true exclusions: generated: lax - paths: - - third_party$ - - builtin$ - - examples$ +run: + timeout: 10m diff --git a/Makefile b/Makefile index 7caade9..0bbfbbb 100644 --- a/Makefile +++ b/Makefile @@ -56,7 +56,7 @@ fmt: install-golangci-lint ## lint: run golangci-lint to check for issues lint: install-golangci-lint - golangci-lint run + unset GOROOT && golangci-lint run ## build_linux_amd64: build the authgate binary for linux amd64 build_linux_amd64: generate diff --git a/browser_flow.go b/browser_flow.go index 866c74c..17c6180 100644 --- a/browser_flow.go +++ b/browser_flow.go @@ -132,7 +132,7 @@ func performBrowserFlowWithUpdates( Step: 1, TotalSteps: 3, Message: "Opening browser", - Data: map[string]interface{}{ + Data: map[string]any{ "url": authURL, }, } @@ -152,7 +152,7 @@ func performBrowserFlowWithUpdates( Step: 2, TotalSteps: 3, Message: "Waiting for callback", - Data: map[string]interface{}{ + Data: map[string]any{ "port": callbackPort, }, } @@ -179,7 +179,7 @@ func performBrowserFlowWithUpdates( updates <- tui.FlowUpdate{ Type: tui.TimerTick, Progress: progress, - Data: map[string]interface{}{ + Data: map[string]any{ "elapsed": elapsed, "timeout": callbackTimeout, }, diff --git a/callback.go b/callback.go index e13b18e..162bad6 100644 --- a/callback.go +++ b/callback.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "fmt" "html" "net" @@ -18,7 +19,7 @@ const ( // ErrCallbackTimeout is returned when no browser callback is received within callbackTimeout. // Callers can use errors.Is to distinguish a timeout from other authorization errors // and decide whether to fall back to Device Code Flow. -var ErrCallbackTimeout = fmt.Errorf("browser authorization timed out") +var ErrCallbackTimeout = errors.New("browser authorization timed out") // callbackResult holds the outcome of the local callback round-trip. type callbackResult struct { diff --git a/callback_test.go b/callback_test.go index 9541829..755d9f2 100644 --- a/callback_test.go +++ b/callback_test.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "fmt" "io" "net/http" @@ -18,7 +19,8 @@ type callbackServerResult struct { // startCallbackServerAsync starts the callback server in a goroutine and // returns a channel that will receive the result (storage or error). func startCallbackServerAsync( - t *testing.T, ctx context.Context, port int, state string, + t *testing.T, ctx context.Context, //nolint:revive // t before ctx in test helpers + port int, state string, exchangeFn func(context.Context, string) (*TokenStorage, error), ) chan callbackServerResult { t.Helper() @@ -37,7 +39,7 @@ func noExchangeFn(t *testing.T) func(context.Context, string) (*TokenStorage, er t.Helper() return func(_ context.Context, _ string) (*TokenStorage, error) { t.Error("exchangeFn should not be called") - return nil, fmt.Errorf("should not be called") + return nil, errors.New("should not be called") } } @@ -68,7 +70,7 @@ func TestCallbackServer_Success(t *testing.T) { "http://127.0.0.1:%d/callback?code=mycode123&state=%s", port, state, ) - resp, err := http.Get(callbackURL) //nolint:noctx,gosec + resp, err := http.Get(callbackURL) //nolint:noctx,gosec // test-only HTTP call to local server if err != nil { t.Fatalf("GET callback failed: %v", err) } @@ -105,7 +107,7 @@ func TestCallbackServer_StateMismatch(t *testing.T) { "http://127.0.0.1:%d/callback?code=mycode&state=wrong-state", port, ) - resp, err := http.Get(callbackURL) //nolint:noctx,gosec + resp, err := http.Get(callbackURL) //nolint:noctx,gosec // test-only HTTP call to local server if err != nil { t.Fatalf("GET callback failed: %v", err) } @@ -136,7 +138,7 @@ func TestCallbackServer_OAuthError(t *testing.T) { "http://127.0.0.1:%d/callback?error=access_denied&error_description=User+denied&state=%s", port, state, ) - resp, err := http.Get(callbackURL) //nolint:noctx,gosec + resp, err := http.Get(callbackURL) //nolint:noctx,gosec // test-only HTTP call to local server if err != nil { t.Fatalf("GET callback failed: %v", err) } @@ -166,14 +168,14 @@ func TestCallbackServer_ExchangeFailure(t *testing.T) { ch := startCallbackServerAsync(t, context.Background(), port, state, func(_ context.Context, _ string) (*TokenStorage, error) { - return nil, fmt.Errorf("unauthorized_client: unauthorized_client") + return nil, errors.New("unauthorized_client: unauthorized_client") }) callbackURL := fmt.Sprintf( "http://127.0.0.1:%d/callback?code=mycode&state=%s", port, state, ) - resp, err := http.Get(callbackURL) //nolint:noctx,gosec + resp, err := http.Get(callbackURL) //nolint:noctx,gosec // test-only HTTP call to local server if err != nil { t.Fatalf("GET callback failed: %v", err) } @@ -208,7 +210,7 @@ func TestCallbackServer_DoubleCallback(t *testing.T) { done := make(chan error, 2) for range 2 { go func() { - resp, err := http.Get(url) //nolint:noctx,gosec + resp, err := http.Get(url) //nolint:noctx,gosec // test-only HTTP call to local server if err == nil { resp.Body.Close() } @@ -247,7 +249,7 @@ func TestCallbackServer_MissingCode(t *testing.T) { "http://127.0.0.1:%d/callback?state=%s", port, state, ) - resp, err := http.Get(callbackURL) //nolint:noctx,gosec + resp, err := http.Get(callbackURL) //nolint:noctx,gosec // test-only HTTP call to local server if err != nil { t.Fatalf("GET callback failed: %v", err) } diff --git a/config.go b/config.go index 8def319..b93ba7a 100644 --- a/config.go +++ b/config.go @@ -2,11 +2,13 @@ package main import ( "crypto/tls" + "errors" "flag" "fmt" "net/http" "net/url" "os" + "strconv" "strings" "time" @@ -107,7 +109,7 @@ func initConfig() { // Resolve callback port (int flag needs special handling). portStr := "" if *flagCallbackPort != 0 { - portStr = fmt.Sprintf("%d", *flagCallbackPort) + portStr = strconv.Itoa(*flagCallbackPort) } portStr = getConfig(portStr, "CALLBACK_PORT", "8888") if _, err := fmt.Sscanf(portStr, "%d", &callbackPort); err != nil || callbackPort <= 0 { @@ -185,7 +187,7 @@ func getEnv(key, defaultValue string) string { func validateServerURL(rawURL string) error { if rawURL == "" { - return fmt.Errorf("server URL cannot be empty") + return errors.New("server URL cannot be empty") } u, err := url.Parse(rawURL) if err != nil { @@ -195,7 +197,7 @@ func validateServerURL(rawURL string) error { return fmt.Errorf("URL scheme must be http or https, got: %s", u.Scheme) } if u.Host == "" { - return fmt.Errorf("URL must include a host") + return errors.New("URL must include a host") } return nil } diff --git a/device_flow.go b/device_flow.go index 7f8344d..b02c91c 100644 --- a/device_flow.go +++ b/device_flow.go @@ -117,21 +117,21 @@ func pollForTokenWithProgress( case "slow_down": backoffMultiplier *= 1.5 - newInterval := time.Duration(float64(pollInterval) * backoffMultiplier) - if newInterval > 60*time.Second { - newInterval = 60 * time.Second - } + newInterval := min( + time.Duration(float64(pollInterval)*backoffMultiplier), + 60*time.Second, + ) pollInterval = newInterval pollTicker.Reset(pollInterval) continue case "expired_token": fmt.Println() - return nil, fmt.Errorf("device code expired, please restart the flow") + return nil, errors.New("device code expired, please restart the flow") case "access_denied": fmt.Println() - return nil, fmt.Errorf("user denied authorization") + return nil, errors.New("user denied authorization") default: fmt.Println() @@ -253,7 +253,7 @@ func performDeviceFlowWithUpdates( Type: tui.DeviceCodeReceived, Step: 1, TotalSteps: 2, - Data: map[string]interface{}{ + Data: map[string]any{ "user_code": deviceAuth.UserCode, "verification_uri": deviceAuth.VerificationURI, "verification_uri_complete": deviceAuth.VerificationURIComplete, @@ -331,7 +331,7 @@ func pollForTokenWithUpdates( updates <- tui.FlowUpdate{ Type: tui.PollingUpdate, Message: "Polling authorization server", - Data: map[string]interface{}{ + Data: map[string]any{ "poll_count": pollCount, "interval": pollInterval, "elapsed": time.Since(startTime), @@ -356,17 +356,17 @@ func pollForTokenWithUpdates( case "slow_down": oldInterval := pollInterval backoffMultiplier *= 1.5 - newInterval := time.Duration(float64(pollInterval) * backoffMultiplier) - if newInterval > 60*time.Second { - newInterval = 60 * time.Second - } + newInterval := min( + time.Duration(float64(pollInterval)*backoffMultiplier), + 60*time.Second, + ) pollInterval = newInterval pollTicker.Reset(pollInterval) updates <- tui.FlowUpdate{ Type: tui.BackoffChanged, Message: "Server requested slower polling", - Data: map[string]interface{}{ + Data: map[string]any{ "old_interval": oldInterval, "new_interval": newInterval, }, @@ -374,10 +374,10 @@ func pollForTokenWithUpdates( continue case "expired_token": - return nil, fmt.Errorf("device code expired, please restart the flow") + return nil, errors.New("device code expired, please restart the flow") case "access_denied": - return nil, fmt.Errorf("user denied authorization") + return nil, errors.New("user denied authorization") default: return nil, fmt.Errorf( @@ -396,7 +396,7 @@ func pollForTokenWithUpdates( case <-uiTicker.C: updates <- tui.FlowUpdate{ Type: tui.TimerTick, - Data: map[string]interface{}{ + Data: map[string]any{ "elapsed": time.Since(startTime), }, } diff --git a/filelock.go b/filelock.go index 3ad0888..a17d768 100644 --- a/filelock.go +++ b/filelock.go @@ -19,7 +19,7 @@ func acquireFileLock(filePath string) (*fileLock, error) { maxRetries := 50 retryDelay := 100 * time.Millisecond - for i := 0; i < maxRetries; i++ { + for range maxRetries { lockFile, err := os.OpenFile(lockPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0o600) if err == nil { fmt.Fprintf(lockFile, "%d", os.Getpid()) diff --git a/filelock_test.go b/filelock_test.go index 4425188..3305c82 100644 --- a/filelock_test.go +++ b/filelock_test.go @@ -40,7 +40,7 @@ func TestConcurrentLocks(t *testing.T) { var mu sync.Mutex concurrent := 0 - for i := 0; i < goroutines; i++ { + for i := range goroutines { wg.Add(1) go func(idx int) { defer wg.Done() diff --git a/main_test.go b/main_test.go index 72e7346..222d9fa 100644 --- a/main_test.go +++ b/main_test.go @@ -25,7 +25,7 @@ func init() { clientID = "test-client" } if tokenFile == "" { - tokenFile = ".authgate-tokens.json" //nolint:gosec + tokenFile = ".authgate-tokens.json" //nolint:gosec // test uses a known-safe filename constant } if scope == "" { scope = "read write" @@ -223,7 +223,7 @@ func TestSaveTokens_ConcurrentWrites(t *testing.T) { const goroutines = 10 var wg sync.WaitGroup wg.Add(goroutines) - for i := 0; i < goroutines; i++ { + for i := range goroutines { go func(id int) { defer wg.Done() if err := saveTokens(&TokenStorage{ @@ -343,7 +343,7 @@ func TestRefreshAccessToken_RotationMode(t *testing.T) { http.Error(w, "bad form", http.StatusBadRequest) return } - resp := map[string]interface{}{ + resp := map[string]any{ "access_token": "new-access-token", "token_type": "Bearer", "expires_in": 3600, @@ -400,7 +400,7 @@ func TestRequestDeviceCode_WithRetry(t *testing.T) { return } w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(map[string]interface{}{ + if err := json.NewEncoder(w).Encode(map[string]any{ "device_code": "test-device-code", "user_code": "TEST-CODE", "verification_uri": testServer.URL + "/device", diff --git a/pkce_test.go b/pkce_test.go index 51efc29..719ac1a 100644 --- a/pkce_test.go +++ b/pkce_test.go @@ -80,7 +80,7 @@ func TestGeneratePKCE_Uniqueness(t *testing.T) { const iterations = 100 seen := make(map[string]bool, iterations) - for i := 0; i < iterations; i++ { + for i := range iterations { p, err := GeneratePKCE() if err != nil { t.Fatalf("GeneratePKCE() error on iteration %d: %v", i, err) @@ -105,7 +105,7 @@ func TestGenerateState_Length(t *testing.T) { func TestGenerateState_Uniqueness(t *testing.T) { const iterations = 50 seen := make(map[string]bool, iterations) - for i := 0; i < iterations; i++ { + for i := range iterations { s, err := generateState() if err != nil { t.Fatalf("generateState() error: %v", err) diff --git a/polling_test.go b/polling_test.go index 70c6c06..ac14ff7 100644 --- a/polling_test.go +++ b/polling_test.go @@ -33,7 +33,7 @@ func TestPollForToken_AuthorizationPending(t *testing.T) { } w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(map[string]interface{}{ + if err := json.NewEncoder(w).Encode(map[string]any{ "access_token": testAccessToken, "refresh_token": "test-refresh-token", "token_type": "Bearer", @@ -101,7 +101,7 @@ func TestPollForToken_SlowDown(t *testing.T) { } w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(map[string]interface{}{ + if err := json.NewEncoder(w).Encode(map[string]any{ "access_token": testAccessToken, "refresh_token": "test-refresh-token", "token_type": "Bearer", @@ -239,7 +239,7 @@ func TestExchangeDeviceCode_Success(t *testing.T) { } w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(map[string]interface{}{ + if err := json.NewEncoder(w).Encode(map[string]any{ "access_token": testAccessToken, "refresh_token": "test-refresh-token", "token_type": "Bearer", diff --git a/tokens.go b/tokens.go index e18409f..03d600c 100644 --- a/tokens.go +++ b/tokens.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "errors" "fmt" "os" "time" @@ -14,7 +15,7 @@ type ErrorResponse struct { } // ErrRefreshTokenExpired indicates the refresh token has expired or is invalid. -var ErrRefreshTokenExpired = fmt.Errorf("refresh token expired or invalid") +var ErrRefreshTokenExpired = errors.New("refresh token expired or invalid") // TokenStorage holds persisted OAuth tokens for one client. type TokenStorage struct { @@ -41,7 +42,7 @@ func loadTokens() (*TokenStorage, error) { return nil, fmt.Errorf("failed to parse token file: %w", err) } if storageMap.Tokens == nil { - return nil, fmt.Errorf("no tokens in file") + return nil, errors.New("no tokens in file") } if storage, ok := storageMap.Tokens[clientID]; ok { return storage, nil @@ -97,7 +98,7 @@ func saveTokens(storage *TokenStorage) error { // validateTokenResponse performs basic sanity checks on a token response. func validateTokenResponse(accessToken, tokenType string, expiresIn int) error { if accessToken == "" { - return fmt.Errorf("access_token is empty") + return errors.New("access_token is empty") } if len(accessToken) < 10 { return fmt.Errorf("access_token is too short (length: %d)", len(accessToken)) diff --git a/tui/browser_view.go b/tui/browser_view.go index 6e876ad..42428c4 100644 --- a/tui/browser_view.go +++ b/tui/browser_view.go @@ -37,7 +37,7 @@ func renderBrowserView(m *BrowserModel) string { BorderForeground(colorInfo). Padding(0, 1). Foreground(colorInfo) - b.WriteString(urlBox.Render(fmt.Sprintf("🌐 %s", m.authURL))) + b.WriteString(urlBox.Render("🌐 " + m.authURL)) b.WriteString("\n\n") } @@ -103,7 +103,7 @@ func renderBrowserComplete(m *BrowserModel) string { preview = preview[:50] + "..." } - b.WriteString(infoStyle.Render(fmt.Sprintf("Access Token: %s", preview))) + b.WriteString(infoStyle.Render("Access Token: " + preview)) b.WriteString("\n") } } diff --git a/tui/components/timer.go b/tui/components/timer.go index 36d1d34..ea74e89 100644 --- a/tui/components/timer.go +++ b/tui/components/timer.go @@ -44,25 +44,19 @@ func (t *Timer) View() string { Bold(true) if t.isCountdown { - remaining := t.totalDuration - t.elapsed - if remaining < 0 { - remaining = 0 - } + remaining := max(t.totalDuration-t.elapsed, 0) return style.Render(fmt.Sprintf("Time remaining: %s / %s", formatDuration(remaining), formatDuration(t.totalDuration))) } - return style.Render(fmt.Sprintf("Elapsed: %s", formatDuration(t.elapsed))) + return style.Render("Elapsed: " + formatDuration(t.elapsed)) } // ViewCompact renders a compact version of the timer func (t *Timer) ViewCompact() string { if t.isCountdown { - remaining := t.totalDuration - t.elapsed - if remaining < 0 { - remaining = 0 - } + remaining := max(t.totalDuration-t.elapsed, 0) return formatDuration(remaining) } return formatDuration(t.elapsed) diff --git a/tui/device_model.go b/tui/device_model.go index 60a39c3..76f88a5 100644 --- a/tui/device_model.go +++ b/tui/device_model.go @@ -163,10 +163,10 @@ func (m *DeviceModel) handleFlowUpdate(update FlowUpdate) tea.Cmd { m.infoBox.Clear() m.infoBox.AddLine("Please authorize this device:") m.infoBox.AddLine("") - m.infoBox.AddLine(fmt.Sprintf("Visit: %s", m.verificationURIComplete)) + m.infoBox.AddLine("Visit: " + m.verificationURIComplete) m.infoBox.AddLine("") - m.infoBox.AddLine(fmt.Sprintf("Or go to: %s", m.verificationURI)) - m.infoBox.AddLine(fmt.Sprintf("And enter: %s", m.userCode)) + m.infoBox.AddLine("Or go to: " + m.verificationURI) + m.infoBox.AddLine("And enter: " + m.userCode) case PollingUpdate: m.pollCount = update.GetInt("poll_count") diff --git a/tui/device_view.go b/tui/device_view.go index f087234..7450c54 100644 --- a/tui/device_view.go +++ b/tui/device_view.go @@ -122,7 +122,7 @@ func renderDeviceComplete(m *DeviceModel) string { preview = preview[:50] + "..." } - b.WriteString(infoStyle.Render(fmt.Sprintf("Access Token: %s", preview))) + b.WriteString(infoStyle.Render("Access Token: " + preview)) b.WriteString("\n") } } diff --git a/tui/manager.go b/tui/manager.go index 0dd786e..6b97416 100644 --- a/tui/manager.go +++ b/tui/manager.go @@ -89,7 +89,7 @@ type TokenStorage struct { AccessToken string RefreshToken string TokenType string - ExpiresAt interface{} // time.Time, but avoiding import cycle + ExpiresAt any // time.Time, but avoiding import cycle ClientID string Flow string } diff --git a/tui/manager_test.go b/tui/manager_test.go index 2e10697..8c41661 100644 --- a/tui/manager_test.go +++ b/tui/manager_test.go @@ -40,29 +40,11 @@ func TestShouldUseSimpleUI(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - // Save current env vars - savedEnv := make(map[string]string) - for key := range tt.envVars { - savedEnv[key] = os.Getenv(key) - } - - // Set test env vars for key, val := range tt.envVars { - os.Setenv(key, val) + t.Setenv(key, val) } - // Test result := isCIEnvironment() || os.Getenv("TERM") == "dumb" - - // Restore env vars - for key, val := range savedEnv { - if val == "" { - os.Unsetenv(key) - } else { - os.Setenv(key, val) - } - } - if result != tt.expected { t.Errorf("%s: expected %v, got %v", tt.desc, tt.expected, result) } @@ -91,7 +73,7 @@ func TestFlowUpdateHelpers(t *testing.T) { Type: StepStart, Step: 1, Message: "Testing", - Data: map[string]interface{}{ + Data: map[string]any{ "string_val": "hello", "int_val": 42, "float_val": 3.14, diff --git a/tui/messages.go b/tui/messages.go index 316286c..d66ea97 100644 --- a/tui/messages.go +++ b/tui/messages.go @@ -21,11 +21,11 @@ const ( // FlowUpdate represents a progress update message from an OAuth flow. type FlowUpdate struct { Type FlowUpdateType - Step int // Current step number (1-indexed) - TotalSteps int // Total number of steps - Message string // Human-readable message - Progress float64 // Progress percentage (0.0 to 1.0) - Data map[string]interface{} // Additional data for specific update types + Step int // Current step number (1-indexed) + TotalSteps int // Total number of steps + Message string // Human-readable message + Progress float64 // Progress percentage (0.0 to 1.0) + Data map[string]any // Additional data for specific update types } // String returns a human-readable representation of the FlowUpdateType. diff --git a/userinfo.go b/userinfo.go index 68bbc38..1b943cc 100644 --- a/userinfo.go +++ b/userinfo.go @@ -3,6 +3,7 @@ package main import ( "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -69,7 +70,7 @@ func fetchUserInfo(ctx context.Context, accessToken string) (*UserInfo, error) { return nil, fmt.Errorf("failed to parse UserInfo response: %w", err) } if info.Sub == "" { - return nil, fmt.Errorf("UserInfo response missing required 'sub' claim") + return nil, errors.New("UserInfo response missing required 'sub' claim") } return &info, nil }