From 1da98dda3559d9151d5e6757a99db0cd24f86054 Mon Sep 17 00:00:00 2001 From: Mikhail Shytsko Date: Mon, 9 Mar 2026 16:28:40 +0100 Subject: [PATCH 1/3] fix: resolve all golangci-lint errors failing CI pipeline --- cmd/grpc_test.go | 6 +-- cmd/seed_dev.go | 29 ---------- cmd/seed_prod.go | 6 --- cmd/seed_tui_prod.go | 6 --- cmd/tuidemo/main.go | 4 +- cmd/ui_helpers.go | 56 -------------------- internal/dsn/postgres.go | 2 +- internal/mcpserver/handlers.go | 8 +-- internal/orchestration/auth_validator.go | 2 +- internal/orchestration/event_handler.go | 6 +-- internal/orchestration/prompt_runner.go | 6 --- internal/orchestration/prompt_runner_test.go | 17 +++--- internal/orchestration/seed_orchestrator.go | 6 +-- internal/seeding/renderer.go | 9 ---- internal/sqlexec/executor.go | 2 +- internal/ui/tui/app_test.go | 14 ++--- internal/ui/tui/components/selector_test.go | 2 +- internal/ui/tui/components/summary_box.go | 9 ++-- internal/ui/tui/integration_test.go | 9 ---- internal/ui/tui/phases/init_phase.go | 5 +- internal/ui/tui/phases/plan_phase.go | 5 +- internal/updates/cache.go | 2 +- internal/updates/checker.go | 4 +- 23 files changed, 41 insertions(+), 174 deletions(-) diff --git a/cmd/grpc_test.go b/cmd/grpc_test.go index 2740e7f..a56d953 100644 --- a/cmd/grpc_test.go +++ b/cmd/grpc_test.go @@ -88,12 +88,8 @@ var grpcTestCmd = &cobra.Command{ fmt.Println("\nStep 5: Testing gRPC connection...") tlsCreds := credentials.NewTLS(tlsCfg) - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - grpcConn, err := grpc.DialContext(ctx, grpcAddr, + grpcConn, err := grpc.NewClient(grpcAddr, grpc.WithTransportCredentials(tlsCreds), - grpc.WithBlock(), ) if err != nil { fmt.Printf("❌ gRPC dial failed: %v\n", err) diff --git a/cmd/seed_dev.go b/cmd/seed_dev.go index bf9a466..d49f064 100644 --- a/cmd/seed_dev.go +++ b/cmd/seed_dev.go @@ -4,32 +4,3 @@ //go:build dev package cmd - -import ( - "seedfast/cli/internal/manifest" - - "github.com/pterm/pterm" -) - -// showEndpointIndicators displays visual indicators for endpoint sources in dev builds. -// Shows [PROD] if using production endpoints, or [CUSTOM] if using custom build-time endpoints. -func showEndpointIndicators() { - if manifest.CustomBackendURL != "" || manifest.CustomGrpcURL != "" { - // Custom endpoints - at least one build-time variable is set - pterm.Println(pterm.NewStyle(pterm.FgYellow, pterm.Bold).Sprint("[CUSTOM]") + - pterm.NewStyle(pterm.FgYellow).Sprint(" Using custom endpoints:")) - - if manifest.CustomBackendURL != "" { - pterm.Printf(" → Backend: %s\n", manifest.CustomBackendURL) - } - if manifest.CustomGrpcURL != "" { - pterm.Printf(" → gRPC: %s\n", manifest.CustomGrpcURL) - } - pterm.Println() - } else { - // Production endpoints - both build-time variables are empty - pterm.Println(pterm.NewStyle(pterm.FgGreen, pterm.Bold).Sprint("[PROD]") + - pterm.NewStyle(pterm.FgGreen).Sprint(" Using production endpoints")) - pterm.Println() - } -} diff --git a/cmd/seed_prod.go b/cmd/seed_prod.go index 0df7b31..6487b38 100644 --- a/cmd/seed_prod.go +++ b/cmd/seed_prod.go @@ -4,9 +4,3 @@ //go:build !dev package cmd - -// showEndpointIndicators is a no-op in production builds. -// No visual indicators are shown for endpoint sources. -func showEndpointIndicators() { - // No-op in production builds -} diff --git a/cmd/seed_tui_prod.go b/cmd/seed_tui_prod.go index e6d8222..6487b38 100644 --- a/cmd/seed_tui_prod.go +++ b/cmd/seed_tui_prod.go @@ -4,9 +4,3 @@ //go:build !dev package cmd - -// isTUIOutputMode always returns false in production builds. -// The TUI renderer is only available in dev builds. -func isTUIOutputMode() bool { - return false -} diff --git a/cmd/tuidemo/main.go b/cmd/tuidemo/main.go index c1074b2..3b133fa 100644 --- a/cmd/tuidemo/main.go +++ b/cmd/tuidemo/main.go @@ -170,7 +170,7 @@ func runInteractive(p *tea.Program) { for _, ts := range tables { p.Send(tui.TableStartedMsg{Name: ts.Name, Remaining: len(tables)}) sleep(randMs(800, 1500)) - p.Send(tui.TableDoneMsg{Name: ts.Name, RecordsCount: ts.RecordsCount}) + p.Send(tui.TableDoneMsg(ts)) } sleep(300) @@ -303,7 +303,7 @@ func sendSeedingFlow(p *tea.Program, tables []tui.TableSpec, failures map[string if reason, failed := failures[ts.Name]; failed { p.Send(tui.TableFailedMsg{Name: ts.Name, Reason: reason}) } else { - p.Send(tui.TableDoneMsg{Name: ts.Name, RecordsCount: ts.RecordsCount}) + p.Send(tui.TableDoneMsg(ts)) } } diff --git a/cmd/ui_helpers.go b/cmd/ui_helpers.go index bff6e7d..4fb7f58 100644 --- a/cmd/ui_helpers.go +++ b/cmd/ui_helpers.go @@ -2,59 +2,3 @@ // Licensed under the MIT License. See LICENSE file in the project root for details. package cmd - -import ( - "fmt" - "io" - "sync" - "time" -) - -// startInlineSpinner starts a simple inline spinner animation on a single line. -// It displays rotating animation frames followed by the provided text, updating -// the same line in the terminal. The spinner runs in a separate goroutine and -// can be stopped by calling the returned function. -// -// The spinner automatically clears the line when stopped and handles text length -// limits to prevent display issues. It uses the provided frames array for animation -// and updates at the specified interval. -// -// Parameters: -// - w: The io.Writer to write the spinner to (typically os.Stdout or os.Stderr) -// - text: The text to display after the spinner animation -// - frames: Array of strings representing animation frames (e.g., ["|", "/", "-", "\\"]) -// - interval: Time duration between frame updates -// -// Returns a function that stops the spinner and cleans up when called. -func startInlineSpinner(w io.Writer, text string, frames []string, interval time.Duration) func() { - stop := make(chan struct{}) - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - i := 0 - ticker := time.NewTicker(interval) - defer ticker.Stop() - for { - select { - case <-stop: - line := fmt.Sprintf("%s %s", frames[i%len(frames)], text) - // Clear the spinner line completely, then return - fmt.Fprintf(w, "\r%*s\r", len(line), "") - return - case <-ticker.C: - line := fmt.Sprintf("%s %s", frames[i%len(frames)], text) - fmt.Fprintf(w, "\r%s", line) - // primitive protection against very long lines - if len(line) > 2000 { - line = line[:2000] - } - i++ - } - } - }() - return func() { - close(stop) - wg.Wait() - } -} diff --git a/internal/dsn/postgres.go b/internal/dsn/postgres.go index 57760d2..88c3537 100644 --- a/internal/dsn/postgres.go +++ b/internal/dsn/postgres.go @@ -26,7 +26,7 @@ func (r *PostgreSQLResolver) Parse(dsn string) (*DSNInfo, error) { // Detect scheme (postgres:// or postgresql://) scheme := "" - remainder := dsn + var remainder string if strings.HasPrefix(dsn, "postgresql://") { scheme = "postgresql" remainder = strings.TrimPrefix(dsn, "postgresql://") diff --git a/internal/mcpserver/handlers.go b/internal/mcpserver/handlers.go index b7eaad8..35dff38 100644 --- a/internal/mcpserver/handlers.go +++ b/internal/mcpserver/handlers.go @@ -480,7 +480,7 @@ func (s *Server) toolRunStatus(ctx context.Context, args json.RawMessage) (CallT sb.WriteString(fmt.Sprintf("Completed: %s\n", run.CompletedAt.Format(time.RFC3339))) } - sb.WriteString(fmt.Sprintf("\nProgress:\n")) + sb.WriteString("\nProgress:\n") sb.WriteString(fmt.Sprintf(" Tables: %d/%d (%.1f%%)\n", run.Progress.CompletedTables, run.Progress.TotalTables, run.Progress.Percent)) sb.WriteString(fmt.Sprintf(" Rows: %d\n", run.Progress.TotalRows)) @@ -496,7 +496,7 @@ func (s *Server) toolRunStatus(ctx context.Context, args json.RawMessage) (CallT } if run.Summary != nil { - sb.WriteString(fmt.Sprintf("\nSummary:\n")) + sb.WriteString("\nSummary:\n") sb.WriteString(fmt.Sprintf(" Success: %v\n", run.Summary.Success)) sb.WriteString(fmt.Sprintf(" Total Tables: %d\n", run.Summary.TotalTables)) sb.WriteString(fmt.Sprintf(" Succeeded: %d\n", run.Summary.SuccessTables)) @@ -561,7 +561,7 @@ func (s *Server) toolRunCancel(ctx context.Context, args json.RawMessage) (CallT // toolPlansList lists all seeding plans in current session. func (s *Server) toolPlansList(ctx context.Context, args json.RawMessage) (CallToolResult, error) { var input plansListInput - if args != nil && len(args) > 0 { + if len(args) > 0 { if err := json.Unmarshal(args, &input); err != nil { return CallToolResult{}, fmt.Errorf("invalid arguments: %w", err) } @@ -672,7 +672,7 @@ func (s *Server) toolPlanCreate(ctx context.Context, args json.RawMessage) (Call } var sb strings.Builder - sb.WriteString(fmt.Sprintf("Plan created successfully\n")) + sb.WriteString("Plan created successfully\n") sb.WriteString(fmt.Sprintf("Plan ID: %s\n", plan.ID)) sb.WriteString(fmt.Sprintf("Scope: %s\n", plan.Scope)) sb.WriteString(fmt.Sprintf("Tables (%d): %s\n", len(plan.Tables), strings.Join(plan.Tables, ", "))) diff --git a/internal/orchestration/auth_validator.go b/internal/orchestration/auth_validator.go index 625c09e..bfc06c0 100644 --- a/internal/orchestration/auth_validator.go +++ b/internal/orchestration/auth_validator.go @@ -44,7 +44,7 @@ func outputAuthError(message, hint, errorCode string) { Message: message + ". " + hint, Code: errorCode, }) - json.NewEncoder(os.Stdout).Encode(event) + _ = json.NewEncoder(os.Stdout).Encode(event) } else { fmt.Println("🔐 " + message) fmt.Println(" " + hint) diff --git a/internal/orchestration/event_handler.go b/internal/orchestration/event_handler.go index 7aa6140..5fa06bf 100644 --- a/internal/orchestration/event_handler.go +++ b/internal/orchestration/event_handler.go @@ -239,7 +239,7 @@ func (eh *EventHandler) handleStreamError(ev seeding.Event) { // Stop UI immediately eh.headerSpinner.Stop() if eh.state.PlanningActive && eh.state.PlanSpinner != nil { - eh.state.PlanSpinner.Stop() + _ = eh.state.PlanSpinner.Stop() eh.state.PlanningActive = false } eh.stopArea() @@ -262,7 +262,7 @@ func (eh *EventHandler) handleStreamError(ev seeding.Event) { // Stop UI immediately eh.headerSpinner.Stop() if eh.state.PlanningActive && eh.state.PlanSpinner != nil { - eh.state.PlanSpinner.Stop() + _ = eh.state.PlanSpinner.Stop() eh.state.PlanningActive = false } eh.stopArea() @@ -282,7 +282,7 @@ func (eh *EventHandler) handleStreamError(ev seeding.Event) { // Stop UI immediately and notify // no prep spinner in the new UI if eh.state.PlanningActive && eh.state.PlanSpinner != nil { - eh.state.PlanSpinner.Stop() + _ = eh.state.PlanSpinner.Stop() eh.state.PlanningActive = false } eh.stopArea() diff --git a/internal/orchestration/prompt_runner.go b/internal/orchestration/prompt_runner.go index ca9dfec..6625480 100644 --- a/internal/orchestration/prompt_runner.go +++ b/internal/orchestration/prompt_runner.go @@ -2,7 +2,6 @@ package orchestration import ( "context" - "errors" "strings" "time" @@ -21,11 +20,6 @@ type promptResult struct { TimedOut bool } -// Errors returned by RunPrompt. -var ( - errPromptCancelled = errors.New("prompt cancelled by user") - errPromptTimeout = errors.New("prompt input timeout") -) // PromptConfig configures a standalone prompt run. type PromptConfig struct { diff --git a/internal/orchestration/prompt_runner_test.go b/internal/orchestration/prompt_runner_test.go index 66991a9..40f5543 100644 --- a/internal/orchestration/prompt_runner_test.go +++ b/internal/orchestration/prompt_runner_test.go @@ -1,6 +1,7 @@ package orchestration import ( + "context" "strings" "testing" @@ -19,7 +20,7 @@ func TestPromptActions_SendHumanResponse_EmptyAnswer(t *testing.T) { "answer": map[string]any{"raw": ""}, } - err := pa.SendHumanResponse(nil, "q-1", responseData) + err := pa.SendHumanResponse(context.TODO(), "q-1", responseData) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -45,7 +46,7 @@ func TestPromptActions_SendHumanResponse_NonEmptyAnswer(t *testing.T) { "answer": map[string]any{"raw": "use English"}, } - err := pa.SendHumanResponse(nil, "q-1", responseData) + err := pa.SendHumanResponse(context.TODO(), "q-1", responseData) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -68,7 +69,7 @@ func TestPromptActions_SendHumanResponse_MissingAnswerKey(t *testing.T) { "other_key": 123, } - err := pa.SendHumanResponse(nil, "q-1", responseData) + err := pa.SendHumanResponse(context.TODO(), "q-1", responseData) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -91,7 +92,7 @@ func TestPromptActions_SendHumanResponse_MalformedAnswer(t *testing.T) { "answer": "not a map", } - err := pa.SendHumanResponse(nil, "q-1", responseData) + err := pa.SendHumanResponse(context.TODO(), "q-1", responseData) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -108,7 +109,7 @@ func TestPromptActions_SendHumanResponse_MalformedAnswer(t *testing.T) { func TestPromptActions_SendCancellation_UserCancelled(t *testing.T) { pa := &promptActions{resultCh: make(chan promptResult, 1)} - err := pa.SendCancellation(nil, "user cancelled") + err := pa.SendCancellation(context.TODO(), "user cancelled") if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -125,7 +126,7 @@ func TestPromptActions_SendCancellation_UserCancelled(t *testing.T) { func TestPromptActions_SendCancellation_InputTimedOut(t *testing.T) { pa := &promptActions{resultCh: make(chan promptResult, 1)} - err := pa.SendCancellation(nil, "input timed out") + err := pa.SendCancellation(context.TODO(), "input timed out") if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -142,7 +143,7 @@ func TestPromptActions_SendCancellation_InputTimedOut(t *testing.T) { func TestPromptActions_SendCancellation_OtherReason(t *testing.T) { pa := &promptActions{resultCh: make(chan promptResult, 1)} - err := pa.SendCancellation(nil, "some other reason") + err := pa.SendCancellation(context.TODO(), "some other reason") if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -163,7 +164,7 @@ func TestPromptActions_NonBlockingWhenChannelFull(t *testing.T) { pa.resultCh <- promptResult{Answer: "first"} // Second send should not block (non-blocking select) - err := pa.SendHumanResponse(nil, "q-1", map[string]any{ + err := pa.SendHumanResponse(context.TODO(), "q-1", map[string]any{ "answer": map[string]any{"raw": "second"}, }) if err != nil { diff --git a/internal/orchestration/seed_orchestrator.go b/internal/orchestration/seed_orchestrator.go index 190d20b..267b60a 100644 --- a/internal/orchestration/seed_orchestrator.go +++ b/internal/orchestration/seed_orchestrator.go @@ -41,10 +41,8 @@ type SeedOrchestrator struct { bridge bridge.Bridge authValidator *AuthValidator dsnResolver *DSNResolver - dbConnector *DatabaseConnector - eventHandler *EventHandler - workerPool *WorkerPool - signalHandler *SignalHandler + dbConnector *DatabaseConnector + signalHandler *SignalHandler // Runtime state dbPool *pgxpool.Pool diff --git a/internal/seeding/renderer.go b/internal/seeding/renderer.go index 3894343..12e70fe 100644 --- a/internal/seeding/renderer.go +++ b/internal/seeding/renderer.go @@ -3,9 +3,6 @@ package seeding -import ( - "github.com/pterm/pterm" -) // Renderer renders seeding events to console with docker-compose-like UI. type Renderer struct { @@ -44,9 +41,3 @@ func (r *Renderer) Render(ev Event) { } } -func stringListToBulletItems(items []string) (out []pterm.BulletListItem) { - for _, s := range items { - out = append(out, pterm.BulletListItem{Level: 0, Text: s}) - } - return out -} diff --git a/internal/sqlexec/executor.go b/internal/sqlexec/executor.go index 6890947..fc15a06 100644 --- a/internal/sqlexec/executor.go +++ b/internal/sqlexec/executor.go @@ -175,7 +175,7 @@ func (e *Executor) ExecuteSQLInSchema(ctx context.Context, sql string, isWrite b jsonBytes, _ := res.MarshalJSON() return string(jsonBytes), nil } - defer tx.Rollback(ctx) // Rollback if commit doesn't happen + defer func() { _ = tx.Rollback(ctx) }() // Rollback if commit doesn't happen ct, err := tx.Exec(ctx, sql) if err != nil { diff --git a/internal/ui/tui/app_test.go b/internal/ui/tui/app_test.go index 1a22efe..fe47ebc 100644 --- a/internal/ui/tui/app_test.go +++ b/internal/ui/tui/app_test.go @@ -16,12 +16,9 @@ var _ tea.Model = AppModel{} // stubPhase is a minimal Phase implementation for testing that tracks calls // and optionally triggers a transition on specific message types. type stubPhase struct { - id PhaseID - initCalled bool - viewText string - transitionOn tea.Msg // if set, trigger transition to transitionTarget on this msg type - transitionTarget PhaseID - transitionData interface{} + id PhaseID + initCalled bool + viewText string } func newStubPhase(id PhaseID, viewText string) *stubPhase { @@ -37,7 +34,7 @@ func (p *stubPhase) Init() tea.Cmd { func (p *stubPhase) Update(msg tea.Msg) (Phase, tea.Cmd, *Transition) { // Check if this message type should trigger a transition. - switch msg.(type) { + switch msg := msg.(type) { case SubscriptionLimitMsg: if p.id == PhaseInit { return p, nil, &Transition{To: PhaseSubscription, Data: msg} @@ -55,8 +52,7 @@ func (p *stubPhase) Update(msg tea.Msg) (Phase, tea.Cmd, *Transition) { return p, nil, &Transition{To: PhaseSeeding, Data: msg} } case StreamErrorMsg: - m := msg.(StreamErrorMsg) - return p, nil, &Transition{To: PhaseError, Data: m.Message} + return p, nil, &Transition{To: PhaseError, Data: msg.Message} case WorkflowCompletedMsg: if p.id == PhaseSeeding { return p, nil, &Transition{To: PhaseCompleted, Data: "done"} diff --git a/internal/ui/tui/components/selector_test.go b/internal/ui/tui/components/selector_test.go index 3dd759e..7333fdf 100644 --- a/internal/ui/tui/components/selector_test.go +++ b/internal/ui/tui/components/selector_test.go @@ -114,7 +114,7 @@ func TestSelector_EnterSelectsCurrentOption(t *testing.T) { // Navigate to index 1 s, _, _ = s.Update(tea.KeyMsg{Type: tea.KeyDown}) - s, _, result := s.Update(tea.KeyMsg{Type: tea.KeyEnter}) + _, _, result := s.Update(tea.KeyMsg{Type: tea.KeyEnter}) if result == nil { t.Fatal("expected a SelectorResult on Enter") diff --git a/internal/ui/tui/components/summary_box.go b/internal/ui/tui/components/summary_box.go index e6050bc..fd60c85 100644 --- a/internal/ui/tui/components/summary_box.go +++ b/internal/ui/tui/components/summary_box.go @@ -15,11 +15,10 @@ const dashboardURL = "https://dashboard.seedfa.st" // SummaryBoxComponent renders the completion or failure summary. type SummaryBoxComponent struct { - success bool - tableCount int - failedCount int - skippedCount int - totalRows int + success bool + tableCount int + failedCount int + totalRows int elapsed time.Duration failures map[string]string theme *tui.Theme diff --git a/internal/ui/tui/integration_test.go b/internal/ui/tui/integration_test.go index a689c59..824ce5c 100644 --- a/internal/ui/tui/integration_test.go +++ b/internal/ui/tui/integration_test.go @@ -61,15 +61,6 @@ func assertViewContains(t *testing.T, m tea.Model, expected string) { } } -// assertViewNotContains checks that the model's View() output does NOT contain the substring. -func assertViewNotContains(t *testing.T, m tea.Model, unexpected string) { - t.Helper() - view := m.View() - if strings.Contains(view, unexpected) { - t.Errorf("View() should not contain %q.\nView output:\n%s", unexpected, view) - } -} - // subscriptionLimitMsg returns a typical SubscriptionLimitMsg for testing. func subscriptionLimitMsg() tui.SubscriptionLimitMsg { remaining := int32(10) diff --git a/internal/ui/tui/phases/init_phase.go b/internal/ui/tui/phases/init_phase.go index daff4ff..c0db3d9 100644 --- a/internal/ui/tui/phases/init_phase.go +++ b/internal/ui/tui/phases/init_phase.go @@ -33,7 +33,7 @@ func (p *InitPhase) Init() tea.Cmd { } func (p *InitPhase) Update(msg tea.Msg) (tui.Phase, tea.Cmd, *tui.Transition) { - switch msg.(type) { + switch msg := msg.(type) { case tui.SubscriptionLimitMsg: p.showSpinner = false // hide before transition so history view is clean return p, nil, &tui.Transition{To: tui.PhaseSubscription, Data: msg} @@ -43,8 +43,7 @@ func (p *InitPhase) Update(msg tea.Msg) (tui.Phase, tea.Cmd, *tui.Transition) { return p, nil, &tui.Transition{To: tui.PhasePrompt, Data: msg} case tui.StreamErrorMsg: p.showSpinner = false - m := msg.(tui.StreamErrorMsg) - return p, nil, &tui.Transition{To: tui.PhaseError, Data: m.Message} + return p, nil, &tui.Transition{To: tui.PhaseError, Data: msg.Message} case tui.SpinnerStartMsg: p.showSpinner = true return p, nil, nil diff --git a/internal/ui/tui/phases/plan_phase.go b/internal/ui/tui/phases/plan_phase.go index f650c5a..55cbcaf 100644 --- a/internal/ui/tui/phases/plan_phase.go +++ b/internal/ui/tui/phases/plan_phase.go @@ -58,7 +58,7 @@ func (p *PlanPhase) Init() tea.Cmd { } func (p *PlanPhase) Update(msg tea.Msg) (tui.Phase, tea.Cmd, *tui.Transition) { - switch msg.(type) { + switch msg := msg.(type) { case tui.AskHumanMsg: p.showSpinner = false // hide before transition so history view is clean return p, nil, &tui.Transition{To: tui.PhasePrompt, Data: msg} @@ -77,8 +77,7 @@ func (p *PlanPhase) Update(msg tea.Msg) (tui.Phase, tea.Cmd, *tui.Transition) { return p, nil, nil case tui.StreamErrorMsg: p.showSpinner = false - m := msg.(tui.StreamErrorMsg) - return p, nil, &tui.Transition{To: tui.PhaseError, Data: m.Message} + return p, nil, &tui.Transition{To: tui.PhaseError, Data: msg.Message} } if p.showSpinner { diff --git a/internal/updates/cache.go b/internal/updates/cache.go index 7a18255..380c249 100644 --- a/internal/updates/cache.go +++ b/internal/updates/cache.go @@ -137,7 +137,7 @@ func SaveNotificationData(data *NotificationData) error { } // Write to file (ignore errors - not critical) - os.WriteFile(path, jsonData, 0644) + _ = os.WriteFile(path, jsonData, 0644) return nil } diff --git a/internal/updates/checker.go b/internal/updates/checker.go index b87f5b6..4df75c5 100644 --- a/internal/updates/checker.go +++ b/internal/updates/checker.go @@ -61,7 +61,7 @@ func CheckAndNotifyIfNeeded(ctx context.Context, cmd *cobra.Command, currentVers // Update cache with fetched version and check time data.LatestVersion = latestVersion data.LastCheckTime = time.Now() - SaveNotificationData(data) + _ = SaveNotificationData(data) } // Determine if we should show notification @@ -74,7 +74,7 @@ func CheckAndNotifyIfNeeded(ctx context.Context, cmd *cobra.Command, currentVers // Update cache with new notification time data.LastNotificationTime = time.Now() - SaveNotificationData(data) + _ = SaveNotificationData(data) } // isCacheFresh returns true if the cached version data is recent enough to skip From d1c16287ceede772b977262542a8bd5903e32c57 Mon Sep 17 00:00:00 2001 From: Mikhail Shytsko Date: Mon, 9 Mar 2026 16:36:21 +0100 Subject: [PATCH 2/3] fix: resolve remaining lint and gofmt errors --- internal/mcpserver/handlers.go | 4 ++-- internal/orchestration/event_handler.go | 6 +++--- internal/orchestration/prompt_runner.go | 1 - internal/orchestration/prompt_runner_test.go | 11 +++++------ internal/orchestration/seed_orchestrator.go | 4 ++-- internal/seeding/renderer.go | 2 -- internal/ui/tui/app_test.go | 5 +---- internal/ui/tui/components/summary_box.go | 6 +++--- internal/ui/tui/phases/prompt_phase.go | 5 ++--- internal/ui/tui/phases/subscription_phase.go | 5 ++--- 10 files changed, 20 insertions(+), 29 deletions(-) diff --git a/internal/mcpserver/handlers.go b/internal/mcpserver/handlers.go index 35dff38..ebbfd23 100644 --- a/internal/mcpserver/handlers.go +++ b/internal/mcpserver/handlers.go @@ -433,7 +433,7 @@ func (s *Server) updateRunFromEvent(ctx context.Context, run *store.Run, event u ElapsedMs: elapsedMs, } if run.Summary.Failures == nil && failedTables > 0 { - // Preserve existing failures + // TODO: Preserve existing failures from prior summary } } @@ -721,7 +721,7 @@ func (s *Server) toolPlanUpdate(ctx context.Context, args json.RawMessage) (Call } var sb strings.Builder - sb.WriteString(fmt.Sprintf("Plan updated successfully\n")) + sb.WriteString("Plan updated successfully\n") sb.WriteString(fmt.Sprintf("Plan ID: %s\n", plan.ID)) sb.WriteString(fmt.Sprintf("Scope: %s\n", plan.Scope)) sb.WriteString(fmt.Sprintf("Tables (%d): %s\n", len(plan.Tables), strings.Join(plan.Tables, ", "))) diff --git a/internal/orchestration/event_handler.go b/internal/orchestration/event_handler.go index 5fa06bf..21fa50c 100644 --- a/internal/orchestration/event_handler.go +++ b/internal/orchestration/event_handler.go @@ -322,7 +322,7 @@ func (eh *EventHandler) handleSubscriptionBlocked(ev seeding.Event) { // Stop UI immediately eh.headerSpinner.Stop() if eh.state.PlanningActive && eh.state.PlanSpinner != nil { - eh.state.PlanSpinner.Stop() + _ = eh.state.PlanSpinner.Stop() eh.state.PlanningActive = false } eh.stopArea() @@ -345,7 +345,7 @@ func (eh *EventHandler) handleStreamClosed(ev seeding.Event) { // no prep spinner in the new UI if eh.state.PlanningActive && eh.state.PlanSpinner != nil { - eh.state.PlanSpinner.Stop() + _ = eh.state.PlanSpinner.Stop() eh.state.PlanningActive = false } eh.stopArea() @@ -393,7 +393,7 @@ func (eh *EventHandler) handlePlanProposed(ev seeding.Event) error { // Replan/reset UI if eh.state.PlanningActive && eh.state.PlanSpinner != nil { - eh.state.PlanSpinner.Stop() + _ = eh.state.PlanSpinner.Stop() eh.state.PlanningActive = false } eh.stopArea() diff --git a/internal/orchestration/prompt_runner.go b/internal/orchestration/prompt_runner.go index 6625480..ed0167e 100644 --- a/internal/orchestration/prompt_runner.go +++ b/internal/orchestration/prompt_runner.go @@ -20,7 +20,6 @@ type promptResult struct { TimedOut bool } - // PromptConfig configures a standalone prompt run. type PromptConfig struct { QuestionID string diff --git a/internal/orchestration/prompt_runner_test.go b/internal/orchestration/prompt_runner_test.go index 40f5543..42fc0d0 100644 --- a/internal/orchestration/prompt_runner_test.go +++ b/internal/orchestration/prompt_runner_test.go @@ -491,12 +491,11 @@ func TestStripAnsi_IncompleteEscape(t *testing.T) { got := stripAnsi(input) // The function should not panic; exact output depends on implementation // but should contain the non-escape characters - if len(got) == 0 { - // The \033 and [ will be consumed looking for 'm', and since - // there's no 'm', the loop exits. The remaining characters from - // index 0 onward where \033 is found get appended including \033 byte. - // As long as no panic, we're good. - } + // The \033 and [ will be consumed looking for 'm', and since + // there's no 'm', the loop exits. The remaining characters from + // index 0 onward where \033 is found get appended including \033 byte. + // As long as no panic, we're good. + _ = got // Main check: no panic occurred } diff --git a/internal/orchestration/seed_orchestrator.go b/internal/orchestration/seed_orchestrator.go index 267b60a..7638f99 100644 --- a/internal/orchestration/seed_orchestrator.go +++ b/internal/orchestration/seed_orchestrator.go @@ -41,8 +41,8 @@ type SeedOrchestrator struct { bridge bridge.Bridge authValidator *AuthValidator dsnResolver *DSNResolver - dbConnector *DatabaseConnector - signalHandler *SignalHandler + dbConnector *DatabaseConnector + signalHandler *SignalHandler // Runtime state dbPool *pgxpool.Pool diff --git a/internal/seeding/renderer.go b/internal/seeding/renderer.go index 12e70fe..ab428d7 100644 --- a/internal/seeding/renderer.go +++ b/internal/seeding/renderer.go @@ -3,7 +3,6 @@ package seeding - // Renderer renders seeding events to console with docker-compose-like UI. type Renderer struct { sectionShown bool @@ -40,4 +39,3 @@ func (r *Renderer) Render(ev Event) { // Suppressed to keep UI clean } } - diff --git a/internal/ui/tui/app_test.go b/internal/ui/tui/app_test.go index fe47ebc..3998e51 100644 --- a/internal/ui/tui/app_test.go +++ b/internal/ui/tui/app_test.go @@ -90,10 +90,7 @@ func newTestAppModel() AppModel { func TestAppModelImplementsTeaModel(t *testing.T) { m := newTestAppModel() - var model tea.Model = m - if model == nil { - t.Fatal("AppModel should implement tea.Model") - } + var _ tea.Model = m } func TestAppModelInitialPhase(t *testing.T) { diff --git a/internal/ui/tui/components/summary_box.go b/internal/ui/tui/components/summary_box.go index fd60c85..da06a7d 100644 --- a/internal/ui/tui/components/summary_box.go +++ b/internal/ui/tui/components/summary_box.go @@ -19,9 +19,9 @@ type SummaryBoxComponent struct { tableCount int failedCount int totalRows int - elapsed time.Duration - failures map[string]string - theme *tui.Theme + elapsed time.Duration + failures map[string]string + theme *tui.Theme } func NewSuccessSummary(theme *tui.Theme, tableCount, totalRows int, elapsed time.Duration) SummaryBoxComponent { diff --git a/internal/ui/tui/phases/prompt_phase.go b/internal/ui/tui/phases/prompt_phase.go index 50c8799..8ca79e5 100644 --- a/internal/ui/tui/phases/prompt_phase.go +++ b/internal/ui/tui/phases/prompt_phase.go @@ -102,7 +102,7 @@ func (p *PromptPhase) Init() tea.Cmd { func (p *PromptPhase) Update(msg tea.Msg) (tui.Phase, tea.Cmd, *tui.Transition) { // Handle events from gRPC that arrive while in prompt phase - switch msg.(type) { + switch msg := msg.(type) { case tui.HumanResponseSentMsg: // Response sent, wait for next gRPC event p.waiting = true @@ -128,8 +128,7 @@ func (p *PromptPhase) Update(msg tea.Msg) (tui.Phase, tea.Cmd, *tui.Transition) return p, nil, nil case tui.StreamErrorMsg: p.waiting = false - m := msg.(tui.StreamErrorMsg) - return p, nil, &tui.Transition{To: tui.PhaseError, Data: m.Message} + return p, nil, &tui.Transition{To: tui.PhaseError, Data: msg.Message} case tui.CancellationSentMsg: return p, tea.Quit, nil } diff --git a/internal/ui/tui/phases/subscription_phase.go b/internal/ui/tui/phases/subscription_phase.go index 7044a9e..2af807b 100644 --- a/internal/ui/tui/phases/subscription_phase.go +++ b/internal/ui/tui/phases/subscription_phase.go @@ -95,7 +95,7 @@ func (p *SubscriptionPhase) Init() tea.Cmd { } func (p *SubscriptionPhase) Update(msg tea.Msg) (tui.Phase, tea.Cmd, *tui.Transition) { - switch msg.(type) { + switch msg := msg.(type) { case tui.PlanProposedMsg: p.showSpinner = false // hide before transition so history view is clean return p, nil, &tui.Transition{To: tui.PhasePlan, Data: msg} @@ -107,8 +107,7 @@ func (p *SubscriptionPhase) Update(msg tea.Msg) (tui.Phase, tea.Cmd, *tui.Transi return p, nil, &tui.Transition{To: tui.PhaseError, Data: msg} case tui.StreamErrorMsg: p.showSpinner = false - m := msg.(tui.StreamErrorMsg) - return p, nil, &tui.Transition{To: tui.PhaseError, Data: m.Message} + return p, nil, &tui.Transition{To: tui.PhaseError, Data: msg.Message} case tui.SpinnerStartMsg: p.showSpinner = true return p, nil, nil From f20bda8e50194be436765b449bf88043af9e09ed Mon Sep 17 00:00:00 2001 From: Mikhail Shytsko Date: Mon, 9 Mar 2026 16:45:23 +0100 Subject: [PATCH 3/3] fix: resolve last 3 lint errors (errcheck, empty branch) --- internal/mcpserver/handlers.go | 2 +- internal/orchestration/event_handler.go | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/mcpserver/handlers.go b/internal/mcpserver/handlers.go index ebbfd23..bb0badf 100644 --- a/internal/mcpserver/handlers.go +++ b/internal/mcpserver/handlers.go @@ -433,7 +433,7 @@ func (s *Server) updateRunFromEvent(ctx context.Context, run *store.Run, event u ElapsedMs: elapsedMs, } if run.Summary.Failures == nil && failedTables > 0 { - // TODO: Preserve existing failures from prior summary + run.Summary.Failures = make(map[string]string) // TODO: Preserve existing failures from prior summary } } diff --git a/internal/orchestration/event_handler.go b/internal/orchestration/event_handler.go index 21fa50c..8c26ce9 100644 --- a/internal/orchestration/event_handler.go +++ b/internal/orchestration/event_handler.go @@ -493,7 +493,7 @@ func (eh *EventHandler) handleAskHuman(ev seeding.Event) error { // If we were already planning, stop that spinner before showing a new question if eh.state.PlanningActive && eh.state.PlanSpinner != nil { - eh.state.PlanSpinner.Stop() + _ = eh.state.PlanSpinner.Stop() eh.state.PlanningActive = false } @@ -682,7 +682,7 @@ func (eh *EventHandler) handleTableStarted(ev seeding.Event) error { if eh.state.AwaitingDecision { eh.state.AwaitingDecision = false if eh.state.PlanningActive && eh.state.PlanSpinner != nil { - eh.state.PlanSpinner.Stop() + _ = eh.state.PlanSpinner.Stop() eh.state.PlanningActive = false } }