Skip to content

Migrate to Bubble Tea v2 API#365

Merged
nicholas-fedor merged 8 commits intomainfrom
fix/bubbletea-v2-api-migration
Feb 28, 2026
Merged

Migrate to Bubble Tea v2 API#365
nicholas-fedor merged 8 commits intomainfrom
fix/bubbletea-v2-api-migration

Conversation

@nicholas-fedor
Copy link
Owner

@nicholas-fedor nicholas-fedor commented Feb 28, 2026

The following PR migrates the project to use the Bubble Tea v2 API.

Problem

Dependency updates introduced breaking API changes in Bubble Tea v2 that caused compilation failures throughout the codebase.

Solution

Updated all TUI-related code to conform to Bubble Tea v2's new API
specifications while maintaining existing functionality.

Changes

  • Update go.mod dependencies to charm.land/bubbletea/v2 and lipgloss/v2
  • Change View() method signature from string to tea.View return type
  • Replace tea.WithAltScreen() option with view.AltScreen field
  • Remove tea.EnterAltScreen command from Init() method
  • Update test suite to use new tea.KeyPressMsg interface
  • Fix import paths across cli package and mocks
  • Add Windows build exclusions in .goreleaser.yaml for riscv64 and arm

Summary by CodeRabbit

  • Chores

    • Upgraded UI libraries to v2, added a new CLI dependency, and refreshed indirect dependencies.
    • Excluded the Windows/arm build target from releases.
    • CI workflows now read the Go version from go.mod.
    • Updated ignore list to include /bin and editor settings.
  • Refactor

    • Migrated UI rendering to newer library APIs and adjusted startup/display behavior.
  • Tests

    • Updated tests and added helpers to align with the UI/library upgrades.
  • Documentation

    • Shortened README description.

- Update dependencies to charm.land/bubbletea/v2 and lipgloss/v2
- Change View() signature from string to tea.View return type
- Replace tea.WithAltScreen() option with view.AltScreen field
- Remove tea.EnterAltScreen command from Init() method
- Update tests to use new tea.KeyPressMsg interface
- Add Windows build exclusions for riscv64 and arm architectures
@coderabbitai
Copy link

coderabbitai bot commented Feb 28, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e0d779 and ea5fe4b.

📒 Files selected for processing (2)
  • .gitignore
  • internal/fs/fs_test.go

Walkthrough

Updates TUI deps to charm.land v2, refactors the TUI to return and render tea.View (Init/AltScreen changes, nil program handling), updates tests for new key-press helpers and View content, switches CI to read Go version from go.mod, adds goreleaser ignore for windows/arm, and updates dependencies.

Changes

Cohort / File(s) Summary
Release config
/.goreleaser.yaml
Added an ignore rule to exclude the windows/arm build target.
CI workflows
.github/workflows/...
/.github/workflows/build.yaml, /.github/workflows/lint.yaml, /.github/workflows/security.yaml, /.github/workflows/test.yaml
Switched setup-go steps to read Go version from go.mod (go-version-file) and removed explicit GO_VERSION / matrix go-version entries.
Dependency manifest
go.mod
Replaced charmbracelet v1 modules with charm.land v2 equivalents, added github.com/spf13/cobra v1.10.2, and updated many indirect dependency paths and versions.
TUI core
internal/cli/tui.go
Replaced imports with charm.land v2, renamed parameters (loggerlog, fsfilesystem), switched listing to filesystem abstraction, made Init() return nil, changed View() to return tea.View, and added nil-program early return and adjusted run flow.
TUI tests & mocks
internal/cli/tui_test.go, internal/cli/cli_test.go, internal/cli/mocks/ProgramRunner.go
Updated imports to charm.land v2, added key-press helpers (keyPress, keyPressString), adapted tests to assert View().Content, and adjusted Init expectations and view rendering assertions.
Filesystem API & tests
internal/fs/fs.go, internal/fs/fs_test.go
Renamed parameter loggerlog in RealFS.RemoveBinary and updated logger usage; tests updated to use filepath.FromSlash(...) for path expectations.
Logger/config
cmd/root.go
When verbose, maps log level string to Zap levels via WithOptions and reassigns the logger to dependency struct.
Linters & tooling
.golangci.yaml, .mockery.yaml
Enabled gocritic checks and added revive var-naming config; removed pinned mockery version and switched install instruction to use @latest.
Repo & docs
.gitignore, README.md
Added /bin to .gitignore and re-added .vscode/settings.json; shortened README description.
Small import-only changes
internal/cli/cli_test.go
Updated bubbletea import path to charm.land/bubbletea/v2 (import change only).

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant RunTUI as "RunTUI (cmd)"
  participant FS as "filesystem"
  participant Model
  participant Runner
  participant Program as "Program (maybe nil)"
  Note over RunTUI,Program: High-level TUI startup and run flow

  Caller->>RunTUI: Call RunTUI(dir, config, log, filesystem, runner)
  RunTUI->>FS: filesystem.ListBinaries(dir)
  FS-->>RunTUI: []binaries
  RunTUI->>Model: New model with log, filesystem, binaries
  RunTUI->>Runner: runner.Run(model)
  Runner-->>RunTUI: Program or nil
  alt Program is nil
    RunTUI-->>Caller: return (early, test/mock path)
  else Program returned
    RunTUI->>Program: Start program.Run()
    Program-->>RunTUI: exit/status
    RunTUI-->>Caller: return
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Migrate to Bubble Tea v2 API' accurately reflects the main objective of this pull request, which is to update the codebase to conform to Bubble Tea v2 API breaking changes.
Docstring Coverage ✅ Passed Docstring coverage is 92.31% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/bubbletea-v2-api-migration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- Configure setup-go action to read Go version from go.mod file
- Remove hardcoded GO_VERSION environment variables
- Remove Go version matrix from test workflow
@codecov
Copy link

