Skip to content

feat: interactive tree view for resource wait display#583

Open
bsquizz wants to merge 1 commit into
masterfrom
feat/resource-tree-view
Open

feat: interactive tree view for resource wait display#583
bsquizz wants to merge 1 commit into
masterfrom
feat/resource-tree-view

Conversation

@bsquizz

@bsquizz bsquizz commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Replaces the static spinner during resource wait with a full-screen Textual TUI showing a live, scrollable tree of namespace resources (ClowdEnvironments → ClowdApps → Deployments/StatefulSets) with status indicators and Kubernetes condition text
  • Runs the wait logic in a daemon thread so the tree view is a detachable display layer — exit with ctrl+q, re-enter with ctrl+t, cancel with ctrl+c
  • Filters tree to only show ClowdEnvironments referenced by deployed ClowdApps, shows resource status conditions next to pending resources, and adds a progress bar with countdown timer

Test plan

  • bonfire deploy <app> — verify tree renders with correct hierarchy, scrolls with arrow/pageup/pagedown, progress bar updates, and resources show ✓ when ready
  • Press ctrl+q during wait — verify tree exits to spinner fallback, press ctrl+t to re-enter tree view
  • Press ctrl+c — verify clean exit and namespace cleanup
  • bonfire namespace wait-on-resources <ns> — verify tree view works for standalone wait command
  • bonfire deploy --db-only <app> — verify it still uses the simple spinner (no tree view)
  • BONFIRE_PLAIN_OUTPUT=1 bonfire deploy <app> — verify non-interactive fallback (no TUI)
  • Verify CPU usage stays flat during wait (no 8fps polling)

🤖 Generated with Claude Code

Replace the static spinner with a full-screen Textual TUI that shows a
live tree of namespace resources while waiting for readiness. The tree
displays parent resources (ClowdEnvironments, ClowdApps) with their child
components (Deployments, StatefulSets), each with a status indicator and
Kubernetes condition text. A progress bar at the bottom shows overall
readiness percentage with a countdown timer.

Key changes:
- Add ResourceWaitApp using textual for scrollable, event-driven tree
- Run wait logic in a daemon thread so the TUI is a detachable view layer
- Support exiting tree view (ctrl+q) and re-entering it (ctrl+t)
- Filter tree to only show relevant ClowdEnvironments
- Show resource status conditions next to pending resources
- Add "Starting resource tree view..." spinner during watcher init

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • Added interactive resource tree view during resource wait operations for better visibility into resource status.
    • Enhanced progress visualization with real-time updates and keyboard shortcuts to toggle between tree and spinner views.
    • Improved feedback during deployments with detailed resource readiness tracking and timeout countdown display.

Walkthrough

This pull request integrates an interactive resource tree view into namespace/resource readiness flows. It adds watcher reuse infrastructure, implements a Rich/Textual-based interactive display with hierarchy visualization and progress tracking, and updates three CLI commands (namespace wait-on-resources, deploy, deploy-env) to use the new watcher and display functions instead of simple spinners.

Changes

Resource Watcher and Interactive Display Integration

Layer / File(s) Summary
Watcher infrastructure and refactoring
bonfire/openshift.py
create_resource_watcher(namespace) encapsulates watcher construction and initial snapshotting; wait_for_all_resources is refactored to accept an optional external watcher parameter and skip redundant start/stop when a watcher is provided.
Resource wait display and interactive tree UI
bonfire/output.py
New constants and helpers for resource status formatting; _build_hierarchy filters and organizes resources into a tree structure with ready/total counts; _make_tree_app_class() defines a Textual app that renders the tree, refreshes on resource changes, and displays progress with spinner and timeout countdown; resource_wait_display(watcher, timeout, wait_fn) orchestrates the wait in a background thread with interactive or spinner-based fallback UI modes.
CLI command integration with watcher and display
bonfire/bonfire.py
Imports resource_wait_display and create_resource_watcher; extends _wait_on_namespace_resources to accept and forward optional watcher parameter; updates namespace wait-on-resources, deploy, and deploy-env commands to create a watcher, wrap the wait in resource_wait_display, and ensure watcher cleanup in finally blocks.
Textual UI framework dependency
pyproject.toml
Adds textual>=1.0.0 to core runtime dependencies.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main feature addition: an interactive tree view for displaying resource wait progress, which is the central change across the modified files.
Description check ✅ Passed The description is directly related to the changeset, detailing the replacement of a static spinner with an interactive Textual TUI for resource waiting, including specific features like tree hierarchy, keyboard controls, and progress tracking.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/resource-tree-view

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bonfire/output.py (1)

413-708: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply Ruff formatting changes before merge.

