Skip to content

feat(ui): add CI-friendly plain text output mode#172

Merged
pbrissaud merged 2 commits intomainfrom
feat/ci-no-spinner
Feb 26, 2026
Merged

feat(ui): add CI-friendly plain text output mode#172
pbrissaud merged 2 commits intomainfrom
feat/ci-no-spinner

Conversation

@pbrissaud
Copy link
Member

Summary

  • Auto-détecte l'absence de TTY (CI, pipes) et désactive les spinners automatiquement
  • Ajoute un flag persistant --no-spinner pour forcer le mode texte brut
  • En mode CI, WaitMessage et TimedSpinner affichent du texte simple (, , ) au lieu des animations qui produisaient des centaines de lignes en environnement non-interactif

Before / After

Before (CI output) :

▀  Creating kind cluster 'kubeasy' (Kubernetes 1.35.0) (0s)
 ▀ Creating kind cluster 'kubeasy' (Kubernetes 1.35.0) (0s)
 ▄ Creating kind cluster 'kubeasy' (Kubernetes 1.35.0) (0s)
...

After (CI output) :

• Creating kind cluster 'kubeasy' (Kubernetes 1.35.0)...
✓ Creating kind cluster 'kubeasy' (Kubernetes 1.35.0) (completed in 19s)

Test plan

  • Vérifier que les spinners fonctionnent toujours normalement dans un terminal interactif
  • Vérifier la sortie CI en lançant kubeasy setup 2>&1 | cat (pipe = pas de TTY)
  • Vérifier le flag explicite : kubeasy --no-spinner setup

🤖 Generated with Claude Code

Disable spinner animations when stdout is not a TTY (auto-detected) or
when the --no-spinner persistent flag is passed. In CI mode, WaitMessage
and TimedSpinner fall back to plain text with bullet/checkmark/cross
prefixes instead of animated spinners that produce garbled output in
non-interactive environments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Contributor

claude bot commented Feb 26, 2026

Claude finished @pbrissaud's task in 1m 25s —— View job


PR Review: feat(ui): add CI-friendly plain text output mode

  • Gather context and read changed files
  • Review cmd/root.go changes
  • Review internal/ui/ui.go changes
  • Post detailed feedback

Good implementation overall — the approach is clean and minimal. The TTY detection is standard and correct, and the --no-spinner escape hatch is a useful addition. A few things worth addressing:

Issues

1. CI failure message in WaitMessage swallows error context

internal/ui/ui.go, WaitMessage CI branch (~line 204-207):

if err != nil {
    fmt.Printf("✗ %s\n", message)  // error detail is lost
    return err
}

The error is returned to the caller, but in CI the only visible output is ✗ Creating namespace with no indication of why it failed. TimedSpinner has the same issue. Consider:

fmt.Printf("✗ %s: %v\n", message, err)

Fix this →

2. Spinner() and MultiSpinner don't respect ciMode

Neither Spinner() (~line 20) nor MultiSpinner.Add() (~line 161) check ciMode. They're currently unused from cmd/, so it's not a regression — but it creates a footgun if someone adds a ui.Spinner(...) call later and wonders why CI is broken again. At minimum a comment on Spinner() noting it doesn't support CI mode would prevent this confusion.

Nits

3. CI output writes failures to stdout instead of stderr

fmt.Printf("✗ %s (failed after %s)\n", message, elapsed)

Failure indicators () are typically more useful on stderr so they're visible regardless of stdout redirection. Worth using fmt.Fprintf(os.Stderr, ...) for the error case.

4. --no-spinner flag description could mention auto-detection

Current: "Disable spinner animations (plain text output, useful for CI/non-interactive environments)"
Since auto-detection already handles non-TTY environments, the flag is mainly for edge cases where auto-detection doesn't work (e.g., pseudo-TTY in Docker). The description could clarify: "Force plain text output (spinners are disabled automatically when stdout is not a TTY)"

Positive notes

  • TTY detection via term.IsTerminal(int(os.Stdout.Fd())) is the correct standard approach
  • PersistentPreRun is the right place to initialize this before any subcommand runs
  • The TimedSpinner CI path correctly preserves timing info (completed in Xs) — great for CI log readability
  • No external dependencies added; golang.org/x/term is already a standard indirect dependency

@sentry
Copy link

sentry bot commented Feb 26, 2026

Codecov Report

❌ Patch coverage is 0% with 20 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/ui/ui.go 0.00% 20 Missing ⚠️

📢 Thoughts on this report? Let us know!

- Print error detail on failure: "✗ <step>: <err>" instead of swallowing context
- Write failure lines to stderr so they're visible regardless of stdout redirection
- Add comments on Spinner() and MultiSpinner warning they don't respect ciMode
- Clarify --no-spinner flag description to mention TTY auto-detection

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Contributor

claude bot commented Feb 26, 2026

Claude finished @pbrissaud's task in 40s —— View job


Re-review: All previous feedback addressed ✓

  • Read updated files
  • Verify previous feedback was addressed
  • Post updated review

The updated commit (c9c546d) correctly addresses all four items from the prior review:

1. Error context included in CI failure output
WaitMessage now uses fmt.Fprintf(os.Stderr, "✗ %s: %v\n", message, err) and TimedSpinner uses fmt.Fprintf(os.Stderr, "✗ %s (failed after %s): %v\n", message, elapsed, err) — both the error detail and stderr routing are now correct.

2. Spinner() and MultiSpinner documented
Both now carry explicit comments noting they don't support ciMode, making the footgun visible to future contributors.

3. Failure output goes to stderr
All lines in both WaitMessage and TimedSpinner now use fmt.Fprintf(os.Stderr, ...). Success lines remain on stdout — correct split.

4. --no-spinner flag description clarified
Updated to "Force plain text output (spinners are disabled automatically when stdout is not a TTY)" — clearly communicates both the auto-detection and the manual escape hatch.


The implementation is clean and minimal. No further issues found. Ready to merge.

@pbrissaud pbrissaud merged commit 1d4489e into main Feb 26, 2026
6 of 7 checks passed
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