fix(cli): require ANSI-capable terminal before colorizing - #3121
Open
mrunalp wants to merge 4 commits into
Open
Conversation
Follow-up to NVIDIA#3026, raised in review. `auto` treated any terminal as styleable, so `TERM=dumb openshell ...` still emitted escapes into a terminal that renders them literally. An unset TERM had the same problem. This is partly a regression that NVIDIA#3026 introduced. `console`, which drives indicatif and dialoguer, already refused to colorize when TERM is `dumb` or unset, and miette applies the same check through supports-color. NVIDIA#3026 overrides both with its own switch, so it replaced two working checks rather than only failing to add one. tracing and the owo-colors wrapper never had detection, so those two are a gap rather than a regression. Add the capability check to the `auto` branch only, matching console's unix rule: `dumb` is not capable, and an unset TERM is not capable because nothing identifies a capable terminal. Empty is treated as unset, which diverges from console — it reads `TERM=""` as capable since the value is not `dumb` — because an empty value names no terminal type and every other variable here already treats empty as unset. Because the check sits after the explicit branches, `--color always` and FORCE_COLOR still force styling on a dumb terminal, and `--color never` and NO_COLOR still suppress it on a capable one. TERM is a unix signal; Windows consoles enable virtual terminal processing and do not set it, so the check does not apply there. The existing pty test now pins TERM. It previously inherited the ambient value, which would make its outcome depend on the environment now that capability is consulted — CI runners frequently leave TERM unset. Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
mrunalp
requested review from
a team,
derekwaynecarr and
sjenning
as code owners
September 2, 2026 01:33
Member
|
/ok-to-test 1f0beb1 |
Signed-off-by: Evan Lezar <elezar@nvidia.com>
Signed-off-by: Evan Lezar <elezar@nvidia.com>
Signed-off-by: Evan Lezar <elezar@nvidia.com>
elezar
approved these changes
Sep 2, 2026
elezar
left a comment
Member
There was a problem hiding this comment.
Approved. I added three follow-up commits after reviewing the implementation:
af6eb3c8— refactor the per-stream ANSI-capability check while keeping theTERMpredicate independently testable.9122a8df— clarify the conservative table-color behavior in the public docs.c8a02a60— cover aSTATUStable with stdout on a TTY and stderr redirected.
These are intended as focused follow-ups, not a change in the PR’s direction. Mrunal, please decide whether you are happy to keep them or would prefer to push back on any of them.
Member
|
/ok to test |
@elezar, there was an error processing your request: See the following link for more information: https://docs.gha-runners.nvidia.com/cpr/e/1/ |
Member
|
/ok-to-test c8a02a6 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
--color autotreated any terminal as able to render ANSI, soTERM=dumb openshell ...printed escape sequences into a terminal that shows them literally. This adds a terminal-capability check to theautobranch, leaving the explicit overrides untouched.Related Issue
Fixes #3120
Changes
term_supports_ansi(TERM)and consult it inresolveunderautoonly: a stream is styled when it is a terminal and that terminal renders ANSI.consoleapplies on unix:dumbis not capable, and an unsetTERMis not capable because nothing identifies a capable terminal.TERMas unset. This diverges fromconsole, which readsTERM=""asOk("")and counts it capable because the value is notdumb. An empty value names no terminal type, and every other variable in this module already treats empty as unset, so it is handled the same way. The divergence is documented at the function.TERMas the signal.TERMin the existing pty test — see the note below.Because the check sits after the explicit branches, precedence is unchanged:
--color alwaysandFORCE_COLORstill force styling on adumbterminal, and--color neverandNO_COLORstill suppress it on a capable one.This is partly a regression from #3026
Worth stating plainly, since it affects how the change should be read. Two of the four styling paths were already correct before #3026:
console, which drawsindicatifprogress bars anddialoguerprompts, refuses to colorize whenTERMisdumbor unset (console-0.15.11/src/unix_term.rs).mietteapplies the same check for error rendering, viasupports-color.#3026 overrode both with a single process-wide switch so
--colorcould govern them, and that switch had no capability check — so it replaced two working checks rather than only failing to add one. The other two paths, thetracingformatter and theowo-colorswrapper, never had detection; those are a gap rather than a regression. This change fixes all four.Testing
Verified against the built binary on a pseudo-terminal:
TERM=xterm-256colorTERM=dumbTERMunsetTERM=""TERM=dumb --color alwaysTERM=dumb FORCE_COLOR=1TERM=xterm NO_COLOR=1TERM6 unit tests covering the capability rule and its interaction with each precedence branch, plus 2 pty integration tests: one asserting
TERM=dumbis not styled while a capable terminal in the same harness is (positive control), one asserting--color alwaysstill styles a dumb terminal.Both streams share one pty in the new harness, because the
owo-colorstable requires both streams to accept escapes — that is the shape of a real interactive session, which is the only place capability matters.I checked the new tests are not vacuous: reverting the condition to
stream_is_terminalalone failsdumb_terminal_is_not_styled_under_autowhile the other 10 in the file still pass.mise run pre-commitpassesmise run testpasses on the full workspace.Note for reviewers: an existing test changed
split_streams_stdout_ttynow pinsTERM=xterm-256color. It previously inherited the ambient value, which did not matter when onlyis_terminal()was consulted. Now that capability is part of the decision, an unsetTERM— common on CI runners — would make that test's positive control fail for environmental reasons. The pin is deliberate, not incidental.Checklist
docs/sandboxes/manage-sandboxes.mdx, and--colorbehavior in theopenshell-cliskill reference.