From 102de5a2c2709a9d132ff1bfcba1d8e91a436ddd Mon Sep 17 00:00:00 2001 From: bircni Date: Mon, 12 Oct 2026 20:27:12 +0200 Subject: [PATCH] fix(output): always report warnings on stderr Signed-off-by: bircni --- docs/migration-1.0.md | 3 ++- internal/app/human_output.go | 11 ++++++----- internal/app/human_output_test.go | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/migration-1.0.md b/docs/migration-1.0.md index a97834ec..dd8a42c3 100644 --- a/docs/migration-1.0.md +++ b/docs/migration-1.0.md @@ -298,7 +298,8 @@ of printing nothing. See `tea help formatting`. When stdout is not a terminal, lists print tab-separated values without header, color or links, with times in RFC 3339 UTC, and a `--page` hint goes to stderr; prefer `--json` in scripts. Commands that change resources report each change on -stderr only on a terminal. Create commands print the URL of the new resource on +stderr only on a terminal; warnings, such as a step that was skipped or failed, +always go to stderr. Create commands print the URL of the new resource on stdout, so `url=$(tea issue create …)` works; other changes print nothing when piped. Bodies are rendered as Markdown only on a terminal (`tea config set markdown raw` turns this off). See `tea help formatting` for the terminal layout. diff --git a/internal/app/human_output.go b/internal/app/human_output.go index 75474ca9..fd2ac993 100644 --- a/internal/app/human_output.go +++ b/internal/app/human_output.go @@ -133,19 +133,20 @@ func writeView[T any](a *application, cmd *cobra.Command, item T, view func(T) t // done reports a completed mutation on stderr, such as "✓ Closed issue alice/x#5 (Fix login)", // when people read stdout: on a TTY and without --json. func (a *application) done(cmd *cobra.Command, format string, args ...any) { + if !a.deps.Streams.OutTTY || a.options.json != "" { + return + } a.feedback(cmd, "✓", "green", format, args...) } -// warn reports a mutation that had nothing to do, such as "! Issue alice/x#5 (Fix login) is already -// closed", under the same conditions as done. +// warn reports on stderr a mutation that had nothing to do, such as "! Issue alice/x#5 (Fix login) +// is already closed", or a part of one that failed. Unlike done it is never silenced, so scripts +// and --json callers still learn about partial failures. func (a *application) warn(cmd *cobra.Command, format string, args ...any) { a.feedback(cmd, "!", "yellow", format, args...) } func (a *application) feedback(cmd *cobra.Command, icon, color, format string, args ...any) { - if !a.deps.Streams.OutTTY || a.options.json != "" { - return - } fmt.Fprintln(cmd.ErrOrStderr(), terminal.Colorize(color, icon, a.colorEnabled())+" "+terminal.SafeLine(fmt.Sprintf(format, args...))) } diff --git a/internal/app/human_output_test.go b/internal/app/human_output_test.go index 620f92db..4768f475 100644 --- a/internal/app/human_output_test.go +++ b/internal/app/human_output_test.go @@ -359,8 +359,8 @@ func TestMutationFeedback(t *testing.T) { tty bool }{ {name: "terminal", tty: true, out: "https://example.org/12\n", stderr: "✓ Created issue alice/x#12 (Fix[2J login)\n! Issue alice/x#12 (Fix[2J login) is already closed\n"}, - {name: "piped", out: "https://example.org/12\n"}, - {name: "JSON", tty: true, json: "number"}, + {name: "piped", out: "https://example.org/12\n", stderr: "! Issue alice/x#12 (Fix[2J login) is already closed\n"}, + {name: "JSON", tty: true, json: "number", stderr: "! Issue alice/x#12 (Fix[2J login) is already closed\n"}, } { t.Run(tc.name, func(t *testing.T) { deps, out, diagnostics := testDeps(t)