From a82500f162d631bcbbee73143d88e4069142b7c1 Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Mon, 6 Aug 2018 05:46:49 -0700 Subject: [PATCH] Support dumb terminals by turning off color (#6246) ANSI colors do not work on dumb terminals, in situations when minio is running as a service under systemd. This PR ensures we turn off color in those situations. --- cmd/globals.go | 74 ++++++++++++++++++++++++++++++++++++++---- cmd/logger/utils.go | 48 +++++++++++++++++++++++---- cmd/update-main.go | 14 +++----- cmd/update-notifier.go | 13 +++----- 4 files changed, 118 insertions(+), 31 deletions(-) diff --git a/cmd/globals.go b/cmd/globals.go index b1bc8c82d..7c8c1ce9a 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -18,10 +18,12 @@ package cmd import ( "crypto/x509" + "fmt" "os" "runtime" "time" + isatty "github.com/mattn/go-isatty" "github.com/minio/minio-go/pkg/set" etcd "github.com/coreos/etcd/clientv3" @@ -219,12 +221,72 @@ var ( // global colors. var ( - colorBold = color.New(color.Bold).SprintFunc() - colorRed = color.New(color.FgRed).SprintfFunc() - colorBlue = color.New(color.FgBlue).SprintfFunc() - colorYellow = color.New(color.FgYellow).SprintfFunc() - colorBgYellow = color.New(color.BgYellow).SprintfFunc() - colorBlack = color.New(color.FgBlack).SprintfFunc() + // Check if we stderr, stdout are dumb terminals, we do not apply + // ansi coloring on dumb terminals. + isTerminal = func() bool { + return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd()) + } + + colorBold = func() func(a ...interface{}) string { + if isTerminal() { + return color.New(color.Bold).SprintFunc() + } + return fmt.Sprint + }() + colorRed = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgRed).SprintfFunc() + } + return fmt.Sprintf + }() + colorBlue = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgBlue).SprintfFunc() + } + return fmt.Sprintf + }() + colorYellow = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgYellow).SprintfFunc() + } + return fmt.Sprintf + }() + colorCyanBold = func() func(a ...interface{}) string { + if isTerminal() { + color.New(color.FgCyan, color.Bold).SprintFunc() + } + return fmt.Sprint + }() + colorYellowBold = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgYellow, color.Bold).SprintfFunc() + } + return fmt.Sprintf + }() + colorBgYellow = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.BgYellow).SprintfFunc() + } + return fmt.Sprintf + }() + colorBlack = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgBlack).SprintfFunc() + } + return fmt.Sprintf + }() + colorGreenBold = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgGreen, color.Bold).SprintfFunc() + } + return fmt.Sprintf + }() + colorRedBold = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgRed, color.Bold).SprintfFunc() + } + return fmt.Sprintf + }() ) // Returns minio global information, as a key value map. diff --git a/cmd/logger/utils.go b/cmd/logger/utils.go index 4368e4daf..5bcac3a5b 100644 --- a/cmd/logger/utils.go +++ b/cmd/logger/utils.go @@ -18,17 +18,45 @@ package logger import ( "fmt" + "os" "regexp" "github.com/fatih/color" + isatty "github.com/mattn/go-isatty" ) // Global colors. var ( - colorBold = color.New(color.Bold).SprintFunc() - colorFgRed = color.New(color.FgRed).SprintfFunc() - colorBgRed = color.New(color.BgRed).SprintfFunc() - colorFgWhite = color.New(color.FgWhite).SprintfFunc() + // Check if we stderr, stdout are dumb terminals, we do not apply + // ansi coloring on dumb terminals. + isTerminal = func() bool { + return isatty.IsTerminal(os.Stdout.Fd()) && isatty.IsTerminal(os.Stderr.Fd()) + } + + colorBold = func() func(a ...interface{}) string { + if isTerminal() { + return color.New(color.Bold).SprintFunc() + } + return fmt.Sprint + }() + colorFgRed = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgRed).SprintfFunc() + } + return fmt.Sprintf + }() + colorBgRed = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.BgRed).SprintfFunc() + } + return fmt.Sprintf + }() + colorFgWhite = func() func(format string, a ...interface{}) string { + if isTerminal() { + return color.New(color.FgWhite).SprintfFunc() + } + return fmt.Sprintf + }() ) var ansiRE = regexp.MustCompile("(\x1b[^m]*m)") @@ -40,15 +68,21 @@ func ansiEscape(format string, args ...interface{}) { } func ansiMoveRight(n int) { - ansiEscape("[%dC", n) + if isTerminal() { + ansiEscape("[%dC", n) + } } func ansiSaveAttributes() { - ansiEscape("7") + if isTerminal() { + ansiEscape("7") + } } func ansiRestoreAttributes() { - ansiEscape("8") + if isTerminal() { + ansiEscape("8") + } } func uniqueEntries(paths []string) []string { diff --git a/cmd/update-main.go b/cmd/update-main.go index 6b0d804ae..dae3b87c7 100644 --- a/cmd/update-main.go +++ b/cmd/update-main.go @@ -30,7 +30,6 @@ import ( "strings" "time" - "github.com/fatih/color" "github.com/inconshreveable/go-update" "github.com/minio/cli" "github.com/minio/minio/cmd/logger" @@ -458,7 +457,7 @@ func getUpdateInfo(timeout time.Duration, mode string) (updateMsg string, sha256 func doUpdate(sha256Hex string, latestReleaseTime time.Time, ok bool) (updateStatusMsg string, err error) { if !ok { - updateStatusMsg = greenColorSprintf("Minio update to version RELEASE.%s canceled.", + updateStatusMsg = colorGreenBold("Minio update to version RELEASE.%s canceled.", latestReleaseTime.Format(minioReleaseTagTimeLayout)) return updateStatusMsg, nil } @@ -484,21 +483,18 @@ func doUpdate(sha256Hex string, latestReleaseTime time.Time, ok bool) (updateSta return updateStatusMsg, err } - return greenColorSprintf("Minio updated to version RELEASE.%s successfully.", + return colorGreenBold("Minio updated to version RELEASE.%s successfully.", latestReleaseTime.Format(minioReleaseTagTimeLayout)), nil } func shouldUpdate(quiet bool, sha256Hex string, latestReleaseTime time.Time) (ok bool) { ok = true if !quiet { - ok = prompt.Confirm(greenColorSprintf("Update to RELEASE.%s [%s]", latestReleaseTime.Format(minioReleaseTagTimeLayout), "yes")) + ok = prompt.Confirm(colorGreenBold("Update to RELEASE.%s [%s]", latestReleaseTime.Format(minioReleaseTagTimeLayout), "yes")) } return ok } -var greenColorSprintf = color.New(color.FgGreen, color.Bold).SprintfFunc() -var redColorSprintf = color.New(color.FgRed, color.Bold).SprintfFunc() - func mainUpdate(ctx *cli.Context) { if len(ctx.Args()) != 0 { cli.ShowCommandHelpAndExit(ctx, "update", -1) @@ -520,7 +516,7 @@ func mainUpdate(ctx *cli.Context) { // Nothing to update running the latest release. if updateMsg == "" { - logger.Info(greenColorSprintf("You are already running the most recent version of ‘minio’.")) + logger.Info(colorGreenBold("You are already running the most recent version of ‘minio’.")) os.Exit(0) } @@ -531,7 +527,7 @@ func mainUpdate(ctx *cli.Context) { var updateStatusMsg string updateStatusMsg, err = doUpdate(sha256Hex, latestReleaseTime, shouldUpdate(quiet, sha256Hex, latestReleaseTime)) if err != nil { - logger.Info(redColorSprintf("Unable to update ‘minio’.")) + logger.Info(colorRedBold("Unable to update ‘minio’.")) logger.Info(err.Error()) os.Exit(-1) } diff --git a/cmd/update-notifier.go b/cmd/update-notifier.go index f164821c6..65882f7b5 100644 --- a/cmd/update-notifier.go +++ b/cmd/update-notifier.go @@ -25,7 +25,6 @@ import ( "github.com/cheggaaa/pb" humanize "github.com/dustin/go-humanize" - "github.com/fatih/color" ) // prepareUpdateMessage - prepares the update message, only if a @@ -46,10 +45,6 @@ func prepareUpdateMessage(downloadURL string, older time.Duration) string { // colorizeUpdateMessage - inspired from Yeoman project npm package https://github.com/yeoman/update-notifier func colorizeUpdateMessage(updateString string, newerThan string) string { - // Initialize coloring. - cyan := color.New(color.FgCyan, color.Bold).SprintFunc() - yellow := color.New(color.FgYellow, color.Bold).SprintfFunc() - msgLine1Fmt := " You are running an older version of Minio released %s " msgLine2Fmt := " Update: %s " @@ -59,8 +54,8 @@ func colorizeUpdateMessage(updateString string, newerThan string) string { line2Length := len(fmt.Sprintf(msgLine2Fmt, updateString)) // Populate lines with color coding. - line1InColor := fmt.Sprintf(msgLine1Fmt, yellow(newerThan)) - line2InColor := fmt.Sprintf(msgLine2Fmt, cyan(updateString)) + line1InColor := fmt.Sprintf(msgLine1Fmt, colorYellowBold(newerThan)) + line2InColor := fmt.Sprintf(msgLine2Fmt, colorCyanBold(updateString)) // calculate the rectangular box size. maxContentWidth := int(math.Max(float64(line1Length), float64(line2Length))) @@ -94,10 +89,10 @@ func colorizeUpdateMessage(updateString string, newerThan string) string { } lines := []string{ - yellow(topLeftChar + strings.Repeat(horizBarChar, maxContentWidth) + topRightChar), + colorYellowBold(topLeftChar + strings.Repeat(horizBarChar, maxContentWidth) + topRightChar), vertBarChar + line1InColor + strings.Repeat(" ", maxContentWidth-line1Length) + vertBarChar, vertBarChar + line2InColor + strings.Repeat(" ", maxContentWidth-line2Length) + vertBarChar, - yellow(bottomLeftChar + strings.Repeat(horizBarChar, maxContentWidth) + bottomRightChar), + colorYellowBold(bottomLeftChar + strings.Repeat(horizBarChar, maxContentWidth) + bottomRightChar), } return "\n" + strings.Join(lines, "\n") + "\n" }