Skip to content

Commit

Permalink
Put formatting methods on color object
Browse files Browse the repository at this point in the history
dgageot@ suggested that instead of passing the color into the formatting
methods, we could make these methods be on the color struct itself,
which makes for a pretty slick interface imo.

One thing that still isn't clear is how to output one line in multiple
colors in a terminal safe way - Sprint/Sprintf would have to bebe aware
of the target output stream to make this happen, or we'd need to
introduce some kind of formatting syntax (e.g. '{blue}blue
text{reset:blue}') but I think that can wait until we really need it,
the workaround in log.go seems find for now.
  • Loading branch information
bobcatfish committed Jul 27, 2018
1 parent b0b9d60 commit b7ee990
Show file tree
Hide file tree
Showing 14 changed files with 94 additions and 138 deletions.
4 changes: 2 additions & 2 deletions cmd/skaffold/app/cmd/fix.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func NewCmdFix(out io.Writer) *cobra.Command {
return
}
if cfg.GetVersion() == config.LatestVersion {
color.Fprintln(out, color.Default, "config is already latest version")
color.Default.Fprintln(out, "config is already latest version")
return
}
if err := runFix(out, cfg); err != nil {
Expand All @@ -74,7 +74,7 @@ func runFix(out io.Writer, cfg schemautil.VersionedConfig) error {
if err := ioutil.WriteFile(filename, newCfg, 0644); err != nil {
return errors.Wrap(err, "writing config file")
}
color.Fprintf(out, color.Default, "New config at version %s generated and written to %s\n", cfg.GetVersion(), filename)
color.Default.Fprintf(out, "New config at version %s generated and written to %s\n", cfg.GetVersion(), filename)
} else {
out.Write(newCfg)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/skaffold/build/gcb/container_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, tagger tag.T
return "", errors.Wrap(err, "checking bucket is in correct project")
}

color.Fprintf(out, color.Default, "Pushing code to gs://%s/%s\n", cbBucket, buildObject)
color.Default.Fprintf(out, "Pushing code to gs://%s/%s\n", cbBucket, buildObject)
if err := docker.UploadContextToGCS(ctx, artifact.Workspace, artifact.DockerArtifact, cbBucket, buildObject); err != nil {
return "", errors.Wrap(err, "uploading source tarball")
}
Expand Down Expand Up @@ -124,7 +124,7 @@ func (b *Builder) buildArtifact(ctx context.Context, out io.Writer, tagger tag.T
return "", errors.Wrapf(err, "getting build ID from op")
}
logsObject := fmt.Sprintf("log-%s.txt", remoteID)
color.Fprintf(out, color.Default, "Logs at available at \nhttps://console.cloud.google.com/m/cloudstorage/b/%s/o/%s\n", cbBucket, logsObject)
color.Default.Fprintf(out, "Logs at available at \nhttps://console.cloud.google.com/m/cloudstorage/b/%s/o/%s\n", cbBucket, logsObject)
var imageID string
offset := int64(0)
watch:
Expand Down
2 changes: 1 addition & 1 deletion pkg/skaffold/build/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
// its checksum. It streams build progress to the writer argument.
func (b *Builder) Build(ctx context.Context, out io.Writer, tagger tag.Tagger, artifacts []*v1alpha2.Artifact) ([]build.Artifact, error) {
if b.localCluster {
if _, err := color.Fprintf(out, color.Default, "Found [%s] context, using local docker daemon.\n", b.kubeContext); err != nil {
if _, err := color.Default.Fprintf(out, "Found [%s] context, using local docker daemon.\n", b.kubeContext); err != nil {
return nil, errors.Wrap(err, "writing status")
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/skaffold/build/parallel.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ func InParallel(ctx context.Context, out io.Writer, tagger tag.Tagger, artifacts

for i, artifact := range artifacts {
for line := range outputs[i] {
color.Fprintln(out, color.Default, line)
color.Default.Fprintln(out, line)
}

if errs[i] != nil {
Expand Down
2 changes: 1 addition & 1 deletion pkg/skaffold/build/sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ func InSequence(ctx context.Context, out io.Writer, tagger tag.Tagger, artifacts
var builds []Artifact

for _, artifact := range artifacts {
color.Fprintf(out, color.Default, "Building [%s]...\n", artifact.ImageName)
color.Default.Fprintf(out, "Building [%s]...\n", artifact.ImageName)

tag, err := buildArtifact(ctx, out, tagger, artifact)
if err != nil {
Expand Down
69 changes: 0 additions & 69 deletions pkg/skaffold/color/colors.go

This file was deleted.

39 changes: 0 additions & 39 deletions pkg/skaffold/color/colors_test.go

This file was deleted.

Binary file modified pkg/skaffold/color/debug.test
Binary file not shown.
60 changes: 54 additions & 6 deletions pkg/skaffold/color/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,82 @@ import (
// for testing to an arbitrary method.
var IsTerminal = isTerminal

// Color can be used to format text using ANSI escape codes so it can be printed to
// the terminal in color.
type Color int

var (
// LightRed can format text to be displayed to the terminal in light red, using ANSI escape codes.
LightRed = Color(91)
// LightGreen can format text to be displayed to the terminal in light green, using ANSI escape codes.
LightGreen = Color(92)
// LightYellow can format text to be displayed to the terminal in light yellow, using ANSI escape codes.
LightYellow = Color(93)
// LightBlue can format text to be displayed to the terminal in light blue, using ANSI escape codes.
LightBlue = Color(94)
// LightPurple can format text to be displayed to the terminal in light purple, using ANSI escape codes.
LightPurple = Color(95)
// Red can format text to be displayed to the terminal in red, using ANSI escape codes.
Red = Color(31)
// Green can format text to be displayed to the terminal in green, using ANSI escape codes.
Green = Color(32)
// Yellow can format text to be displayed to the terminal in yellow, using ANSI escape codes.
Yellow = Color(33)
// Blue can format text to be displayed to the terminal in blue, using ANSI escape codes.
Blue = Color(34)
// Purple can format text to be displayed to the terminal in purple, using ANSI escape codes.
Purple = Color(35)
// Cyan can format text to be displayed to the terminal in cyan, using ANSI escape codes.
Cyan = Color(36)
// None uses ANSI escape codes to reset all formatting.
None = Color(0)

// Default default output color for output from Skaffold to the user
Default = Blue
)

func wrapTextIfTerminal(out io.Writer, c Color, a ...interface{}) string {
if IsTerminal(out) {
return c.Sprint(a...)
}
return fmt.Sprint(a...)
}

// Fprint wraps the operands in the color ANSI escape codes, and outputs the result to
// Sprint will format the operands such that they are surrounded by the ANSI escape sequence
// required to display the text to the terminal in color.
func (c Color) Sprint(a ...interface{}) string {
text := fmt.Sprint(a...)
return fmt.Sprintf("\033[%dm%s\033[0m", c, text)
}

// Sprintf will format the operands according ot the format specifier and wrap the resulting text
// with the ANSI escape sequence required to display the text to the terminal in color.
func (c Color) Sprintf(format string, a ...interface{}) string {
formatSpecifier := c.Sprint(format)
return fmt.Sprintf(formatSpecifier, a...)
}

// Fprint wraps the operands in c's ANSI escape codes, and outputs the result to
// out. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func Fprint(out io.Writer, c Color, a ...interface{}) (n int, err error) {
func (c Color) Fprint(out io.Writer, a ...interface{}) (n int, err error) {
t := wrapTextIfTerminal(out, c, a...)
return fmt.Fprint(out, t)
}

// Fprintln wraps the operands in the color ANSI escape codes, and outputs the result to
// Fprintln wraps the operands in c's ANSI escape codes, and outputs the result to
// out, followed by a newline. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func Fprintln(out io.Writer, c Color, a ...interface{}) (n int, err error) {
func (c Color) Fprintln(out io.Writer, a ...interface{}) (n int, err error) {
t := wrapTextIfTerminal(out, c, a...)
return fmt.Fprintln(out, t)
}

// Fprintf applies formats according to the format specifier (and the optional interfaces provided),
// wraps the result in the color ANSI escape codes, and outputs the result to
// wraps the result in c's ANSI escape codes, and outputs the result to
// out, followed by a newline. If out is not a terminal, the escape codes will not be added.
// It returns the number of bytes written and any errors encountered.
func Fprintf(out io.Writer, c Color, format string, a ...interface{}) (n int, err error) {
func (c Color) Fprintf(out io.Writer, format string, a ...interface{}) (n int, err error) {
var t string
if IsTerminal(out) {
t = c.Sprintf(format, a...)
Expand Down
28 changes: 22 additions & 6 deletions pkg/skaffold/color/formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,29 @@ func compareText(t *testing.T, expected, actual string, expectedN int, actualN i
}
}

func TestColorSprint(t *testing.T) {
c := Red.Sprint("TEXT")
expected := "\033[31mTEXT\033[0m"
if c != expected {
t.Errorf("Expected %s. Got %s", expected, c)
}
}

func TestColorSprintf(t *testing.T) {
c := Green.Sprintf("A GREAT NUMBER IS %d", 5)
expected := "\033[32mA GREAT NUMBER IS 5\033[0m"
if c != expected {
t.Errorf("Expected %s. Got %s", expected, c)
}
}

func TestFprint(t *testing.T) {
orgIsTerminal := IsTerminal
defer func() { IsTerminal = orgIsTerminal }()
IsTerminal = func(_ io.Writer) bool { return true }

var b bytes.Buffer
n, err := Fprint(&b, Green, "It's not easy being")
n, err := Green.Fprint(&b, "It's not easy being")
expected := "\033[32mIt's not easy being\033[0m"
compareText(t, expected, b.String(), 28, n, err)
}
Expand All @@ -52,7 +68,7 @@ func TestFprintln(t *testing.T) {
IsTerminal = func(_ io.Writer) bool { return true }

var b bytes.Buffer
n, err := Fprintln(&b, Green, "2 less chars!")
n, err := Green.Fprintln(&b, "2 less chars!")
expected := "\033[32m2 less chars!\033[0m\n"
compareText(t, expected, b.String(), 23, n, err)
}
Expand All @@ -63,7 +79,7 @@ func TestFprintf(t *testing.T) {
IsTerminal = func(_ io.Writer) bool { return true }

var b bytes.Buffer
n, err := Fprintf(&b, Green, "It's been %d %s", 1, "week")
n, err := Green.Fprintf(&b, "It's been %d %s", 1, "week")
expected := "\033[32mIt's been 1 week\033[0m"
compareText(t, expected, b.String(), 25, n, err)
}
Expand All @@ -75,7 +91,7 @@ func TestFprintNoTTY(t *testing.T) {

var b bytes.Buffer
expected := "It's not easy being"
n, err := Fprint(&b, Green, expected)
n, err := Green.Fprint(&b, expected)
compareText(t, expected, b.String(), 19, n, err)
}

Expand All @@ -85,7 +101,7 @@ func TestFprintlnNoTTY(t *testing.T) {
IsTerminal = func(_ io.Writer) bool { return false }

var b bytes.Buffer
n, err := Fprintln(&b, Green, "2 less chars!")
n, err := Green.Fprintln(&b, "2 less chars!")
expected := "2 less chars!\n"
compareText(t, expected, b.String(), 14, n, err)
}
Expand All @@ -95,7 +111,7 @@ func TestFprintfNoTTY(t *testing.T) {
IsTerminal = func(_ io.Writer) bool { return false }

var b bytes.Buffer
n, err := Fprintf(&b, Green, "It's been %d %s", 1, "week")
n, err := Green.Fprintf(&b, "It's been %d %s", 1, "week")
expected := "It's been 1 week"
compareText(t, expected, b.String(), 16, n, err)
}
4 changes: 2 additions & 2 deletions pkg/skaffold/deploy/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func (h *HelmDeployer) deployRelease(out io.Writer, r v1alpha2.HelmRelease, buil
return nil, errors.Wrap(err, "cannot parse the release name template")
}
if err := h.helm(out, "get", releaseName); err != nil {
color.Fprintf(out, color.Red, "Helm release %s not installed. Installing...\n", releaseName)
color.Red.Fprintf(out, "Helm release %s not installed. Installing...\n", releaseName)
isInstalled = false
}
params, err := joinTagsToBuildResult(builds, r.Values)
Expand Down Expand Up @@ -213,7 +213,7 @@ func (h *HelmDeployer) deployRelease(out io.Writer, r v1alpha2.HelmRelease, buil
for k, v := range m {
envMap[k+suffix] = v
}
color.Fprintf(out, color.Default, "EnvVarMap: %#v\n", envMap)
color.Default.Fprintf(out, "EnvVarMap: %#v\n", envMap)
}
for k, v := range r.SetValueTemplates {
t, err := util.ParseEnvTemplate(v)
Expand Down
2 changes: 1 addition & 1 deletion pkg/skaffold/kubernetes/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func (a *LogAggregator) streamRequest(ctx context.Context, headerColor color.Col
continue
}

if _, err := color.Fprintf(a.output, headerColor, "%s ", header); err != nil {
if _, err := headerColor.Fprintf(a.output, "%s ", header); err != nil {
return errors.Wrap(err, "writing pod prefix header to out")
}
if _, err := fmt.Fprint(a.output, string(line)); err != nil {
Expand Down
4 changes: 2 additions & 2 deletions pkg/skaffold/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*v1
}
logger.Unmute()

color.Fprintln(out, color.Default, "Watching for changes...")
color.Default.Fprintln(out, "Watching for changes...")
return nil
}

Expand All @@ -197,7 +197,7 @@ func (r *SkaffoldRunner) Dev(ctx context.Context, out io.Writer, artifacts []*v1
err := r.buildAndDeploy(ctx, out, changes, imageList)
logger.Unmute()

color.Fprintln(out, color.Default, "Watching for changes...")
color.Default.Fprintln(out, "Watching for changes...")
return err
}

Expand Down
Loading

0 comments on commit b7ee990

Please sign in to comment.