Add a console output backend for condensed deploy output - #1918
Conversation
Register a `console` logger in the output framework that replaces the raw
SSHKit command firehose on the terminal with a condensed view: a header
panel, one section per deploy phase, a live per-host status line, and a
summary panel. Raw output is suppressed on success and replayed for any
host that fails; -v/--verbose restores the full firehose.
It reconstructs the view from the line stream every backend already
receives — CLI phase markers (say), host-tagged SSHKit lines, and the
modify.kamal exception payload — so no deploy code needs to know it
exists. When active it owns the screen, so the raw stream is routed to a
null sink (still teed to other backends, e.g. file). On a TTY it renders
concurrent per-host spinners via tty-spinner/pastel; non-TTY output uses
a deterministic line-based renderer.
Opt in with `output: { console: {} }`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CodeQL flagged the trailing-strip regex as polynomial ReDoS: end-anchored with + but not start-anchored, so it rescans across start positions. The input is Kamal's own say() markers, not untrusted data, but the linear non-regex strip is clearer and clears the alert. strip already removes whitespace, so only trailing dots/colons remain. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in console output backend that renders a condensed deploy view (header + per-phase progress + per-host results + summary), including a TTY spinner renderer and a deterministic non-TTY renderer. This plugs into the existing output framework and wires CLI markers / SSHKit stream metadata into the console backend.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Changes:
- Introduces
Kamal::Output::ConsoleLoggerplus plain/TTY renderers for condensed deploy output. - Adds
consoleto output configuration + documentation and extends CLI/SSHKit wiring to support marker/host/severity context. - Adds
pastelandtty-spinnerdependencies and tests for the new backend.
Reviewed changes
Copilot reviewed 11 out of 12 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/output/console_logger_test.rb | Adds unit coverage for console backend rendering and failure attribution. |
| test/configuration/output_test.rb | Verifies console backend configuration, settings validation, and enablement. |
| lib/kamal/output/console/tty_renderer.rb | Implements interactive TTY spinner rendering per phase/host. |
| lib/kamal/output/console/renderer.rb | Adds shared formatting/panel helpers and color handling for console renderers. |
| lib/kamal/output/console/plain_renderer.rb | Implements deterministic, line-based rendering for non-TTY/CI output. |
| lib/kamal/output/console_logger.rb | Core console backend: reconstructs phases/host statuses and emits summary + retained output. |
| lib/kamal/configuration/output.rb | Registers console as a valid output logger type. |
| lib/kamal/configuration/docs/output.yml | Documents the new output.console settings and behavior. |
| lib/kamal/commander.rb | Routes SSHKit formatter output to a null sink when console backend is enabled. |
| lib/kamal/cli/base.rb | Tags say markers with thread-local color metadata for the console backend. |
| kamal.gemspec | Adds runtime deps for TTY rendering (pastel, tty-spinner). |
| Gemfile.lock | Locks the new dependencies and their transitive requirements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Honor -v/--verbose: skip the null sink and disable the console renderer so the raw SSHKit firehose shows; close the null-sink FD at exit. - Attribute exception failures by whole-host token (10.0.0.1 no longer flagged by a failure on 10.0.0.10) and consider all configured hosts, so a host that failed before emitting output is still flagged. - Drop the severity-based failure path: SSHKit logs command output at DEBUG (never ERROR) within host context, so it was dead code; failures are attributed from the modify.kamal exception payload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
lib/kamal/commander.rb:228
- When
@console_outputis enabled,@null_output = File.open(File::NULL, "w")will overwrite any previously-open null sink without closing it first (and theelsebranch never closes it when switching back). If output is configured more than once in a process, this will leak FDs; it also keeps the sink open even after the command finishes. Consider closing any existing sink before replacing it, and closing+nil’ing it when console output is not active.
if @console_output
@null_output = File.open(File::NULL, "w")
SSHKit.config.output = Kamal::Output::Formatter.new(@null_output, output_logger)
else
SSHKit.config.output = Kamal::Output::Formatter.new($stdout, output_logger)
end
… on reset - Only magenta say markers open a phase; red/yellow (errors/warnings) and any colorless say now render as notices instead of spurious phases, and a say is flagged by origin so non-magenta messages no longer disappear from the terminal. TTY notices buffer during a phase and flush once its spinners resolve, so the live region isn't corrupted. - Close @null_output before niling it in reset so repeated commands in a long-lived process (aliases) don't leak a /dev/null FD. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Example output via a script that simulates a deploy, minus the colors. Successful deploy : ..and one with a failure : |
What
consoleoutput backend that condenses a deploy into a header panel, per-phase sections, live per-host status lines, and an end-of-run summary — opt in withoutput: { console: {} }.file);-v/--verboserestores it.Why
A full deploy buries prompts and errors under a wall of interleaved SSHKit output; this gives an at-a-glance view of progress while keeping any failure — and the failing host's retained output — prominent. It's built as an opt-in backend on the existing output framework and reconstructed entirely from the line stream every backend already receives (phase markers, host-tagged lines, the modify.kamal exception payload), so it adds no deploy-path coupling and leaves current behavior unchanged when off.
Testing
bin/test test/output/console_logger_test.rb test/configuration/output_test.rb→ 20 runs, 0 failures.main(git-email and Docker/arch env issues), none from this change.rubocopon all changed files → clean.Composed with Claude Code (Opus 4.8)