Przeglądaj źródła

better support NO_COLOR by disabling colors, not ANSI TUI (#10434)

Signed-off-by: Nicolas De Loof <[email protected]>
Nicolas De loof 2 lat temu
rodzic
commit
d762f5f473

+ 5 - 3
cmd/compose/compose.go

@@ -305,11 +305,13 @@ func RootCommand(streams command.Cli, backend api.Service) *cobra.Command { //no
 				logrus.SetLevel(logrus.TraceLevel)
 			}
 
-			if noColor, ok := os.LookupEnv("NO_COLOR"); ok && noColor != "" && !cmd.Flags().Changed("ansi") {
-				ansi = "never"
+			formatter.SetANSIMode(streams, ansi)
+
+			if noColor, ok := os.LookupEnv("NO_COLOR"); ok && noColor != "" {
+				progress.NoColor()
+				formatter.SetANSIMode(streams, formatter.Never)
 			}
 
-			formatter.SetANSIMode(streams, ansi)
 			switch ansi {
 			case "never":
 				progress.Mode = progress.ModePlain

+ 47 - 0
pkg/progress/colors.go

@@ -0,0 +1,47 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package progress
+
+import (
+	"github.com/morikuni/aec"
+)
+
+type colorFunc func(string) string
+
+var (
+	nocolor colorFunc = func(s string) string {
+		return s
+	}
+
+	DoneColor    colorFunc = aec.BlueF.Apply
+	TimerColor   colorFunc = aec.BlueF.Apply
+	CountColor   colorFunc = aec.YellowF.Apply
+	WarningColor colorFunc = aec.YellowF.With(aec.Bold).Apply
+	SuccessColor colorFunc = aec.GreenF.Apply
+	ErrorColor   colorFunc = aec.RedF.With(aec.Bold).Apply
+	PrefixColor  colorFunc = aec.CyanF.Apply
+)
+
+func NoColor() {
+	DoneColor = nocolor
+	TimerColor = nocolor
+	CountColor = nocolor
+	WarningColor = nocolor
+	SuccessColor = nocolor
+	ErrorColor = nocolor
+	PrefixColor = nocolor
+}

+ 11 - 13
pkg/progress/event.go

@@ -18,23 +18,21 @@ package progress
 
 import (
 	"time"
-
-	"github.com/morikuni/aec"
 )
 
 // EventStatus indicates the status of an action
 type EventStatus int
 
-func (s EventStatus) color() aec.ANSI {
+func (s EventStatus) colorFn() colorFunc {
 	switch s {
 	case Done:
-		return aec.GreenF
+		return SuccessColor
 	case Warning:
-		return aec.YellowF.With(aec.Bold)
+		return WarningColor
 	case Error:
-		return aec.RedF.With(aec.Bold)
+		return ErrorColor
 	default:
-		return aec.DefaultF
+		return nocolor
 	}
 }
 
@@ -170,19 +168,19 @@ func (e *Event) stop() {
 }
 
 var (
-	spinnerDone    = aec.Apply("✔", aec.GreenF)
-	spinnerWarning = aec.Apply("!", aec.YellowF)
-	spinnerError   = aec.Apply("✘", aec.RedF)
+	spinnerDone    = "✔"
+	spinnerWarning = "!"
+	spinnerError   = "✘"
 )
 
 func (e *Event) Spinner() any {
 	switch e.Status {
 	case Done:
-		return spinnerDone
+		return SuccessColor(spinnerDone)
 	case Warning:
-		return spinnerWarning
+		return WarningColor(spinnerWarning)
 	case Error:
-		return spinnerError
+		return ErrorColor(spinnerError)
 	default:
 		return e.spinner.String()
 	}

+ 6 - 6
pkg/progress/tty.go

@@ -151,7 +151,7 @@ func (w *ttyWriter) print() { //nolint:gocyclo
 
 	firstLine := fmt.Sprintf("[+] Running %d/%d", numDone(w.events), w.numLines)
 	if w.numLines != 0 && numDone(w.events) == w.numLines {
-		firstLine = aec.Apply(firstLine, aec.BlueF)
+		firstLine = DoneColor(firstLine)
 	}
 	fmt.Fprintln(w.out, firstLine)
 
@@ -210,7 +210,7 @@ func (w *ttyWriter) lineText(event Event, pad string, terminalWidth, statusPaddi
 	}
 	prefix := ""
 	if dryRun {
-		prefix = aec.Apply(api.DRYRUN_PREFIX, aec.CyanF)
+		prefix = PrefixColor(api.DRYRUN_PREFIX)
 	}
 
 	elapsed := endTime.Sub(event.startTime).Seconds()
@@ -234,8 +234,8 @@ func (w *ttyWriter) lineText(event Event, pad string, terminalWidth, statusPaddi
 	if len(completion) > 0 {
 		txt = fmt.Sprintf("%s %s [%s] %7s/%-7s %s",
 			event.ID,
-			aec.Apply(fmt.Sprintf("%d layers", len(completion)), aec.YellowF),
-			aec.Apply(strings.Join(completion, ""), aec.GreenF, aec.Bold),
+			CountColor(fmt.Sprintf("%d layers", len(completion))),
+			SuccessColor(strings.Join(completion, "")),
 			units.HumanSize(float64(current)), units.HumanSize(float64(total)),
 			event.Text)
 	} else {
@@ -260,10 +260,10 @@ func (w *ttyWriter) lineText(event Event, pad string, terminalWidth, statusPaddi
 		prefix,
 		txt,
 		strings.Repeat(" ", padding),
-		aec.Apply(status, event.Status.color()),
+		event.Status.colorFn()(status),
 	)
 	timer := fmt.Sprintf("%.1fs ", elapsed)
-	o := align(text, aec.Apply(timer, aec.BlueF), terminalWidth)
+	o := align(text, TimerColor(timer), terminalWidth)
 
 	return o
 }

+ 3 - 3
pkg/progress/tty_test.go

@@ -42,7 +42,7 @@ func TestLineText(t *testing.T) {
 	lineWidth := len(fmt.Sprintf("%s %s", ev.ID, ev.Text))
 
 	out := tty().lineText(ev, "", 50, lineWidth, false)
-	assert.Equal(t, out, " . id Text \x1b[39mStatus\x1b[0m                            \x1b[34m0.0s \x1b[0m\n")
+	assert.Equal(t, out, " . id Text Status                            \x1b[34m0.0s \x1b[0m\n")
 
 	ev.Status = Done
 	out = tty().lineText(ev, "", 50, lineWidth, false)
@@ -50,11 +50,11 @@ func TestLineText(t *testing.T) {
 
 	ev.Status = Error
 	out = tty().lineText(ev, "", 50, lineWidth, false)
-	assert.Equal(t, out, " \x1b[31m✘\x1b[0m id Text \x1b[31m\x1b[1mStatus\x1b[0m                            \x1b[34m0.0s \x1b[0m\n")
+	assert.Equal(t, out, " \x1b[31m\x1b[1m✘\x1b[0m id Text \x1b[31m\x1b[1mStatus\x1b[0m                            \x1b[34m0.0s \x1b[0m\n")
 
 	ev.Status = Warning
 	out = tty().lineText(ev, "", 50, lineWidth, false)
-	assert.Equal(t, out, " \x1b[33m!\x1b[0m id Text \x1b[33m\x1b[1mStatus\x1b[0m                            \x1b[34m0.0s \x1b[0m\n")
+	assert.Equal(t, out, " \x1b[33m\x1b[1m!\x1b[0m id Text \x1b[33m\x1b[1mStatus\x1b[0m                            \x1b[34m0.0s \x1b[0m\n")
 }
 
 func TestLineTextSingleEvent(t *testing.T) {