bashunit
bashunit copied to clipboard
use tput or similar
Using tput instead of direct ANSI escape sequences for changing text and background colors offers several advantages:
- Portability: tput adapts to different terminal capabilities using the terminfo database, ensuring scripts work across various terminals.
- Readability: Commands with tput are more descriptive, making the code easier to understand and maintain.
- Flexibility: tput provides access to a wide range of terminal functions beyond color changes, like cursor movement and screen clearing.
- Safety: Relying on terminfo reduces the risk of injecting harmful or incorrect escape sequences.
- Adaptability: tput adjusts to the terminal's supported features, preventing unexpected output in terminals that don't support certain colors or effects.
In summary, tput offers a safer, more portable, and maintainable solution for terminal manipulation than hard-coding ANSI escape sequences.
But not everything that glitters is gold; in PR #245, we saw that a large number of tests failed or became inconsistent across different environments, which really makes us reconsider how to implement these kinds of tools. Therefore, we need to study whether we want to use tput or some alternative and how to do so in a way that does not reduce the reliability of bashunit.
As the pioneer of this topic, I can add that after implementing tput
and patching some codes into assert_equals_ignore_colors
, really only the snapshots failed across different terminal emulators, since they capture everything, including the environment-specific stuff.
In addition to already mentioned benefits, a specific scenario improved via the use of tput
that is probably not too specific to me — committing from inside of neovim (via fugitive.vim
, for example).
This is the pre-commit hook output in the message area with the hardcoded colors:
lib/bashunit
^[[1m^[[32mbashunit^[[0m - 0.10.1
Running scripts/__tests__/example_test.sh
^[[33m↷ Skipped^[[0m: Skipped test example
^[[2m Not needed in this env^[[0m
^[[36m✒ Incomplete^[[0m: Incomplete test example
^[[2m Add unit tests for scripts^[[0m
^[[32m✓ Passed^[[0m: Passing test example
^[[2mTests: ^[[0m ^[[32m1 passed^[[0m, ^[[33m1 skipped^[[0m, ^[[36m1 incomplete^[[0m, 3 total
^[[2mAssertions:^[[0m ^[[32m1 passed^[[0m, ^[[33m1 skipped^[[0m, ^[[36m1 incomplete^[[0m, 3 total
^[[46mSome tests incomplete^[[0m
This is the output with tput
implemented:
lib/bashunit
bashunit - 0.10.1
Running scripts/__tests__/example_test.sh
↷ Skipped: Skipped test example
Not needed in this env
✓ Passed: Passing test example
✒ Incomplete: Incomplete test example
Add unit tests for scripts
Tests: 1 passed, 1 skipped, 1 incomplete, 3 total
Assertions: 1 passed, 1 skipped, 1 incomplete, 3 total
Some tests incomplete
This behavior is largely explained in the first post under "Portability" but to add more resolution — in a limited environment where terminal output is not expected or needed, $TERM
is (hopefully) set to dumb
, which means no fancy output please. This ensures exactly what my example highlights: just in case there happens to be output, make it still readable by not printing any color codes.
Thanks for adding extra context.
I also have an observation to make after trying out tput, the GitHub actions went from being colored to black and white.
The truth is that color in actions helps with reading them since a very common use of bashunit is precisely its execution during CI/CD.
Current bashunit output on actions:
Output using tput on actions:
I also have an observation to make after trying out tput, the GitHub actions went from being colored to black and white.
Yes, that is the effect of TERM
being unset in that environment, which tput
relies on to output anything.
There is surely a middle ground but it has not revealed itself yet.
Ok, I've figured out a way to serve my usecase and leave the rest intact.
The solution is to slightly modify the function I added here so whenever TERM
is explicitly dumb
, it outputs nothing:
https://github.com/TypedDevs/bashunit/blob/46138132838dc7544296bd2760f1f461fa7b4eb6/src/colors.sh#L9-L18
This should keep colors in every other scenario, even when TERM
is undefined:
sgr() {
- local codes=${1:-0}
+ local codes return
+
+ codes=${1:-0}
shift
for c in "$@"; do
codes="$codes;$c"
done
- echo $'\e'"[${codes}m"
+ return=$'\e'"[${codes}m"
+
+ if [[ $TERM == 'dumb' ]]; then
+ return=""
+ fi
+
+ echo "$return"
}
Simple as that, let me know if a PR like that would be welcome!
Thanks, @h0adp0re ! Feel free to create a PR and we can continue the conversation from there 👍🏼
@h0adp0re ping on this one? :)