这是indexloc提供的服务,不要输入任何密码
Skip to content

feat(colors): add options for forcing color/no-color #990

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ jobs:
run: pnpm install

- name: Build & Unit Test
run: pnpm turbo run test --scope=cli
run: pnpm turbo -- run test --scope=cli --color
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the positional arguments were being passed in before:

./turbow.sh "run" "test"

This now gets sent as:

./turbow.sh "run" "test" "--scope=cli" "--color"

Likely a change in behavior when switching to pnpm from yarn


- name: Lint
run: pnpm turbo run lint --scope=cli
run: pnpm turbo -- run lint --scope=cli --color

- name: E2E Tests
run: |
Expand Down Expand Up @@ -143,4 +143,6 @@ jobs:

- name: Check examples
shell: bash
env:
FORCED_COLOR: true
run: ./scripts/run-examples.sh
7 changes: 6 additions & 1 deletion cli/cmd/turbo/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func main() {
traceFile := ""
cpuprofileFile := ""
argsEnd := 0
colorMode := uiPkg.GetColorModeFromEnv()
for _, arg := range args {
switch {
case strings.HasPrefix(arg, "--heap="):
Expand All @@ -36,6 +37,10 @@ func main() {
traceFile = arg[len("--trace="):]
case strings.HasPrefix(arg, "--cpuprofile="):
cpuprofileFile = arg[len("--cpuprofile="):]
case arg == "--color":
colorMode = ui.ColorModeForced
case arg == "--no-color":
colorMode = ui.ColorModeSuppressed
default:
// Strip any arguments that were handled above
args[argsEnd] = arg
Expand All @@ -44,10 +49,10 @@ func main() {
}
args = args[:argsEnd]

ui := ui.BuildColoredUi(colorMode);
c := cli.NewCLI("turbo", turboVersion)

util.InitPrintf()
ui := ui.Default()

c.Args = args
c.HelpWriter = os.Stdout
Expand Down
4 changes: 2 additions & 2 deletions cli/internal/run/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -810,10 +810,10 @@ func replayLogs(logger hclog.Logger, prefixUi cli.Ui, runOptions *RunOptions, lo
if outputLogsMode == HashLogs {
//Writing to Stdout only the "cache hit, replaying output" line
scan.Scan()
prefixUi.Output(ui.StripAnsi(string(scan.Bytes())))
prefixUi.Output(string(scan.Bytes()))
} else {
for scan.Scan() {
prefixUi.Output(ui.StripAnsi(string(scan.Bytes()))) //Writing to Stdout
prefixUi.Output(string(scan.Bytes())) //Writing to Stdout
}
}
}
Expand Down
54 changes: 54 additions & 0 deletions cli/internal/ui/colors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package ui

import (
"os"

"github.com/fatih/color"
)

type ColorMode int

const (
ColorModeUndefined ColorMode = iota + 1
ColorModeSuppressed
ColorModeForced
)

func GetColorModeFromEnv() ColorMode {
// The FORCED_COLOR behavior and accepted values are taken from the supports-color NodeJS Package:
// The accepted values as documented are "0" to disable, and "1", "2", or "3" to force-enable color
// at the specified support level (1 = 16 colors, 2 = 256 colors, 3 = 16M colors).
// We don't currently use the level for anything specific, and just treat things as on and off.
//
// Note: while "false" and "true" aren't documented, the library coerces these values to 0 and 1
// respectively, so that behavior is reproduced here as well.
// https://www.npmjs.com/package/supports-color

switch forcedColor := os.Getenv("FORCED_COLOR"); {
case forcedColor == "false" || forcedColor == "0":
return ColorModeSuppressed
case forcedColor == "true" || forcedColor == "1" || forcedColor == "2" || forcedColor == "3":
return ColorModeForced
default:
return ColorModeUndefined
}
}

func applyColorMode(colorMode ColorMode) ColorMode {
switch colorMode {
case ColorModeForced:
color.NoColor = false
case ColorModeSuppressed:
color.NoColor = true
case ColorModeUndefined:
default:
// color.NoColor already gets its default value based on
// isTTY and/or the presence of the NO_COLOR env variable.
}

if color.NoColor {
return ColorModeSuppressed;
} else {
return ColorModeForced;
}
}
50 changes: 38 additions & 12 deletions cli/internal/ui/ui.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ui

import (
"fmt"
"io"
"math"
"os"
"regexp"
Expand All @@ -23,13 +24,6 @@ var WARNING_PREFIX = color.New(color.Bold, color.FgYellow, color.ReverseVideo).S

var ansiRegex = regexp.MustCompile(ansiEscapeStr)

func StripAnsi(str string) string {
if !IsTTY {
return ansiRegex.ReplaceAllString(str, "")
}
return str
}

// Dim prints out dimmed text
func Dim(str string) string {
return gray.Sprint(str)
Expand All @@ -50,9 +44,6 @@ func rgb(i int) (int, int, int) {

// Rainbow function returns a formated colorized string ready to print it to the shell/terminal
func Rainbow(text string) string {
if !IsTTY {
return text
}
var rainbowStr []string
for index, value := range text {
r, g, b := rgb(index)
Expand All @@ -63,13 +54,48 @@ func Rainbow(text string) string {
return strings.Join(rainbowStr, "")
}

type stripAnsiWriter struct {
wrappedWriter io.Writer
}

func (into *stripAnsiWriter) Write(p []byte) (int, error) {
n, err := into.wrappedWriter.Write(ansiRegex.ReplaceAll(p, []byte{}))
if (err != nil) {
// The number of bytes returned here isn't directly related to the input bytes
// if ansi color codes were being stripped out, but we are counting on Stdout.Write
// not failing under typical operation as well.
return n, err
}

// Write must return a non-nil error if it returns n < len(p). Consequently, if the
// wrappedWrite.Write call succeeded we will return len(p) as the number of bytes
// written.
return len(p), nil
}

// Default returns the default colored ui
func Default() *cli.ColoredUi {
return BuildColoredUi(ColorModeUndefined)
}

func BuildColoredUi(colorMode ColorMode) *cli.ColoredUi {
colorMode = applyColorMode(colorMode);

var outWriter, errWriter io.Writer

if colorMode == ColorModeSuppressed {
outWriter = &stripAnsiWriter{wrappedWriter: os.Stdout}
errWriter = &stripAnsiWriter{wrappedWriter: os.Stderr}
} else {
outWriter = os.Stdout
errWriter = os.Stderr
}

return &cli.ColoredUi{
Ui: &cli.BasicUi{
Reader: os.Stdin,
Writer: os.Stdout,
ErrorWriter: os.Stderr,
Writer: outWriter,
ErrorWriter: errWriter,
},
OutputColor: cli.UiColorNone,
InfoColor: cli.UiColorNone,
Expand Down
40 changes: 40 additions & 0 deletions docs/pages/docs/reference/command-line-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,46 @@ Boolean options can be enabled as follows:
--<option>=false
```

## Global Arguments

The following flags apply to all commands.

#### `--color`

Forces the use of color even when the output stream is not considered to be a TTY terminal.
This can be used to enable turbo's color output for CI runners such as Github Actions which
have support for rendering color in their log output.

```sh
turbo run build --color
```

Alternatively, you can also enable color using the `FORCED_COLOR` environment variable (borrowed
from the [supports-color nodejs package](https://www.npmjs.com/package/supports-color)). Going
this route may also unlock some additional colored output from the actual tasks themselves if
they use `supports-color` to determine whether or not to output with colored output.

```sh
declare -x FORCED_COLOR=1
turbo run build
```

#### `--no-color`

Suppresses the use of color in the output when running turbo in an interactive / TTY session.

```sh
turbo run build --no-color
```

Alternatively, you can also suppress color using the `FORCED_COLOR` environment variable (borrowed
from the [supports-color nodejs package](https://www.npmjs.com/package/supports-color)).

```sh
declare -x FORCED_COLOR=0
turbo run build
```

## `turbo run <task>`

Run NPM scripts across all packages in specified scope. Tasks must be specified in your `pipeline` configuration.
Expand Down