codecov bot commented Feb 28, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #365      +/-   ##
==========================================
+ Coverage   69.68%   70.11%   +0.42%     
==========================================
  Files           6        6              
  Lines         353      358       +5     
==========================================
+ Hits          246      251       +5     
  Misses         97       97              
  Partials       10       10              
Files with missing lines Coverage Δ
cmd/root.go 6.89% <ø> (ø)
internal/cli/tui.go 86.70% <100.00%> (+0.36%) ⬆️
internal/fs/fs.go 95.55% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
.goreleaser.yaml (1)

19-23: Drop the unreachable windows/riscv64 ignore entry.

goarch does not currently include riscv64, so this ignore rule never matches and can be misleading.

✂️ Suggested cleanup
     ignore:
-      - goos: windows
-        goarch: riscv64
       - goos: windows
         goarch: arm
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.goreleaser.yaml around lines 19 - 23, Remove the unreachable ignore entry
for goos: windows / goarch: riscv64 from the .goreleaser.yaml ignore list: the
goarch value riscv64 is not recognized by Go toolchain so this rule never
matches—delete that specific ignore mapping and keep only valid ignore entries
(e.g., the existing windows/arm entry) so the ignore list reflects actual
supported GOARCH values.
internal/cli/tui.go (1)

304-393: Split View() into smaller render helpers.

This method now carries multiple responsibilities (empty-state handling, grid rendering, footer/layout composition, and view wrapping), which raises maintenance cost and test surface.

♻️ Refactor sketch
 func (m *model) View() tea.View {
-	if len(m.choices) == 0 {
-		view := tea.NewView("No binaries found.\n")
-		view.AltScreen = true
-		return view
-	}
-	// styles + grid + layout + wrap
-	...
+	if len(m.choices) == 0 {
+		return m.renderEmptyView()
+	}
+	return m.renderChoicesView()
 }
