github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/output/color.go (about)

     1  /*
     2  Copyright 2020 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package output
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"strings"
    24  
    25  	colors "github.com/heroku/color"
    26  	"github.com/mattn/go-colorable"
    27  
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/term"
    30  )
    31  
    32  // Maintain compatibility with the old color coding.
    33  // 34 is the code for blue.
    34  const DefaultColorCode = 34
    35  
    36  func init() {
    37  	colors.Disable(true)
    38  }
    39  
    40  var DefaultColorCodes = []Color{
    41  	LightRed,
    42  	LightGreen,
    43  	LightYellow,
    44  	LightBlue,
    45  	LightPurple,
    46  	Red,
    47  	Green,
    48  	Yellow,
    49  	Blue,
    50  	Purple,
    51  	Cyan,
    52  }
    53  
    54  // SetupColors conditionally wraps the input `Writer` with a color enabled `Writer`.
    55  func SetupColors(ctx context.Context, out io.Writer, defaultColor int, forceColors bool) io.Writer {
    56  	_, isTerm := term.IsTerminal(out)
    57  	supportsColor, err := term.SupportsColor(ctx)
    58  	if err != nil {
    59  		log.Entry(context.TODO()).Debugf("error checking for color support: %v", err)
    60  	}
    61  
    62  	useColors := (isTerm && supportsColor) || forceColors
    63  	if useColors {
    64  		// Use EnableColorsStdout to enable use of color on Windows
    65  		useColors = false // value is updated if color-enablement is successful
    66  		colorable.EnableColorsStdout(&useColors)
    67  	}
    68  	colors.Disable(!useColors)
    69  
    70  	// Maintain compatibility with the old color coding.
    71  	Default = map[int]Color{
    72  		91: LightRed,
    73  		92: LightGreen,
    74  		93: LightYellow,
    75  		94: LightBlue,
    76  		95: LightPurple,
    77  		31: Red,
    78  		32: Green,
    79  		33: Yellow,
    80  		34: Blue,
    81  		35: Purple,
    82  		36: Cyan,
    83  		37: White,
    84  		0:  None,
    85  	}[defaultColor]
    86  
    87  	if useColors {
    88  		return NewColorWriter(out)
    89  	}
    90  	return out
    91  }
    92  
    93  // Color can be used to format text so it can be printed to the terminal in color.
    94  type Color struct {
    95  	color *colors.Color
    96  }
    97  
    98  type colorableWriter struct {
    99  	io.Writer
   100  }
   101  
   102  var (
   103  	// LightRed can format text to be displayed to the terminal in light red.
   104  	LightRed = Color{color: colors.New(colors.FgHiRed)}
   105  	// LightGreen can format text to be displayed to the terminal in light green.
   106  	LightGreen = Color{color: colors.New(colors.FgHiGreen)}
   107  	// LightYellow can format text to be displayed to the terminal in light yellow.
   108  	LightYellow = Color{color: colors.New(colors.FgHiYellow)}
   109  	// LightBlue can format text to be displayed to the terminal in light blue.
   110  	LightBlue = Color{color: colors.New(colors.FgHiBlue)}
   111  	// LightPurple can format text to be displayed to the terminal in light purple.
   112  	LightPurple = Color{color: colors.New(colors.FgHiMagenta)}
   113  	// Red can format text to be displayed to the terminal in red.
   114  	Red = Color{color: colors.New(colors.FgRed)}
   115  	// Green can format text to be displayed to the terminal in green.
   116  	Green = Color{color: colors.New(colors.FgGreen)}
   117  	// Yellow can format text to be displayed to the terminal in yellow.
   118  	Yellow = Color{color: colors.New(colors.FgYellow)}
   119  	// Blue can format text to be displayed to the terminal in blue.
   120  	Blue = Color{color: colors.New(colors.FgBlue)}
   121  	// Purple can format text to be displayed to the terminal in purple.
   122  	Purple = Color{color: colors.New(colors.FgHiMagenta)}
   123  	// Cyan can format text to be displayed to the terminal in cyan.
   124  	Cyan = Color{color: colors.New(colors.FgHiCyan)}
   125  	// White can format text to be displayed to the terminal in white.
   126  	White = Color{color: colors.New(colors.FgWhite)}
   127  	// None uses ANSI escape codes to reset all formatting.
   128  	None = Color{}
   129  
   130  	// Default default output color for output from Skaffold to the user
   131  	Default = Blue
   132  )
   133  
   134  // Fprintln outputs the result to out, followed by a newline.
   135  func (c Color) Fprintln(out io.Writer, a ...interface{}) {
   136  	if c.color == nil || !IsColorable(out) {
   137  		fmt.Fprintln(out, a...)
   138  		return
   139  	}
   140  
   141  	fmt.Fprintln(out, c.color.Sprint(strings.TrimSuffix(fmt.Sprintln(a...), "\n")))
   142  }
   143  
   144  // Fprintf outputs the result to out.
   145  func (c Color) Fprintf(out io.Writer, format string, a ...interface{}) {
   146  	if c.color == nil || !IsColorable(out) {
   147  		fmt.Fprintf(out, format, a...)
   148  		return
   149  	}
   150  
   151  	fmt.Fprint(out, c.color.Sprintf(format, a...))
   152  }
   153  
   154  func (c Color) Sprintf(format string, a ...interface{}) string {
   155  	if c.color == nil {
   156  		return fmt.Sprintf(format, a...)
   157  	}
   158  
   159  	return c.color.Sprintf(format, a...)
   160  }
   161  
   162  func NewColorWriter(out io.Writer) io.Writer {
   163  	return colorableWriter{out}
   164  }
   165  
   166  func IsColorable(out io.Writer) bool {
   167  	switch w := out.(type) {
   168  	case colorableWriter:
   169  		return true
   170  	case skaffoldWriter:
   171  		return IsColorable(w.MainWriter)
   172  	default:
   173  		return false
   174  	}
   175  }