fix(progress): probe stderr (not stdout) for TTY auto-detection#13837
fix(progress): probe stderr (not stdout) for TTY auto-detection#13837glours wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes Compose’s progress renderer auto-detection by probing the TTY status of stderr (the stream actually used by the progress UI) instead of stdout, restoring expected behavior when stdout is redirected but stderr remains attached to a terminal.
Changes:
- Switch auto-mode TTY probing from
dockerCli.Out().IsTerminal()todockerCli.Err().IsTerminal(). - Extract progress EventProcessor selection into
selectEventProcessor(...)to make the logic unit-testable. - Add unit tests covering auto-mode and explicit
--progressmodes.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| cmd/compose/compose.go | Routes progress auto-detection to stderr and factors selection logic into selectEventProcessor. |
| cmd/compose/compose_progress_test.go | Adds tests to validate stream probing behavior and explicit progress modes. |
| func newMockCli(t *testing.T, out, errStream *streams.Out) *mocks.MockCli { | ||
| t.Helper() | ||
| cli := mocks.NewMockCli(gomock.NewController(t)) | ||
| cli.EXPECT().Out().Return(out).AnyTimes() | ||
| cli.EXPECT().Err().Return(errStream).AnyTimes() | ||
| return cli | ||
| } |
| import ( | ||
| "fmt" | ||
| "os" | ||
| "testing" | ||
|
|
||
| "github.com/creack/pty" | ||
| "github.com/docker/cli/cli/streams" | ||
| "go.uber.org/mock/gomock" | ||
| "gotest.tools/v3/assert" | ||
|
|
||
| "github.com/docker/compose/v5/cmd/display" | ||
| "github.com/docker/compose/v5/pkg/mocks" | ||
| ) | ||
|
|
||
| // saveDisplayMode snapshots display.Mode and restores it on cleanup, since | ||
| // selectEventProcessor mutates this global as a side effect. | ||
| func saveDisplayMode(t *testing.T) { | ||
| t.Helper() | ||
| original := display.Mode | ||
| t.Cleanup(func() { display.Mode = original }) | ||
| } |
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The fix correctly aligns TTY auto-detection with the stream the renderer actually writes to — probing Err() (stderr) instead of Out() (stdout). This restores the expected behavior for docker compose up | tee log and similar piping scenarios.
What was reviewed:
selectEventProcessorcorrectly callsdockerCli.Err().IsTerminal()(wasdockerCli.Out().IsTerminal())- All explicit
--progressmodes (tty,plain,quiet,json,none) are correctly handled in the extracted function - Error handling is properly propagated via
(api.EventProcessor, error)return signature - The new test file covers the core regression scenario (
stderr TTY, stdout piped → Full) and all explicit-mode branches saveDisplayModecorrectly saves/restores thedisplay.Modeglobal to prevent test pollution
Minor observations (not blocking):
- In the
ModeAuto+ TTY branch,display.Modeis not updated toModeTTY(staysModeAuto). This is consistent with the old code and has no observable effect since no downstream code checksdisplay.Mode == display.ModeTTY— onlydisplay.ModeJSONis gated on. - With
ansi=alwaysand a non-TTY stderr,display.Modeis left asModeTTY(set by the outeransiswitch) while aplainWriteris returned. Again, no current code path is broken by this inconsistency. - Tests do not use
t.Parallel(), which is fine given thedisplay.Modeglobal mutation; just worth noting if parallelism is added later.
The core bugfix is sound and the test coverage for the regression is well-structured.
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The fix correctly aligns TTY auto-detection with the stream that the progress renderer actually writes to. The extracted selectEventProcessor function is well-structured and the new tests validate the regression path.
No high or medium severity issues were found in the changed code.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
ccbe129 to
f0a76df
Compare
|
/review |
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
The fix correctly aligns TTY auto-detection with the stream the renderer actually writes to (dockerCli.Err() / stderr instead of dockerCli.Out() / stdout). This restores the expected behavior when stdout is piped while stderr remains a terminal (e.g., docker compose up | tee log).
What was verified:
- The core fix (
Err().IsTerminal()replacingOut().IsTerminal()) is correct and directly addresses the reported issue. - The auto-mode TTY path in
selectEventProcessordoes not setdisplay.Mode = display.ModeTTY— but this is pre-existing behavior (the old inline switch also omitted this), and all downstream consumers ofdisplay.Modeare consistent with it remaining"auto"in this path (none require it to be"tty"for correct behavior). - The regression test
TestSelectEventProcessor_AutoMode_StdoutPipedStderrTTYuses genuinely distinct streams (a real PTY for stderr, a pipe for stdout), providing real discriminating power for the exact scenario being fixed. - Extraction of
selectEventProcessoris a clean refactor that improves testability without behavioral regressions.
No confirmed or likely bugs found in the changed code.
The progress UI writes to dockerCli.Err() but the auto-mode selector was probing dockerCli.Out().IsTerminal(), introduced when the EventProcessor was moved to the CLI layer. Any context that pipes stdout while keeping stderr attached — `docker compose up | tee log`, some CI wrappers, PowerShell native-command capture — silently dropped to plain mode. Align the detection with the stream the renderer actually targets, restoring v4 behavior. Extract the switch into selectEventProcessor so the auto-mode logic can be unit-tested with a real pty pair. Fixes docker#13570 Signed-off-by: Guillaume Lours <glours@users.noreply.github.com>
f0a76df to
041ff77
Compare
|
/review |
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
This PR correctly fixes TTY auto-detection for the progress renderer by probing dockerCli.Err().IsTerminal() instead of dockerCli.Out().IsTerminal(), aligning the detection with the stream the renderer actually writes to.
What was reviewed:
selectEventProcessorextraction and TTY probe fix incmd/compose/compose.go- New unit tests in
cmd/compose/compose_progress_test.gousing a real pty pair
No confirmed or likely bugs found. The refactor faithfully preserves existing behavior while fixing the stdout/stderr mismatch. All explicit progress modes (tty, plain, quiet, json) correctly set display.Mode. The new test coverage for auto-mode TTY detection is well-structured.
What I did
The progress UI writes to dockerCli.Err() but the auto-mode selector was probing dockerCli.Out().IsTerminal(), introduced when the EventProcessor was moved to the CLI layer. Any context that pipes stdout while keeping stderr attached —
docker compose up | tee log, some CI wrappers, PowerShell native-command capture — silently dropped to plain mode.Align the detection with the stream the renderer actually targets, restoring v4 behavior. Extract the switch into selectEventProcessor so the auto-mode logic can be unit-tested with a real pty pair.
Fixes #13570
Related issue
(not mandatory) A picture of a cute animal, if possible in relation to what you did