+
+func (m *model) renderEmptyView() tea.View {
+	view := tea.NewView("No binaries found.\n")
+	view.AltScreen = true
+	return view
+}
+
+func (m *model) renderChoicesView() tea.View {
+	// style setup, grid construction, footer/layout composition
+	// then wrap with tea.NewView(...) and AltScreen=true
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/tui.go` around lines 304 - 393, The View() method on model is
doing too many things; split it into smaller render helpers to improve
readability and testability: extract empty-state handling into
renderEmptyView(), grid construction into renderGrid() (use m.choices, m.rows,
m.cols, m.cursorX/Y, colWidthPadding, visibleLenPrefix), status/footer
composition into renderFooter() (use m.status, m.styles and footer text), and
the final layout/boxing into renderContent(width,height) that applies lipgloss
styles and padding; then have View() call these helpers
(renderEmptyView()/renderGrid()/renderFooter()/renderContent()) and return the
resulting tea.View so behavior remains unchanged while each helper can be
unit-tested.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In @.goreleaser.yaml:
- Around line 19-23: Remove the unreachable ignore entry for goos: windows /
goarch: riscv64 from the .goreleaser.yaml ignore list: the goarch value riscv64
is not recognized by Go toolchain so this rule never matches—delete that
specific ignore mapping and keep only valid ignore entries (e.g., the existing
windows/arm entry) so the ignore list reflects actual supported GOARCH values.

In `@internal/cli/tui.go`:
- Around line 304-393: The View() method on model is doing too many things;
split it into smaller render helpers to improve readability and testability:
extract empty-state handling into renderEmptyView(), grid construction into
renderGrid() (use m.choices, m.rows, m.cols, m.cursorX/Y, colWidthPadding,
visibleLenPrefix), status/footer composition into renderFooter() (use m.status,
m.styles and footer text), and the final layout/boxing into
renderContent(width,height) that applies lipgloss styles and padding; then have
View() call these helpers
(renderEmptyView()/renderGrid()/renderFooter()/renderContent()) and return the
resulting tea.View so behavior remains unchanged while each helper can be
unit-tested.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7509497 and 6632960.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (6)
  • .goreleaser.yaml
  • go.mod
  • internal/cli/cli_test.go
  • internal/cli/mocks/ProgramRunner.go
  • internal/cli/tui.go
  • internal/cli/tui_test.go

- Simplify project description in README
- Update mockery configuration to use latest version
- Add ./bin directory to gitignore
- Remove redundant windows/riscv64 ignore from goreleaser
- Add gocritic and revive configurations to .golangci.yaml
- Rename parameters to avoid package name conflicts
- Refactor test assertions to use literal path strings
- Apply consistent formatting with blank lines and append patterns
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
internal/cli/tui.go (1)

309-399: Refactor View() into smaller helpers to reduce complexity.

This function currently combines multiple responsibilities (state branches, grid rendering, layout assembly, and view wrapping). Splitting it will make future UI changes safer and easier to test.

♻️ Refactor sketch
 func (m *model) View() tea.View {
-	if len(m.choices) == 0 {
-		view := tea.NewView("No binaries found.\n")
-		view.AltScreen = true
-
-		return view
-	}
-	// styles + grid + layout + footer + wrapper...
-	view := tea.NewView(content)
-	view.AltScreen = true
-
-	return view
+	if len(m.choices) == 0 {
+		return m.newAltScreenView("No binaries found.\n")
+	}
+	return m.newAltScreenView(m.renderMainContent())
 }
+
+func (m *model) newAltScreenView(content string) tea.View {
+	view := tea.NewView(content)
+	view.AltScreen = true
+	return view
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/cli/tui.go` around lines 309 - 399, The View method on model is
doing too much—split it into smaller helpers to reduce complexity: extract the
empty-case into renderEmptyView(), the grid building loop into buildGrid() or
renderGrid(rows, cols, choices, cursorX, cursorY, colWidth, styles), the
header/status/footer assembly into renderHeader(status,
styles)/renderFooter(styles) and the final layout/padding into
renderContent(width, height, leftPadding, totalHeightBase); update model.View to
call these helpers (renderEmptyView, renderGrid/buildGrid, renderHeader,
renderFooter, renderContent) so each helper has a single responsibility and the
main View only composes their results.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.gitignore:
- Line 5: Replace the invalid ignore pattern "./bin" with a root-relative or
directory pattern such as "/bin/" (or "bin/") in the .gitignore so generated
binaries are reliably ignored; update the entry that currently reads "./bin" to
"/bin/".

In `@internal/fs/fs_test.go`:
- Around line 74-75: Tests in internal/fs/fs_test.go use hardcoded forward-slash
paths like "/go/bin" which break on Windows; replace those literal expectations
with platform-aware constructions (e.g., use filepath.Join("go", "bin") or
filepath.FromSlash("/go/bin")) wherever you see the hardcoded strings (instances
of "/go/bin" and other "/"-separated expectations around the spans mentioned) so
the tests assert OS-normalized paths; import "path/filepath" in the test file if
not already present.

---

Nitpick comments:
In `@internal/cli/tui.go`:
- Around line 309-399: The View method on model is doing too much—split it into
smaller helpers to reduce complexity: extract the empty-case into
renderEmptyView(), the grid building loop into buildGrid() or renderGrid(rows,
cols, choices, cursorX, cursorY, colWidth, styles), the header/status/footer
assembly into renderHeader(status, styles)/renderFooter(styles) and the final
layout/padding into renderContent(width, height, leftPadding, totalHeightBase);
update model.View to call these helpers (renderEmptyView, renderGrid/buildGrid,
renderHeader, renderFooter, renderContent) so each helper has a single
responsibility and the main View only composes their results.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b74f8a1 and 4e0d779.

📒 Files selected for processing (10)
  • .gitignore
  • .golangci.yaml
  • .goreleaser.yaml
  • .mockery.yaml
  • README.md
  • cmd/root.go
  • internal/cli/tui.go
  • internal/cli/tui_test.go
  • internal/fs/fs.go
  • internal/fs/fs_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/cli/tui_test.go

nicholas-fedor and others added 4 commits February 28, 2026 05:15
- Replace hardcoded Unix-style paths with filepath.Join() calls
- Update test cases for DetermineBinDir and AdjustBinaryPath
- Ensure Windows compatibility for path separators
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Signed-off-by: Nick Fedor <nick@nickfedor.com>
- Replace filepath.Join with filepath.FromSlash for OS-agnostic paths
- Ensure correct path separators on Windows and Unix systems
@nicholas-fedor nicholas-fedor merged commit c453cbd into main Feb 28, 2026
16 of 17 checks passed
@nicholas-fedor nicholas-fedor deleted the fix/bubbletea-v2-api-migration branch February 28, 2026 12:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant