Skip to content
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
3 changes: 2 additions & 1 deletion pkg/cmd/pipelinerun/log_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func (lw *LogWriter) Write(s *cli.Stream, logC <-chan Log, errC <-chan error) {
fmt.Fprintf(s.Out, "\n")
continue
}
lw.fmt.Header(s.Out, "[%s : %s] ", l.Task, l.Step)

lw.fmt.Rainbow.Fprintf(l.Step, s.Out, "[%s : %s] ", l.Task, l.Step)
fmt.Fprintf(s.Out, "%s\n", l.Log)
case e, ok := <-errC:
if !ok {
Expand Down
2 changes: 1 addition & 1 deletion pkg/cmd/taskrun/log_writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (lw *LogWriter) Write(s *cli.Stream, logC <-chan Log, errC <-chan error) {
continue
}

lw.fmt.Header(s.Out, "[%s] ", l.Step)
lw.fmt.Rainbow.Fprintf(l.Step, s.Out, "[%s] ", l.Step)
fmt.Fprintf(s.Out, "%s\n", l.Log)
case e, ok := <-errC:
if !ok {
Expand Down
64 changes: 59 additions & 5 deletions pkg/formatted/color.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,78 @@ package formatted

import (
"io"
"sync"
"sync/atomic"

"github.com/fatih/color"
)

var (
// Red is avoided as keeping it for errors
palette = []color.Attribute{
color.FgHiGreen,
color.FgHiYellow,
color.FgHiBlue,
color.FgHiMagenta,
color.FgHiCyan,
}
)

type atomicCounter struct {
Comment thread
sthaha marked this conversation as resolved.
value uint32
threshold int
}

func (c *atomicCounter) next() int {
v := atomic.AddUint32(&c.value, 1)
next := int(v-1) % c.threshold
atomic.CompareAndSwapUint32(&c.value, uint32(c.threshold), 0)
return next

}

type rainbow struct {
cache sync.Map
counter atomicCounter
}

func newRainbow() *rainbow {
return &rainbow{
counter: atomicCounter{threshold: len(palette)},
}
}

func (r *rainbow) get(x string) color.Attribute {
if value, ok := r.cache.Load(x); ok {
return value.(color.Attribute)
}

clr := palette[r.counter.next()]
r.cache.Store(x, clr)
return clr
}

// Fprintf formats according to a format specifier and writes to w.
// the first argument is a label to keep the same colour on.
func (r *rainbow) Fprintf(label string, w io.Writer, format string, args ...interface{}) {
Comment thread
sthaha marked this conversation as resolved.
attribute := r.get(label)
crainbow := color.Set(attribute).Add(color.Bold)
crainbow.Fprintf(w, format, args...)
}

//Color formatter to print the colored output on streams
type Color struct {
Rainbow *rainbow

red *color.Color
blue *color.Color
}

//NewColor returns a new instance color formatter
func NewColor() *Color {
return &Color{
Rainbow: newRainbow(),

red: color.New(color.FgRed),
blue: color.New(color.FgBlue),
}
Expand All @@ -39,11 +98,6 @@ func (c *Color) PrintBlue(w io.Writer, format string, args ...interface{}) {
c.blue.Fprintf(w, format, args...)
}

//Header prints the formatted content to given destination in blue color
func (c *Color) Header(w io.Writer, format string, args ...interface{}) {
c.PrintBlue(w, format, args...)
}

//PrintBlue prints the formatted content to given destination in red color
func (c *Color) PrintRed(w io.Writer, format string, args ...interface{}) {
c.red.Fprintf(w, format, args...)
Expand Down
41 changes: 41 additions & 0 deletions pkg/formatted/color_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright © 2019 The Tekton 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 formatted

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestRainbowsColours(t *testing.T) {
rb := newRainbow()
assert.Equal(t, rb.counter.value, uint32(0)) // nothing

c := rb.get("a") // get a label
assert.NotNil(t, c)
assert.Equal(t, rb.counter.value, uint32(1))

_ = rb.get("b") // incremented
assert.Equal(t, rb.counter.value, uint32(2))

_ = rb.get("a") // no increment (cached)
assert.Equal(t, rb.counter.value, uint32(2))

rb = newRainbow()
for c := range palette {
rb.get(string(c))
}
assert.Equal(t, rb.counter.value, uint32(0)) // Looped back to 0
}