@@ -2,38 +2,59 @@ package runner
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/ssh/terminal"
|
||||
)
|
||||
|
||||
const (
|
||||
//nocolor = 0
|
||||
red = 31
|
||||
green = 32
|
||||
yellow = 33
|
||||
blue = 36
|
||||
gray = 37
|
||||
red = 31
|
||||
green = 32
|
||||
yellow = 33
|
||||
blue = 34
|
||||
magenta = 35
|
||||
cyan = 36
|
||||
gray = 37
|
||||
)
|
||||
|
||||
// NewJobLogger gets the logger for the Job
|
||||
func NewJobLogger(jobName string, dryrun bool) logrus.FieldLogger {
|
||||
var colors []int
|
||||
var nextColor int
|
||||
|
||||
func init() {
|
||||
nextColor = 0
|
||||
colors = []int{
|
||||
blue, yellow, green, magenta, red, gray, cyan,
|
||||
}
|
||||
}
|
||||
|
||||
// WithJobLogger attaches a new logger to context that is aware of steps
|
||||
func WithJobLogger(ctx context.Context, jobName string) context.Context {
|
||||
formatter := new(stepLogFormatter)
|
||||
formatter.color = colors[nextColor%len(colors)]
|
||||
nextColor = nextColor + 1
|
||||
|
||||
logger := logrus.New()
|
||||
logger.SetFormatter(new(jobLogFormatter))
|
||||
logger.SetFormatter(formatter)
|
||||
logger.SetOutput(os.Stdout)
|
||||
logger.SetLevel(logrus.GetLevel())
|
||||
rtn := logger.WithFields(logrus.Fields{"job_name": jobName, "dryrun": dryrun})
|
||||
return rtn
|
||||
rtn := logger.WithFields(logrus.Fields{"job": jobName, "dryrun": common.Dryrun(ctx)})
|
||||
|
||||
return common.WithLogger(ctx, rtn)
|
||||
}
|
||||
|
||||
type jobLogFormatter struct {
|
||||
type stepLogFormatter struct {
|
||||
color int
|
||||
}
|
||||
|
||||
func (f *jobLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
func (f *stepLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
if f.isColored(entry) {
|
||||
@@ -46,32 +67,20 @@ func (f *jobLogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func (f *jobLogFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry) {
|
||||
var levelColor int
|
||||
switch entry.Level {
|
||||
case logrus.DebugLevel, logrus.TraceLevel:
|
||||
levelColor = gray
|
||||
case logrus.WarnLevel:
|
||||
levelColor = yellow
|
||||
case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
|
||||
levelColor = red
|
||||
default:
|
||||
levelColor = blue
|
||||
}
|
||||
|
||||
func (f *stepLogFormatter) printColored(b *bytes.Buffer, entry *logrus.Entry) {
|
||||
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
||||
jobName := entry.Data["job_name"]
|
||||
jobName := entry.Data["job"]
|
||||
|
||||
if entry.Data["dryrun"] == true {
|
||||
fmt.Fprintf(b, "\x1b[%dm*DRYRUN* \x1b[%dm[%s] \x1b[0m%s", green, levelColor, jobName, entry.Message)
|
||||
fmt.Fprintf(b, "\x1b[1m\x1b[%dm\x1b[7m*DRYRUN*\x1b[0m \x1b[%dm[%s] \x1b[0m%s", gray, f.color, jobName, entry.Message)
|
||||
} else {
|
||||
fmt.Fprintf(b, "\x1b[%dm[%s] \x1b[0m%s", levelColor, jobName, entry.Message)
|
||||
fmt.Fprintf(b, "\x1b[%dm[%s] \x1b[0m%s", f.color, jobName, entry.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *jobLogFormatter) print(b *bytes.Buffer, entry *logrus.Entry) {
|
||||
func (f *stepLogFormatter) print(b *bytes.Buffer, entry *logrus.Entry) {
|
||||
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
||||
jobName := entry.Data["job_name"]
|
||||
jobName := entry.Data["job"]
|
||||
|
||||
if entry.Data["dryrun"] == true {
|
||||
fmt.Fprintf(b, "*DRYRUN* [%s] %s", jobName, entry.Message)
|
||||
@@ -80,7 +89,7 @@ func (f *jobLogFormatter) print(b *bytes.Buffer, entry *logrus.Entry) {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *jobLogFormatter) isColored(entry *logrus.Entry) bool {
|
||||
func (f *stepLogFormatter) isColored(entry *logrus.Entry) bool {
|
||||
|
||||
isColored := checkIfTerminal(entry.Logger.Out)
|
||||
|
||||
|
@@ -55,7 +55,19 @@ func (rc *RunContext) Executor() common.Executor {
|
||||
if step.ID == "" {
|
||||
step.ID = fmt.Sprintf("%d", i)
|
||||
}
|
||||
steps = append(steps, rc.newStepExecutor(step))
|
||||
s := step
|
||||
steps = append(steps, func(ctx context.Context) error {
|
||||
//common.Logger(ctx).Infof("\U0001F680 Begin %s", step)
|
||||
//common.Logger(ctx).Infof("\u2728 Begin - %s", step)
|
||||
common.Logger(ctx).Infof("\u2B50 Begin - %s", s)
|
||||
err := rc.newStepExecutor(s)(ctx)
|
||||
if err == nil {
|
||||
common.Logger(ctx).Infof(" \u2705 Success - %s", s)
|
||||
} else {
|
||||
common.Logger(ctx).Errorf(" \u274C Failure - %s", s)
|
||||
}
|
||||
return err
|
||||
})
|
||||
}
|
||||
return common.NewPipelineExecutor(steps...).Finally(rc.Close)
|
||||
}
|
||||
@@ -116,6 +128,17 @@ func (rc *RunContext) runContainer(containerSpec *model.ContainerSpec) common.Ex
|
||||
entrypoint = strings.Fields(containerSpec.Entrypoint)
|
||||
}
|
||||
|
||||
var logWriter io.Writer
|
||||
logger := common.Logger(ctx)
|
||||
if entry, ok := logger.(*log.Entry); ok {
|
||||
logWriter = entry.Writer()
|
||||
} else if lgr, ok := logger.(*log.Logger); ok {
|
||||
logWriter = lgr.Writer()
|
||||
} else {
|
||||
logger.Errorf("Unable to get writer from logger (type=%T)", logger)
|
||||
}
|
||||
logWriter = os.Stdout
|
||||
|
||||
return container.NewDockerRunExecutor(container.NewDockerRunExecutorInput{
|
||||
Cmd: cmd,
|
||||
Entrypoint: entrypoint,
|
||||
@@ -130,6 +153,8 @@ func (rc *RunContext) runContainer(containerSpec *model.ContainerSpec) common.Ex
|
||||
},
|
||||
Content: map[string]io.Reader{"/github": ghReader},
|
||||
ReuseContainers: rc.Config.ReuseContainers,
|
||||
Stdout: logWriter,
|
||||
Stderr: logWriter,
|
||||
})(ctx)
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
@@ -64,5 +65,8 @@ func (runner *runnerImpl) NewRunExecutor(run *model.Run) common.Executor {
|
||||
rc.Config = runner.config
|
||||
rc.Run = run
|
||||
rc.EventJSON = runner.eventJSON
|
||||
return rc.Executor()
|
||||
return func(ctx context.Context) error {
|
||||
ctx = WithJobLogger(ctx, rc.Run.String())
|
||||
return rc.Executor()(ctx)
|
||||
}
|
||||
}
|
||||
|
@@ -50,6 +50,7 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
|
||||
containerSpec.Image = fmt.Sprintf("%s:%s", containerSpec.Name, "latest")
|
||||
return common.NewPipelineExecutor(
|
||||
rc.setupAction(containerSpec, filepath.Join(rc.Config.Workdir, step.Uses)),
|
||||
applyWith(containerSpec, step),
|
||||
rc.pullImage(containerSpec),
|
||||
rc.runContainer(containerSpec),
|
||||
)
|
||||
@@ -73,6 +74,7 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
|
||||
Dir: cloneDir,
|
||||
}),
|
||||
rc.setupAction(containerSpec, filepath.Join(cloneDir, remoteAction.Path)),
|
||||
applyWith(containerSpec, step),
|
||||
rc.pullImage(containerSpec),
|
||||
rc.runContainer(containerSpec),
|
||||
)
|
||||
@@ -81,6 +83,18 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
|
||||
return common.NewErrorExecutor(fmt.Errorf("Unable to determine how to run job:%s step:%+v", rc.Run, step))
|
||||
}
|
||||
|
||||
func applyWith(containerSpec *model.ContainerSpec, step *model.Step) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
if entrypoint, ok := step.With["entrypoint"]; ok {
|
||||
containerSpec.Entrypoint = entrypoint
|
||||
}
|
||||
if args, ok := step.With["args"]; ok {
|
||||
containerSpec.Args = args
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// StepEnv returns the env for a step
|
||||
func (rc *RunContext) StepEnv(step *model.Step) map[string]string {
|
||||
env := make(map[string]string)
|
||||
@@ -113,7 +127,7 @@ func (rc *RunContext) StepEnv(step *model.Step) map[string]string {
|
||||
if err != nil {
|
||||
log.Warningf("unable to get git ref: %v", err)
|
||||
} else {
|
||||
log.Infof("using github ref: %s", ref)
|
||||
log.Debugf("using github ref: %s", ref)
|
||||
env["GITHUB_REF"] = ref
|
||||
}
|
||||
job := rc.Run.Job()
|
||||
|
29
pkg/runner/testdata/parallel/push.yml
vendored
Normal file
29
pkg/runner/testdata/parallel/push.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: basic
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: ./actions/action1
|
||||
with:
|
||||
args: echo 'build'
|
||||
- uses: actions/hello-world-javascript-action@master
|
||||
with:
|
||||
who-to-greet: 'Mona the Octocat'
|
||||
test1:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: docker://ubuntu:18.04
|
||||
with:
|
||||
args: echo ${GITHUB_REF} | grep nektos/act
|
||||
- uses: ./actions/docker-url
|
||||
with:
|
||||
args: npm install angular-cli
|
||||
test2:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- run: echo hello
|
||||
- run: echo world
|
Reference in New Issue
Block a user