Skip to content

experimental/ssh: show a spinner per agent-shim bootstrap step - #6823

Open
rclarey wants to merge 1 commit into
mainfrom
spinner-cherry-pick
Open

rclarey wants to merge 1 commit into
mainfrom
spinner-cherry-pick

Conversation

@rclarey

@rclarey rclarey commented Sep 23, 2026

Copy link
Copy Markdown
Contributor

Changes

Show spinners per install stage for ssh agent-shim

Why

Looks nicer

Tests

test new progress renderer

The agent-shim command streamed the raw output of its bootstrap
subprocesses (uv, ucode, node/npm) straight to the terminal, which was
noisy and hard to follow.

Render each step with the shared cmdio spinner and a short description
instead: the subprocess output is captured and hidden, the spinner is
replaced with a checkmark when the step succeeds, and the full captured
output (stdout+stderr, in order) is printed only when a step fails. In a
non-interactive terminal it degrades to a plain checkmark line per step.

Co-authored-by: Isaac <no-reply@databricks.com>
@rclarey
rclarey requested a review from a team as a code owner September 23, 2026 15:00
@rclarey
rclarey requested a review from simonfaltum September 23, 2026 15:00
@eng-dev-ecosystem-bot

Copy link
Copy Markdown
Collaborator

Integration test report

Commit: 6bce09f

Run: 35878481008

Env 🔄​flaky ✅​pass 🙈​skip Time
✅​ aws linux 276 15 8:03
🔄​ aws windows 1 277 13 6:55
✅​ azure linux 275 15 7:26
✅​ azure windows 277 13 5:37
✅​ gcp linux 276 15 7:37
✅​ gcp windows 278 13 7:09
Test Name aws windows
🔄​ TestSyncIncrementalFileSync 🔄​f
Top 5 slowest tests (at least 2 minutes):
duration env testname
3:58 aws windows TestAccept
3:54 gcp windows TestAccept
3:15 azure windows TestAccept
2:47 gcp windows TestFilerWorkspaceFilesExtensionsReadDir
2:14 aws linux TestImportDirDoesNotOverwrite

@simonfaltum simonfaltum left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review (automated, 2 agent(s))

Verdict: Approved

0 Critical | 0 Major | 0 Gap | 2 Nit | 1 Suggestion

See inline comments for details.

Comment on lines +25 to +33
func newProgressUI(ctx context.Context) *progressUI {
// Mint the checkmark styles from a renderer targeting stderr so color handling
// stays centralized in cmdio.NewRenderer, matching cmdio's own spinner.
r, _ := cmdio.NewRenderer(ctx, os.Stderr)
return &progressUI{
w: os.Stderr,
check: r.NewStyle().Foreground(lipgloss.Color("10")).Render("✓"), // green
cross: r.NewStyle().Foreground(lipgloss.Color("9")).Render("✗"), // red
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[Suggestion] Outcome lines write to os.Stderr while the spinner writes to cmdio's stderr

runStep starts cmdio.NewSpinner(ctx), which paints on c.err from context (cmd.ErrOrStderr()). Check/cross lines and the failure dump go to a hardcoded os.Stderr minted here. In the default CLI process those are the same fd, so this works today. They diverge as soon as cobra's error writer is not os.Stderr (tests using SetErr, a wrapped stderr, NewTestContextWithStderr). Color is also decided on os.Stderr (cmdio.NewRenderer(ctx, os.Stderr)) while spinner interactivity is decided on c.err. The sandbox spinner wrapper prints the post-spinner mark with cmdio.LogString so both phases share one stream. The progressUI.w seam exists so tests can avoid MockDiscard's discarded stderr; cmdio.NewTestContextWithStderr already gives a capturable, non-TTY stderr and would make the split unnecessary.

Suggestion: Print the ✓/✗ line (and the failure dump) through cmdio, e.g. cmdio.LogString for the status line and LogString of the trimmed capture for the dump, and drive tests with NewTestContextWithStderr instead of injecting os.Stderr. If a raw writer is kept, initialize w from the same stream the spinner uses rather than os.Stderr.

cmdio.LogString(ctx, "Installing uv...")
if err := runShell(ctx, "curl -LsSf https://astral.sh/uv/install.sh | sh"); err != nil {
return fmt.Errorf("failed to install uv: %w", err)
if err := ui.runStep(ctx, "Installing dependencies", func(out io.Writer) error {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[Nit] First step label is vaguer than the siblings and than the error it wraps

The uv step is shown as "Installing dependencies" while the next two steps are "Installing Unity Gateway CLI" and "Installing Node.js". The old message was "Installing uv...", and the error returned from the same closure is still failed to install uv. On failure the user sees ✗ Installing dependencies followed by Error: failed to install uv: ..., which names two different things. The other labels name the tool; this one does not.

Suggestion: Use "Installing uv" (or "Installing uv..." if you want to keep the old wording).

Comment on lines +633 to +634
// runShell runs a shell snippet — for the curl|sh / curl|tar pipelines — writing
// combined stdout+stderr to out.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[Nit] runShell comment mentions a curl|tar pipeline that no longer exists

The new comment says runShell is for the curl|sh / curl|tar pipelines. The only caller is the uv curl … | sh line. Node extraction goes through runCommand(..., "tar", ...) in ensureNode, not runShell.

Suggestion: Drop the curl|tar mention; say it runs the uv curl|sh install (or "a shell snippet").

This branch has not been deployed

No deployments
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.

3 participants