2020-02-07 14:17:58 +08:00
|
|
|
package runner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-02-14 16:41:20 +08:00
|
|
|
"encoding/json"
|
2020-11-18 01:31:05 +08:00
|
|
|
"errors"
|
2020-02-07 14:17:58 +08:00
|
|
|
"fmt"
|
|
|
|
"os"
|
2020-02-24 07:01:25 +08:00
|
|
|
"path/filepath"
|
2020-02-07 14:17:58 +08:00
|
|
|
"regexp"
|
2020-02-25 02:56:49 +08:00
|
|
|
"runtime"
|
2020-02-07 14:17:58 +08:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/nektos/act/pkg/container"
|
|
|
|
|
|
|
|
"github.com/nektos/act/pkg/common"
|
|
|
|
"github.com/nektos/act/pkg/model"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
|
|
|
// RunContext contains info about current job
|
|
|
|
type RunContext struct {
|
2020-02-27 15:29:43 +08:00
|
|
|
Name string
|
2020-02-24 07:01:25 +08:00
|
|
|
Config *Config
|
|
|
|
Matrix map[string]interface{}
|
|
|
|
Run *model.Run
|
|
|
|
EventJSON string
|
|
|
|
Env map[string]string
|
|
|
|
ExtraPath []string
|
|
|
|
CurrentStep string
|
|
|
|
StepResults map[string]*stepResult
|
|
|
|
ExprEval ExpressionEvaluator
|
|
|
|
JobContainer container.Container
|
2020-02-14 16:41:20 +08:00
|
|
|
}
|
|
|
|
|
2020-02-27 15:29:43 +08:00
|
|
|
func (rc *RunContext) String() string {
|
|
|
|
return fmt.Sprintf("%s/%s", rc.Run.Workflow.Name, rc.Name)
|
|
|
|
}
|
|
|
|
|
2020-02-14 16:41:20 +08:00
|
|
|
type stepResult struct {
|
|
|
|
Success bool `json:"success"`
|
|
|
|
Outputs map[string]string `json:"outputs"`
|
2020-02-07 14:17:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetEnv returns the env for the context
|
|
|
|
func (rc *RunContext) GetEnv() map[string]string {
|
|
|
|
if rc.Env == nil {
|
2020-04-18 01:04:40 +08:00
|
|
|
rc.Env = mergeMaps(rc.Config.Env, rc.Run.Workflow.Env, rc.Run.Job().Env)
|
2020-02-07 14:17:58 +08:00
|
|
|
}
|
2020-11-18 23:14:34 +08:00
|
|
|
rc.Env["ACT"] = "true"
|
2020-02-07 14:17:58 +08:00
|
|
|
return rc.Env
|
|
|
|
}
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
func (rc *RunContext) jobContainerName() string {
|
2020-02-27 15:29:43 +08:00
|
|
|
return createContainerName("act", rc.String())
|
2020-02-07 14:17:58 +08:00
|
|
|
}
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
func (rc *RunContext) startJobContainer() common.Executor {
|
2020-03-17 05:58:10 +08:00
|
|
|
image := rc.platformImage()
|
2020-02-24 07:01:25 +08:00
|
|
|
|
|
|
|
return func(ctx context.Context) error {
|
|
|
|
rawLogger := common.Logger(ctx).WithField("raw_output", true)
|
2020-02-25 04:48:12 +08:00
|
|
|
logWriter := common.NewLineWriter(rc.commandHandler(ctx), func(s string) bool {
|
2020-02-24 07:01:25 +08:00
|
|
|
if rc.Config.LogOutput {
|
2020-06-24 22:05:05 +08:00
|
|
|
rawLogger.Infof("%s", s)
|
2020-02-24 07:01:25 +08:00
|
|
|
} else {
|
2020-06-24 22:05:05 +08:00
|
|
|
rawLogger.Debugf("%s", s)
|
2020-02-24 07:01:25 +08:00
|
|
|
}
|
2020-02-25 04:48:12 +08:00
|
|
|
return true
|
2020-02-24 07:01:25 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
common.Logger(ctx).Infof("\U0001f680 Start image=%s", image)
|
|
|
|
name := rc.jobContainerName()
|
|
|
|
|
2020-02-25 02:56:49 +08:00
|
|
|
envList := make([]string, 0)
|
|
|
|
bindModifiers := ""
|
|
|
|
if runtime.GOOS == "darwin" {
|
|
|
|
bindModifiers = ":delegated"
|
|
|
|
}
|
|
|
|
|
2020-02-26 00:52:05 +08:00
|
|
|
envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TOOL_CACHE", "/opt/hostedtoolcache"))
|
2020-04-23 14:57:36 +08:00
|
|
|
envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_OS", "Linux"))
|
2020-04-23 23:18:36 +08:00
|
|
|
envList = append(envList, fmt.Sprintf("%s=%s", "RUNNER_TEMP", "/tmp"))
|
2020-02-25 02:56:49 +08:00
|
|
|
|
2020-02-25 09:48:21 +08:00
|
|
|
binds := []string{
|
|
|
|
fmt.Sprintf("%s:%s", "/var/run/docker.sock", "/var/run/docker.sock"),
|
|
|
|
}
|
|
|
|
if rc.Config.BindWorkdir {
|
|
|
|
binds = append(binds, fmt.Sprintf("%s:%s%s", rc.Config.Workdir, "/github/workspace", bindModifiers))
|
|
|
|
}
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
rc.JobContainer = container.NewContainer(&container.NewContainerInput{
|
|
|
|
Cmd: nil,
|
2020-02-24 14:34:48 +08:00
|
|
|
Entrypoint: []string{"/usr/bin/tail", "-f", "/dev/null"},
|
2020-02-24 07:01:25 +08:00
|
|
|
WorkingDir: "/github/workspace",
|
|
|
|
Image: image,
|
|
|
|
Name: name,
|
2020-02-25 02:56:49 +08:00
|
|
|
Env: envList,
|
2020-02-24 07:01:25 +08:00
|
|
|
Mounts: map[string]string{
|
2020-02-25 04:48:12 +08:00
|
|
|
name: "/github",
|
|
|
|
"act-toolcache": "/toolcache",
|
2020-02-25 08:38:49 +08:00
|
|
|
"act-actions": "/actions",
|
2020-02-24 07:01:25 +08:00
|
|
|
},
|
2020-03-10 08:43:24 +08:00
|
|
|
NetworkMode: "host",
|
|
|
|
Binds: binds,
|
|
|
|
Stdout: logWriter,
|
|
|
|
Stderr: logWriter,
|
2020-08-02 04:21:49 +08:00
|
|
|
Privileged: rc.Config.Privileged,
|
2020-02-24 07:01:25 +08:00
|
|
|
})
|
|
|
|
|
2020-03-10 08:45:42 +08:00
|
|
|
var copyWorkspace bool
|
|
|
|
var copyToPath string
|
|
|
|
if !rc.Config.BindWorkdir {
|
|
|
|
copyToPath, copyWorkspace = rc.localCheckoutPath()
|
|
|
|
copyToPath = filepath.Join("/github/workspace", copyToPath)
|
|
|
|
}
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
return common.NewPipelineExecutor(
|
|
|
|
rc.JobContainer.Pull(rc.Config.ForcePull),
|
2020-06-18 23:21:55 +08:00
|
|
|
rc.stopJobContainer(),
|
2020-02-24 07:01:25 +08:00
|
|
|
rc.JobContainer.Create(),
|
|
|
|
rc.JobContainer.Start(false),
|
2020-10-09 13:30:50 +08:00
|
|
|
rc.JobContainer.CopyDir(copyToPath, rc.Config.Workdir+string(filepath.Separator)+".").IfBool(copyWorkspace),
|
2020-02-24 07:01:25 +08:00
|
|
|
rc.JobContainer.Copy("/github/", &container.FileEntry{
|
|
|
|
Name: "workflow/event.json",
|
2020-08-09 04:31:26 +08:00
|
|
|
Mode: 0644,
|
2020-02-24 07:01:25 +08:00
|
|
|
Body: rc.EventJSON,
|
2021-01-12 14:39:43 +08:00
|
|
|
}, &container.FileEntry{
|
|
|
|
Name: "workflow/envs.txt",
|
|
|
|
Mode: 0644,
|
|
|
|
Body: "",
|
2020-02-25 08:38:49 +08:00
|
|
|
}, &container.FileEntry{
|
|
|
|
Name: "home/.act",
|
2020-08-09 04:31:26 +08:00
|
|
|
Mode: 0644,
|
2020-02-25 08:38:49 +08:00
|
|
|
Body: "",
|
2020-02-24 07:01:25 +08:00
|
|
|
}),
|
|
|
|
)(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
func (rc *RunContext) execJobContainer(cmd []string, env map[string]string) common.Executor {
|
|
|
|
return func(ctx context.Context) error {
|
|
|
|
return rc.JobContainer.Exec(cmd, env)(ctx)
|
|
|
|
}
|
|
|
|
}
|
2020-06-18 23:21:55 +08:00
|
|
|
|
|
|
|
// stopJobContainer removes the job container (if it exists) and its volume (if it exists) if !rc.Config.ReuseContainers
|
2020-02-24 07:01:25 +08:00
|
|
|
func (rc *RunContext) stopJobContainer() common.Executor {
|
|
|
|
return func(ctx context.Context) error {
|
|
|
|
if rc.JobContainer != nil && !rc.Config.ReuseContainers {
|
|
|
|
return rc.JobContainer.Remove().
|
|
|
|
Then(container.NewDockerVolumeRemoveExecutor(rc.jobContainerName(), false))(ctx)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-25 02:56:49 +08:00
|
|
|
// ActionCacheDir is for rc
|
|
|
|
func (rc *RunContext) ActionCacheDir() string {
|
2020-02-24 14:34:48 +08:00
|
|
|
var xdgCache string
|
|
|
|
var ok bool
|
|
|
|
if xdgCache, ok = os.LookupEnv("XDG_CACHE_HOME"); !ok {
|
|
|
|
if home, ok := os.LookupEnv("HOME"); ok {
|
|
|
|
xdgCache = fmt.Sprintf("%s/.cache", home)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return filepath.Join(xdgCache, "act")
|
|
|
|
}
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
// Executor returns a pipeline executor for all the steps in the job
|
|
|
|
func (rc *RunContext) Executor() common.Executor {
|
2020-02-07 14:17:58 +08:00
|
|
|
steps := make([]common.Executor, 0)
|
2020-02-27 15:29:43 +08:00
|
|
|
|
|
|
|
steps = append(steps, func(ctx context.Context) error {
|
|
|
|
if len(rc.Matrix) > 0 {
|
|
|
|
common.Logger(ctx).Infof("\U0001F9EA Matrix: %v", rc.Matrix)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
steps = append(steps, rc.startJobContainer())
|
2020-02-07 14:17:58 +08:00
|
|
|
|
2020-02-11 07:27:05 +08:00
|
|
|
for i, step := range rc.Run.Job().Steps {
|
|
|
|
if step.ID == "" {
|
|
|
|
step.ID = fmt.Sprintf("%d", i)
|
|
|
|
}
|
2020-02-24 07:01:25 +08:00
|
|
|
steps = append(steps, rc.newStepExecutor(step))
|
|
|
|
}
|
|
|
|
steps = append(steps, rc.stopJobContainer())
|
2020-02-18 02:11:16 +08:00
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
return common.NewPipelineExecutor(steps...).If(rc.isEnabled)
|
|
|
|
}
|
2020-02-18 02:11:16 +08:00
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
|
|
|
|
sc := &StepContext{
|
|
|
|
RunContext: rc,
|
|
|
|
Step: step,
|
2020-02-07 14:17:58 +08:00
|
|
|
}
|
2020-02-18 02:11:16 +08:00
|
|
|
return func(ctx context.Context) error {
|
2020-02-24 07:01:25 +08:00
|
|
|
rc.CurrentStep = sc.Step.ID
|
|
|
|
rc.StepResults[rc.CurrentStep] = &stepResult{
|
|
|
|
Success: true,
|
|
|
|
Outputs: make(map[string]string),
|
2020-02-18 02:11:16 +08:00
|
|
|
}
|
2020-03-07 05:39:01 +08:00
|
|
|
|
2021-01-13 08:02:54 +08:00
|
|
|
exprEval, err := sc.setupEnv(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2021-01-12 14:39:43 +08:00
|
|
|
}
|
2021-01-15 13:37:38 +08:00
|
|
|
rc.ExprEval = exprEval
|
2020-02-18 02:11:16 +08:00
|
|
|
|
2020-11-18 01:31:05 +08:00
|
|
|
runStep, err := rc.EvalBool(sc.Step.If)
|
|
|
|
if err != nil {
|
|
|
|
common.Logger(ctx).Errorf(" \u274C Error in if: expression - %s", sc.Step)
|
|
|
|
rc.StepResults[rc.CurrentStep].Success = false
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !runStep {
|
2020-02-24 07:01:25 +08:00
|
|
|
log.Debugf("Skipping step '%s' due to '%s'", sc.Step.String(), sc.Step.If)
|
2020-02-18 02:11:16 +08:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
common.Logger(ctx).Infof("\u2B50 Run %s", sc.Step)
|
2020-11-18 01:31:05 +08:00
|
|
|
err = sc.Executor()(ctx)
|
2020-02-24 07:01:25 +08:00
|
|
|
if err == nil {
|
|
|
|
common.Logger(ctx).Infof(" \u2705 Success - %s", sc.Step)
|
|
|
|
} else {
|
|
|
|
common.Logger(ctx).Errorf(" \u274C Failure - %s", sc.Step)
|
|
|
|
rc.StepResults[rc.CurrentStep].Success = false
|
2020-02-21 11:43:20 +08:00
|
|
|
}
|
2020-02-24 07:01:25 +08:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
2020-02-21 11:43:20 +08:00
|
|
|
|
2020-03-17 05:58:10 +08:00
|
|
|
func (rc *RunContext) platformImage() string {
|
|
|
|
job := rc.Run.Job()
|
|
|
|
|
|
|
|
c := job.Container()
|
|
|
|
if c != nil {
|
2020-11-11 06:55:27 +08:00
|
|
|
return rc.ExprEval.Interpolate(c.Image)
|
2020-03-17 05:58:10 +08:00
|
|
|
}
|
|
|
|
|
2021-01-21 22:02:48 +08:00
|
|
|
if job.RunsOn() == nil {
|
|
|
|
log.Errorf("'runs-on' key not defined in %s", rc.String())
|
|
|
|
}
|
|
|
|
|
2020-03-17 05:58:10 +08:00
|
|
|
for _, runnerLabel := range job.RunsOn() {
|
|
|
|
platformName := rc.ExprEval.Interpolate(runnerLabel)
|
|
|
|
image := rc.Config.Platforms[strings.ToLower(platformName)]
|
|
|
|
if image != "" {
|
|
|
|
return image
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
func (rc *RunContext) isEnabled(ctx context.Context) bool {
|
|
|
|
job := rc.Run.Job()
|
2020-05-05 03:18:13 +08:00
|
|
|
l := common.Logger(ctx)
|
2020-11-18 01:31:05 +08:00
|
|
|
runJob, err := rc.EvalBool(job.If)
|
|
|
|
if err != nil {
|
|
|
|
common.Logger(ctx).Errorf(" \u274C Error in if: expression - %s", job.Name)
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if !runJob {
|
2020-05-05 03:18:13 +08:00
|
|
|
l.Debugf("Skipping job '%s' due to '%s'", job.Name, job.If)
|
2020-02-24 07:01:25 +08:00
|
|
|
return false
|
|
|
|
}
|
2020-02-21 11:43:20 +08:00
|
|
|
|
2020-03-17 05:58:10 +08:00
|
|
|
img := rc.platformImage()
|
|
|
|
if img == "" {
|
2021-01-21 22:02:48 +08:00
|
|
|
if job.RunsOn() == nil {
|
|
|
|
log.Errorf("'runs-on' key not defined in %s", rc.String())
|
|
|
|
}
|
|
|
|
|
2020-12-09 02:13:07 +08:00
|
|
|
for _, runnerLabel := range job.RunsOn() {
|
|
|
|
platformName := rc.ExprEval.Interpolate(runnerLabel)
|
|
|
|
l.Infof("\U0001F6A7 Skipping unsupported platform '%+v'", platformName)
|
|
|
|
}
|
2020-02-24 07:01:25 +08:00
|
|
|
return false
|
2020-02-18 02:11:16 +08:00
|
|
|
}
|
2020-02-24 07:01:25 +08:00
|
|
|
return true
|
2020-02-18 02:11:16 +08:00
|
|
|
}
|
|
|
|
|
2021-01-12 14:39:43 +08:00
|
|
|
var splitPattern *regexp.Regexp
|
|
|
|
|
2020-02-18 02:11:16 +08:00
|
|
|
// EvalBool evaluates an expression against current run context
|
2020-11-18 01:31:05 +08:00
|
|
|
func (rc *RunContext) EvalBool(expr string) (bool, error) {
|
2021-01-12 14:39:43 +08:00
|
|
|
if splitPattern == nil {
|
|
|
|
splitPattern = regexp.MustCompile(fmt.Sprintf(`%s|%s|\S+`, expressionPattern.String(), operatorPattern.String()))
|
|
|
|
}
|
2020-11-18 01:31:05 +08:00
|
|
|
if strings.HasPrefix(strings.TrimSpace(expr), "!") {
|
|
|
|
return false, errors.New("expressions starting with ! must be wrapped in ${{ }}")
|
|
|
|
}
|
2020-02-18 02:11:16 +08:00
|
|
|
if expr != "" {
|
2020-11-18 01:31:05 +08:00
|
|
|
parts := splitPattern.FindAllString(expr, -1)
|
2020-11-13 00:15:09 +08:00
|
|
|
var evaluatedParts []string
|
2020-11-18 01:31:05 +08:00
|
|
|
for i, part := range parts {
|
|
|
|
if operatorPattern.MatchString(part) {
|
2020-11-13 00:15:09 +08:00
|
|
|
evaluatedParts = append(evaluatedParts, part)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-11-18 01:31:05 +08:00
|
|
|
interpolatedPart, isString := rc.ExprEval.InterpolateWithStringCheck(part)
|
|
|
|
|
|
|
|
// This peculiar transformation has to be done because the Github parser
|
2021-01-15 13:21:44 +08:00
|
|
|
// treats false returned from contexts as a string, not a boolean.
|
2020-11-18 01:31:05 +08:00
|
|
|
// Hence env.SOMETHING will be evaluated to true in an if: expression
|
|
|
|
// regardless if SOMETHING is set to false, true or any other string.
|
|
|
|
// It also handles some other weirdness that I found by trial and error.
|
|
|
|
if (expressionPattern.MatchString(part) && // it is an expression
|
|
|
|
!strings.Contains(part, "!")) && // but it's not negated
|
|
|
|
interpolatedPart == "false" && // and the interpolated string is false
|
|
|
|
(isString || previousOrNextPartIsAnOperator(i, parts)) { // and it's of type string or has an logical operator before or after
|
|
|
|
|
|
|
|
interpolatedPart = fmt.Sprintf("'%s'", interpolatedPart) // then we have to quote the false expression
|
2020-11-13 00:15:09 +08:00
|
|
|
}
|
2020-11-18 01:31:05 +08:00
|
|
|
|
|
|
|
evaluatedParts = append(evaluatedParts, interpolatedPart)
|
2020-11-13 00:15:09 +08:00
|
|
|
}
|
|
|
|
|
2020-11-18 01:31:05 +08:00
|
|
|
joined := strings.Join(evaluatedParts, " ")
|
|
|
|
v, _, err := rc.ExprEval.Evaluate(fmt.Sprintf("Boolean(%s)", joined))
|
2020-02-18 02:11:16 +08:00
|
|
|
if err != nil {
|
2021-01-19 03:44:27 +08:00
|
|
|
return false, err
|
2020-02-18 02:11:16 +08:00
|
|
|
}
|
2020-03-07 03:30:39 +08:00
|
|
|
log.Debugf("expression '%s' evaluated to '%s'", expr, v)
|
2020-11-18 01:31:05 +08:00
|
|
|
return v == "true", nil
|
2020-02-18 02:11:16 +08:00
|
|
|
}
|
2020-11-18 01:31:05 +08:00
|
|
|
return true, nil
|
2020-11-13 00:15:09 +08:00
|
|
|
}
|
|
|
|
|
2020-11-18 01:31:05 +08:00
|
|
|
func previousOrNextPartIsAnOperator(i int, parts []string) bool {
|
|
|
|
operator := false
|
|
|
|
if i > 0 {
|
|
|
|
operator = operatorPattern.MatchString(parts[i-1])
|
|
|
|
}
|
|
|
|
if i+1 < len(parts) {
|
|
|
|
operator = operator || operatorPattern.MatchString(parts[i+1])
|
2020-11-13 00:15:09 +08:00
|
|
|
}
|
2020-11-18 01:31:05 +08:00
|
|
|
return operator
|
2020-11-13 00:15:09 +08:00
|
|
|
}
|
|
|
|
|
2020-02-07 14:17:58 +08:00
|
|
|
func mergeMaps(maps ...map[string]string) map[string]string {
|
|
|
|
rtnMap := make(map[string]string)
|
|
|
|
for _, m := range maps {
|
|
|
|
for k, v := range m {
|
|
|
|
rtnMap[k] = v
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rtnMap
|
|
|
|
}
|
|
|
|
|
2020-02-24 07:01:25 +08:00
|
|
|
func createContainerName(parts ...string) string {
|
|
|
|
name := make([]string, 0)
|
|
|
|
pattern := regexp.MustCompile("[^a-zA-Z0-9]")
|
2020-02-24 08:36:44 +08:00
|
|
|
partLen := (30 / len(parts)) - 1
|
2020-02-24 14:34:48 +08:00
|
|
|
for i, part := range parts {
|
|
|
|
if i == len(parts)-1 {
|
|
|
|
name = append(name, pattern.ReplaceAllString(part, "-"))
|
|
|
|
} else {
|
|
|
|
name = append(name, trimToLen(pattern.ReplaceAllString(part, "-"), partLen))
|
|
|
|
}
|
2020-02-24 07:01:25 +08:00
|
|
|
}
|
2020-04-17 07:05:00 +08:00
|
|
|
return strings.Trim(strings.Join(name, "-"), "-")
|
2020-02-07 14:17:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func trimToLen(s string, l int) string {
|
2020-02-21 11:43:20 +08:00
|
|
|
if l < 0 {
|
|
|
|
l = 0
|
|
|
|
}
|
2020-02-07 14:17:58 +08:00
|
|
|
if len(s) > l {
|
|
|
|
return s[:l]
|
|
|
|
}
|
|
|
|
return s
|
|
|
|
}
|
2020-02-14 16:41:20 +08:00
|
|
|
|
|
|
|
type jobContext struct {
|
|
|
|
Status string `json:"status"`
|
|
|
|
Container struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Network string `json:"network"`
|
|
|
|
} `json:"container"`
|
|
|
|
Services map[string]struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
} `json:"services"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rc *RunContext) getJobContext() *jobContext {
|
|
|
|
jobStatus := "success"
|
|
|
|
for _, stepStatus := range rc.StepResults {
|
|
|
|
if !stepStatus.Success {
|
|
|
|
jobStatus = "failure"
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return &jobContext{
|
|
|
|
Status: jobStatus,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rc *RunContext) getStepsContext() map[string]*stepResult {
|
|
|
|
return rc.StepResults
|
|
|
|
}
|
|
|
|
|
|
|
|
type githubContext struct {
|
|
|
|
Event map[string]interface{} `json:"event"`
|
|
|
|
EventPath string `json:"event_path"`
|
|
|
|
Workflow string `json:"workflow"`
|
|
|
|
RunID string `json:"run_id"`
|
|
|
|
RunNumber string `json:"run_number"`
|
|
|
|
Actor string `json:"actor"`
|
|
|
|
Repository string `json:"repository"`
|
|
|
|
EventName string `json:"event_name"`
|
|
|
|
Sha string `json:"sha"`
|
|
|
|
Ref string `json:"ref"`
|
|
|
|
HeadRef string `json:"head_ref"`
|
|
|
|
BaseRef string `json:"base_ref"`
|
|
|
|
Token string `json:"token"`
|
|
|
|
Workspace string `json:"workspace"`
|
|
|
|
Action string `json:"action"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (rc *RunContext) getGithubContext() *githubContext {
|
2020-03-07 06:17:57 +08:00
|
|
|
token, ok := rc.Config.Secrets["GITHUB_TOKEN"]
|
|
|
|
if !ok {
|
|
|
|
token = os.Getenv("GITHUB_TOKEN")
|
|
|
|
}
|
2020-09-23 05:13:29 +08:00
|
|
|
runID := rc.Config.Env["GITHUB_RUN_ID"]
|
2020-09-30 04:39:45 +08:00
|
|
|
if runID == "" {
|
2020-09-23 05:13:29 +08:00
|
|
|
runID = "1"
|
|
|
|
}
|
|
|
|
runNumber := rc.Config.Env["GITHUB_RUN_NUMBER"]
|
2020-09-30 04:39:45 +08:00
|
|
|
if runNumber == "" {
|
2020-09-23 05:13:29 +08:00
|
|
|
runNumber = "1"
|
|
|
|
}
|
2020-02-14 16:41:20 +08:00
|
|
|
ghc := &githubContext{
|
|
|
|
Event: make(map[string]interface{}),
|
|
|
|
EventPath: "/github/workflow/event.json",
|
|
|
|
Workflow: rc.Run.Workflow.Name,
|
2020-09-23 05:13:29 +08:00
|
|
|
RunID: runID,
|
|
|
|
RunNumber: runNumber,
|
2020-05-12 15:14:56 +08:00
|
|
|
Actor: rc.Config.Actor,
|
2020-02-14 16:41:20 +08:00
|
|
|
EventName: rc.Config.EventName,
|
2020-03-07 06:17:57 +08:00
|
|
|
Token: token,
|
2020-02-14 16:41:20 +08:00
|
|
|
Workspace: "/github/workspace",
|
|
|
|
Action: rc.CurrentStep,
|
|
|
|
}
|
|
|
|
|
2020-05-12 15:14:56 +08:00
|
|
|
// Backwards compatibility for configs that require
|
|
|
|
// a default rather than being run as a cmd
|
|
|
|
if ghc.Actor == "" {
|
|
|
|
ghc.Actor = "nektos/act"
|
|
|
|
}
|
|
|
|
|
2020-02-14 16:41:20 +08:00
|
|
|
repoPath := rc.Config.Workdir
|
|
|
|
repo, err := common.FindGithubRepo(repoPath)
|
|
|
|
if err != nil {
|
|
|
|
log.Warningf("unable to get git repo: %v", err)
|
|
|
|
} else {
|
|
|
|
ghc.Repository = repo
|
|
|
|
}
|
|
|
|
|
|
|
|
_, sha, err := common.FindGitRevision(repoPath)
|
|
|
|
if err != nil {
|
|
|
|
log.Warningf("unable to get git revision: %v", err)
|
|
|
|
} else {
|
|
|
|
ghc.Sha = sha
|
|
|
|
}
|
|
|
|
|
|
|
|
ref, err := common.FindGitRef(repoPath)
|
|
|
|
if err != nil {
|
|
|
|
log.Warningf("unable to get git ref: %v", err)
|
|
|
|
} else {
|
|
|
|
log.Debugf("using github ref: %s", ref)
|
|
|
|
ghc.Ref = ref
|
|
|
|
}
|
2020-05-05 03:18:13 +08:00
|
|
|
if rc.EventJSON != "" {
|
|
|
|
err = json.Unmarshal([]byte(rc.EventJSON), &ghc.Event)
|
|
|
|
if err != nil {
|
2020-11-13 00:15:09 +08:00
|
|
|
log.Errorf("Unable to Unmarshal event '%s': %v", rc.EventJSON, err)
|
2020-05-05 03:18:13 +08:00
|
|
|
}
|
2020-02-14 16:41:20 +08:00
|
|
|
}
|
2020-03-07 06:17:57 +08:00
|
|
|
|
2020-09-02 22:56:44 +08:00
|
|
|
// set the branch in the event data
|
|
|
|
if rc.Config.DefaultBranch != "" {
|
|
|
|
ghc.Event = withDefaultBranch(rc.Config.DefaultBranch, ghc.Event)
|
|
|
|
} else {
|
|
|
|
ghc.Event = withDefaultBranch("master", ghc.Event)
|
|
|
|
}
|
|
|
|
|
2020-03-07 06:17:57 +08:00
|
|
|
if ghc.EventName == "pull_request" {
|
|
|
|
ghc.BaseRef = asString(nestedMapLookup(ghc.Event, "pull_request", "base", "ref"))
|
|
|
|
ghc.HeadRef = asString(nestedMapLookup(ghc.Event, "pull_request", "head", "ref"))
|
|
|
|
}
|
|
|
|
|
2020-02-14 16:41:20 +08:00
|
|
|
return ghc
|
|
|
|
}
|
|
|
|
|
2020-03-10 08:45:42 +08:00
|
|
|
func (ghc *githubContext) isLocalCheckout(step *model.Step) bool {
|
|
|
|
if step.Type() != model.StepTypeUsesActionRemote {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
remoteAction := newRemoteAction(step.Uses)
|
|
|
|
if !remoteAction.IsCheckout() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if repository, ok := step.With["repository"]; ok && repository != ghc.Repository {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if repository, ok := step.With["ref"]; ok && repository != ghc.Ref {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-03-07 06:17:57 +08:00
|
|
|
func asString(v interface{}) string {
|
|
|
|
if v == nil {
|
|
|
|
return ""
|
|
|
|
} else if s, ok := v.(string); ok {
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func nestedMapLookup(m map[string]interface{}, ks ...string) (rval interface{}) {
|
|
|
|
var ok bool
|
|
|
|
|
|
|
|
if len(ks) == 0 { // degenerate input
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if rval, ok = m[ks[0]]; !ok {
|
|
|
|
return nil
|
|
|
|
} else if len(ks) == 1 { // we've reached the final key
|
|
|
|
return rval
|
|
|
|
} else if m, ok = rval.(map[string]interface{}); !ok {
|
|
|
|
return nil
|
|
|
|
} else { // 1+ more keys
|
|
|
|
return nestedMapLookup(m, ks[1:]...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-02 22:56:44 +08:00
|
|
|
func withDefaultBranch(b string, event map[string]interface{}) map[string]interface{} {
|
|
|
|
repoI, ok := event["repository"]
|
|
|
|
if !ok {
|
|
|
|
repoI = make(map[string]interface{})
|
|
|
|
}
|
|
|
|
|
|
|
|
repo, ok := repoI.(map[string]interface{})
|
|
|
|
if !ok {
|
|
|
|
log.Warnf("unable to set default branch to %v", b)
|
|
|
|
return event
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the branch is already there return with no changes
|
|
|
|
if _, ok = repo["default_branch"]; ok {
|
|
|
|
return event
|
|
|
|
}
|
|
|
|
|
|
|
|
repo["default_branch"] = b
|
|
|
|
event["repository"] = repo
|
|
|
|
|
|
|
|
return event
|
|
|
|
}
|
|
|
|
|
2020-02-14 16:41:20 +08:00
|
|
|
func (rc *RunContext) withGithubEnv(env map[string]string) map[string]string {
|
|
|
|
github := rc.getGithubContext()
|
2020-09-28 23:22:42 +08:00
|
|
|
env["CI"] = "true"
|
2020-02-14 16:41:20 +08:00
|
|
|
env["HOME"] = "/github/home"
|
2021-01-12 14:39:43 +08:00
|
|
|
env["GITHUB_ENV"] = "/github/workflow/envs.txt"
|
|
|
|
|
2020-02-14 16:41:20 +08:00
|
|
|
env["GITHUB_WORKFLOW"] = github.Workflow
|
|
|
|
env["GITHUB_RUN_ID"] = github.RunID
|
|
|
|
env["GITHUB_RUN_NUMBER"] = github.RunNumber
|
|
|
|
env["GITHUB_ACTION"] = github.Action
|
2020-02-25 02:56:49 +08:00
|
|
|
env["GITHUB_ACTIONS"] = "true"
|
2020-02-14 16:41:20 +08:00
|
|
|
env["GITHUB_ACTOR"] = github.Actor
|
|
|
|
env["GITHUB_REPOSITORY"] = github.Repository
|
|
|
|
env["GITHUB_EVENT_NAME"] = github.EventName
|
|
|
|
env["GITHUB_EVENT_PATH"] = github.EventPath
|
|
|
|
env["GITHUB_WORKSPACE"] = github.Workspace
|
|
|
|
env["GITHUB_SHA"] = github.Sha
|
|
|
|
env["GITHUB_REF"] = github.Ref
|
2020-03-10 08:45:42 +08:00
|
|
|
env["GITHUB_TOKEN"] = github.Token
|
2020-02-14 16:41:20 +08:00
|
|
|
return env
|
|
|
|
}
|
2020-03-10 08:45:42 +08:00
|
|
|
|
|
|
|
func (rc *RunContext) localCheckoutPath() (string, bool) {
|
|
|
|
ghContext := rc.getGithubContext()
|
|
|
|
for _, step := range rc.Run.Job().Steps {
|
|
|
|
if ghContext.isLocalCheckout(step) {
|
|
|
|
return step.With["path"], true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "", false
|
|
|
|
}
|