CI is currently failing because ruff-format reformats bonfire/output.py. This is merge-blocking and should be fixed in this PR.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bonfire/output.py` around lines 413 - 708, This file fails CI because
ruff-format changed formatting; run ruff format (or your pre-commit formatter)
against bonfire/output.py and commit the resulting formatting changes so the
file matches ruff's style; focus on reformatting the affected functions and
classes such as _resource_status_summary, _resource_label_text,
_build_hierarchy, _make_tree_app_class, and resource_wait_display (or run ruff
--fix on the repo and commit) to resolve the merge-blocking formatting
differences.

Source: Pipeline failures

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bonfire/output.py`:
- Around line 585-590: The recursive helper _add_nodes dereferences resource.uid
unguarded; modify it to first retrieve uid via getattr(resource, "uid", None)
(or check hasattr) and only iterate children_map.get(uid, []) when uid is not
None/truthy to avoid crashes for resources missing uid; keep the rest of the
logic (creating node, setting self._node_map[resource.key], calling _add_nodes
recursively) unchanged and reference children_map and _build_hierarchy as the
surrounding context.
- Around line 547-557: The footer readiness counts in _refresh_status currently
compute total/ready from self._watcher.resources filtered only by
_SKIPPED_TREE_RESTYPES, which can differ from the tree/progress because
_build_hierarchy applies additional filtering (e.g., ClowdEnvironment
selection); update _refresh_status to reuse the same filtering pipeline as the
tree by calling the same helper used to produce the displayed items (invoke
_build_hierarchy or the shared filter logic) to derive the set of resources to
count, then compute total and ready from that resulting collection and pass them
to _update_status_label so the footer matches the tree/progress.

---

Outside diff comments:
In `@bonfire/output.py`:
- Around line 413-708: This file fails CI because ruff-format changed
formatting; run ruff format (or your pre-commit formatter) against
bonfire/output.py and commit the resulting formatting changes so the file
matches ruff's style; focus on reformatting the affected functions and classes
such as _resource_status_summary, _resource_label_text, _build_hierarchy,
_make_tree_app_class, and resource_wait_display (or run ruff --fix on the repo
and commit) to resolve the merge-blocking formatting differences.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: e860d595-8b8e-47f1-ab82-76d7054f28ac

📥 Commits

Reviewing files that changed from the base of the PR and between 626cbfc and 9d14a6b.

📒 Files selected for processing (4)
  • bonfire/bonfire.py
  • bonfire/openshift.py
  • bonfire/output.py
  • pyproject.toml

Comment thread bonfire/output.py
Comment on lines +547 to +557
def _refresh_status(self) -> None:
if self._done_event.is_set():
self.exit()
return
resources_snapshot = self._watcher.resources.copy()
filtered = {
k: r for k, r in resources_snapshot.items() if r.restype not in _SKIPPED_TREE_RESTYPES
}
total = len(filtered)
ready = sum(1 for r in filtered.values() if r.ready)
self._update_status_label(total, ready)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep footer readiness counts consistent with tree filtering.

Line 552-556 recomputes totals with only restype filtering, while tree/progress uses _build_hierarchy filtering (including relevant ClowdEnvironment filtering). This can show inconsistent ready/total values.

Proposed fix
         def _refresh_status(self) -> None:
             if self._done_event.is_set():
                 self.exit()
                 return
             resources_snapshot = self._watcher.resources.copy()
-            filtered = {
-                k: r for k, r in resources_snapshot.items() if r.restype not in _SKIPPED_TREE_RESTYPES
-            }
-            total = len(filtered)
-            ready = sum(1 for r in filtered.values() if r.ready)
+            _, _, total, ready = _build_hierarchy(resources_snapshot)
             self._update_status_label(total, ready)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bonfire/output.py` around lines 547 - 557, The footer readiness counts in
_refresh_status currently compute total/ready from self._watcher.resources
filtered only by _SKIPPED_TREE_RESTYPES, which can differ from the tree/progress
because _build_hierarchy applies additional filtering (e.g., ClowdEnvironment
selection); update _refresh_status to reuse the same filtering pipeline as the
tree by calling the same helper used to produce the displayed items (invoke
_build_hierarchy or the shared filter logic) to derive the set of resources to
count, then compute total and ready from that resulting collection and pass them
to _update_status_label so the footer matches the tree/progress.

Comment thread bonfire/output.py
Comment on lines +585 to +590
def _add_nodes(parent_node, resource):
label = _resource_label_text(resource)
node = parent_node.add(label, data=resource.key, expand=True)
self._node_map[resource.key] = node
for child in children_map.get(resource.uid, []):
_add_nodes(node, child)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard tree recursion against resources without uid.

_build_hierarchy already handles missing uid defensively, but Line 589 dereferences resource.uid unguarded. Any resource lacking uid will crash tree refresh.

Proposed fix
                 def _add_nodes(parent_node, resource):
                     label = _resource_label_text(resource)
                     node = parent_node.add(label, data=resource.key, expand=True)
                     self._node_map[resource.key] = node
-                    for child in children_map.get(resource.uid, []):
+                    resource_uid = getattr(resource, "uid", None)
+                    for child in children_map.get(resource_uid, []):
                         _add_nodes(node, child)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bonfire/output.py` around lines 585 - 590, The recursive helper _add_nodes
dereferences resource.uid unguarded; modify it to first retrieve uid via
getattr(resource, "uid", None) (or check hasattr) and only iterate
children_map.get(uid, []) when uid is not None/truthy to avoid crashes for
resources missing uid; keep the rest of the logic (creating node, setting
self._node_map[resource.key], calling _add_nodes recursively) unchanged and
reference children_map and _build_hierarchy as the surrounding context.

Comment thread bonfire/output.py
return roots, children, total, ready


def _make_tree_app_class():

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

for textual its rather uncommon to make app classes in a helper method (that more of a confused web framework things)

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.

2 participants