2020-02-07 00:17:58 -06:00
|
|
|
package runner
|
2019-01-12 22:45:25 -06:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2020-02-11 11:10:35 -06:00
|
|
|
"context"
|
2019-01-12 22:45:25 -06:00
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"strings"
|
2020-02-12 01:38:30 -06:00
|
|
|
"sync"
|
2019-01-12 22:45:25 -06:00
|
|
|
|
2020-02-11 11:10:35 -06:00
|
|
|
"github.com/nektos/act/pkg/common"
|
|
|
|
|
2019-01-12 22:45:25 -06:00
|
|
|
"github.com/sirupsen/logrus"
|
2021-01-12 00:39:43 -06:00
|
|
|
"golang.org/x/term"
|
2019-01-12 22:45:25 -06:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2021-01-12 00:39:43 -06:00
|
|
|
// nocolor = 0
|
2020-02-11 11:10:35 -06:00
|
|
|
red = 31
|
|
|
|
green = 32
|
|
|
|
yellow = 33
|
|
|
|
blue = 34
|
|
|
|
magenta = 35
|
|
|
|
cyan = 36
|
|
|
|
gray = 37
|
2019-01-12 22:45:25 -06:00
|
|
|
)
|
|
|
|
|
2020-02-11 11:10:35 -06:00
|
|
|
var colors []int
|
|
|
|
var nextColor int
|
2020-02-12 01:38:30 -06:00
|
|
|
var mux sync.Mutex
|
2020-02-11 11:10:35 -06:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
nextColor = 0
|
|
|
|
colors = []int{
|
|
|
|
blue, yellow, green, magenta, red, gray, cyan,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-11 14:06:05 -05:00
|
|
|
type masksContextKey string
|
|
|
|
|
|
|
|
const masksContextKeyVal = masksContextKey("logrus.FieldLogger")
|
|
|
|
|
|
|
|
// Logger returns the appropriate logger for current context
|
|
|
|
func Masks(ctx context.Context) *[]string {
|
|
|
|
val := ctx.Value(masksContextKeyVal)
|
|
|
|
if val != nil {
|
|
|
|
if masks, ok := val.(*[]string); ok {
|
|
|
|
return masks
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &[]string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithLogger adds a value to the context for the logger
|
|
|
|
func WithMasks(ctx context.Context, masks *[]string) context.Context {
|
|
|
|
return context.WithValue(ctx, masksContextKeyVal, masks)
|
|
|
|
}
|
|
|
|
|
2020-02-11 11:10:35 -06:00
|
|
|
// WithJobLogger attaches a new logger to context that is aware of steps
|
2022-09-27 10:49:22 -05:00
|
|
|
func WithJobLogger(ctx context.Context, jobID string, jobName string, config *Config, masks *[]string, matrix map[string]interface{}) context.Context {
|
2020-02-12 01:38:30 -06:00
|
|
|
mux.Lock()
|
|
|
|
defer mux.Unlock()
|
2022-03-14 10:33:11 -05:00
|
|
|
|
|
|
|
var formatter logrus.Formatter
|
|
|
|
if config.JSONLogger {
|
|
|
|
formatter = &jobLogJSONFormatter{
|
|
|
|
formatter: &logrus.JSONFormatter{},
|
2022-05-11 14:06:05 -05:00
|
|
|
masker: valueMasker(config.InsecureSecrets, config.Secrets),
|
2022-03-14 10:33:11 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
formatter = &jobLogFormatter{
|
|
|
|
color: colors[nextColor%len(colors)],
|
2022-05-11 14:06:05 -05:00
|
|
|
masker: valueMasker(config.InsecureSecrets, config.Secrets),
|
2022-03-14 10:33:11 -05:00
|
|
|
}
|
|
|
|
}
|
2022-03-02 02:29:34 -06:00
|
|
|
|
2020-02-12 01:38:30 -06:00
|
|
|
nextColor++
|
2022-05-11 14:06:05 -05:00
|
|
|
ctx = WithMasks(ctx, masks)
|
2020-02-11 11:10:35 -06:00
|
|
|
|
2021-11-19 08:42:39 -06:00
|
|
|
logger := logrus.New()
|
2022-07-29 02:43:42 -05:00
|
|
|
if hook := common.LoggerHook(ctx); hook != nil {
|
|
|
|
logger.AddHook(hook)
|
|
|
|
}
|
2020-02-11 11:10:35 -06:00
|
|
|
logger.SetFormatter(formatter)
|
2019-02-18 18:30:34 -06:00
|
|
|
logger.SetOutput(os.Stdout)
|
2023-03-08 00:46:39 -06:00
|
|
|
if config.JobLoggerLevel != nil {
|
|
|
|
logger.SetLevel(*config.JobLoggerLevel)
|
|
|
|
} else {
|
|
|
|
logger.SetLevel(logrus.TraceLevel)
|
|
|
|
}
|
2022-06-17 10:55:21 -05:00
|
|
|
rtn := logger.WithFields(logrus.Fields{
|
|
|
|
"job": jobName,
|
|
|
|
"jobID": jobID,
|
|
|
|
"dryrun": common.Dryrun(ctx),
|
2022-09-27 10:49:22 -05:00
|
|
|
"matrix": matrix,
|
2022-06-17 10:55:21 -05:00
|
|
|
}).WithContext(ctx)
|
2020-02-11 11:10:35 -06:00
|
|
|
|
|
|
|
return common.WithLogger(ctx, rtn)
|
2019-01-12 22:45:25 -06:00
|
|
|
}
|
|
|
|
|
2022-05-11 14:06:05 -05:00
|
|
|
func WithCompositeLogger(ctx context.Context, masks *[]string) context.Context {
|
|
|
|
ctx = WithMasks(ctx, masks)
|
|
|
|
return common.WithLogger(ctx, common.Logger(ctx).WithFields(logrus.Fields{}).WithContext(ctx))
|
|
|
|
}
|
|
|
|
|
2022-07-27 14:56:41 -05:00
|
|
|
func WithCompositeStepLogger(ctx context.Context, stepID string) context.Context {
|
|
|
|
val := common.Logger(ctx)
|
|
|
|
stepIDs := make([]string, 0)
|
|
|
|
|
|
|
|
if logger, ok := val.(*logrus.Entry); ok {
|
|
|
|
if oldStepIDs, ok := logger.Data["stepID"].([]string); ok {
|
|
|
|
stepIDs = append(stepIDs, oldStepIDs...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
stepIDs = append(stepIDs, stepID)
|
|
|
|
|
|
|
|
return common.WithLogger(ctx, common.Logger(ctx).WithFields(logrus.Fields{
|
|
|
|
"stepID": stepIDs,
|
|
|
|
}).WithContext(ctx))
|
|
|
|
}
|
|
|
|
|
2022-09-22 08:56:43 -05:00
|
|
|
func withStepLogger(ctx context.Context, stepNumber int, stepID, stepName, stageName string) context.Context {
|
2022-06-17 10:55:21 -05:00
|
|
|
rtn := common.Logger(ctx).WithFields(logrus.Fields{
|
2022-09-22 08:56:43 -05:00
|
|
|
"stepNumber": stepNumber,
|
|
|
|
"step": stepName,
|
|
|
|
"stepID": []string{stepID},
|
|
|
|
"stage": stageName,
|
2022-06-17 10:55:21 -05:00
|
|
|
})
|
2022-03-14 12:38:30 -05:00
|
|
|
return common.WithLogger(ctx, rtn)
|
|
|
|
}
|
|
|
|
|
2022-03-14 10:33:11 -05:00
|
|
|
type entryProcessor func(entry *logrus.Entry) *logrus.Entry
|
2020-02-07 00:17:58 -06:00
|
|
|
|
2022-05-11 14:06:05 -05:00
|
|
|
func valueMasker(insecureSecrets bool, secrets map[string]string) entryProcessor {
|
2022-03-14 10:33:11 -05:00
|
|
|
return func(entry *logrus.Entry) *logrus.Entry {
|
|
|
|
if insecureSecrets {
|
|
|
|
return entry
|
|
|
|
}
|
2019-01-12 22:45:25 -06:00
|
|
|
|
2022-05-11 14:06:05 -05:00
|
|
|
masks := Masks(entry.Context)
|
|
|
|
|
2022-03-14 10:33:11 -05:00
|
|
|
for _, v := range secrets {
|
2021-06-07 08:54:12 -05:00
|
|
|
if v != "" {
|
|
|
|
entry.Message = strings.ReplaceAll(entry.Message, v, "***")
|
|
|
|
}
|
2021-01-12 00:28:45 -06:00
|
|
|
}
|
2022-03-02 02:29:34 -06:00
|
|
|
|
2022-03-14 10:33:11 -05:00
|
|
|
for _, v := range *masks {
|
2022-03-02 02:29:34 -06:00
|
|
|
if v != "" {
|
|
|
|
entry.Message = strings.ReplaceAll(entry.Message, v, "***")
|
|
|
|
}
|
|
|
|
}
|
2022-03-14 10:33:11 -05:00
|
|
|
|
|
|
|
return entry
|
2020-05-13 17:22:31 -05:00
|
|
|
}
|
2022-03-14 10:33:11 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type jobLogFormatter struct {
|
|
|
|
color int
|
|
|
|
masker entryProcessor
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *jobLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|
|
|
b := &bytes.Buffer{}
|
|
|
|
|
|
|
|
entry = f.masker(entry)
|
2020-05-13 17:22:31 -05:00
|
|
|
|
2019-01-12 22:45:25 -06:00
|
|
|
if f.isColored(entry) {
|
|
|
|
f.printColored(b, entry)
|
|
|
|
} else {
|
|
|
|
f.print(b, entry)
|
|
|
|
}
|
|
|
|
|
|
|
|
b.WriteByte('\n')
|
|
|
|
return b.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
2022-03-14 10:33:11 -05:00
|
|
|
func (f *jobLogFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry) {
|
2019-01-12 22:45:25 -06:00
|
|
|
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
2020-02-11 11:10:35 -06:00
|
|
|
jobName := entry.Data["job"]
|
2022-06-17 10:55:21 -05:00
|
|
|
debugFlag := ""
|
|
|
|
if entry.Level == logrus.DebugLevel {
|
|
|
|
debugFlag = "[DEBUG] "
|
|
|
|
}
|
2019-01-12 22:45:25 -06:00
|
|
|
|
2020-02-12 01:38:30 -06:00
|
|
|
if entry.Data["raw_output"] == true {
|
|
|
|
fmt.Fprintf(b, "\x1b[%dm|\x1b[0m %s", f.color, entry.Message)
|
|
|
|
} else if entry.Data["dryrun"] == true {
|
2022-06-17 10:55:21 -05:00
|
|
|
fmt.Fprintf(b, "\x1b[1m\x1b[%dm\x1b[7m*DRYRUN*\x1b[0m \x1b[%dm[%s] \x1b[0m%s%s", gray, f.color, jobName, debugFlag, entry.Message)
|
2019-01-12 22:45:25 -06:00
|
|
|
} else {
|
2022-06-17 10:55:21 -05:00
|
|
|
fmt.Fprintf(b, "\x1b[%dm[%s] \x1b[0m%s%s", f.color, jobName, debugFlag, entry.Message)
|
2019-01-12 22:45:25 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 10:33:11 -05:00
|
|
|
func (f *jobLogFormatter) print(b *bytes.Buffer, entry *logrus.Entry) {
|
2019-01-12 22:45:25 -06:00
|
|
|
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
2020-02-11 11:10:35 -06:00
|
|
|
jobName := entry.Data["job"]
|
2022-06-17 10:55:21 -05:00
|
|
|
debugFlag := ""
|
|
|
|
if entry.Level == logrus.DebugLevel {
|
|
|
|
debugFlag = "[DEBUG] "
|
|
|
|
}
|
2019-01-12 22:45:25 -06:00
|
|
|
|
2020-02-12 01:38:30 -06:00
|
|
|
if entry.Data["raw_output"] == true {
|
|
|
|
fmt.Fprintf(b, "[%s] | %s", jobName, entry.Message)
|
|
|
|
} else if entry.Data["dryrun"] == true {
|
2022-06-17 10:55:21 -05:00
|
|
|
fmt.Fprintf(b, "*DRYRUN* [%s] %s%s", jobName, debugFlag, entry.Message)
|
2019-01-12 22:45:25 -06:00
|
|
|
} else {
|
2022-06-17 10:55:21 -05:00
|
|
|
fmt.Fprintf(b, "[%s] %s%s", jobName, debugFlag, entry.Message)
|
2019-01-12 22:45:25 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-14 10:33:11 -05:00
|
|
|
func (f *jobLogFormatter) isColored(entry *logrus.Entry) bool {
|
2019-01-12 22:45:25 -06:00
|
|
|
isColored := checkIfTerminal(entry.Logger.Out)
|
|
|
|
|
|
|
|
if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok && force != "0" {
|
|
|
|
isColored = true
|
|
|
|
} else if ok && force == "0" {
|
|
|
|
isColored = false
|
|
|
|
} else if os.Getenv("CLICOLOR") == "0" {
|
|
|
|
isColored = false
|
|
|
|
}
|
|
|
|
|
|
|
|
return isColored
|
|
|
|
}
|
|
|
|
|
|
|
|
func checkIfTerminal(w io.Writer) bool {
|
|
|
|
switch v := w.(type) {
|
|
|
|
case *os.File:
|
2021-01-12 00:39:43 -06:00
|
|
|
return term.IsTerminal(int(v.Fd()))
|
2019-01-12 22:45:25 -06:00
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
2022-03-14 10:33:11 -05:00
|
|
|
|
|
|
|
type jobLogJSONFormatter struct {
|
|
|
|
masker entryProcessor
|
|
|
|
formatter *logrus.JSONFormatter
|
|
|
|
}
|
|
|
|
|
|
|
|
func (f *jobLogJSONFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
|
|
|
return f.formatter.Format(f.masker(entry))
|
|
|
|
}
|