From 88041afb87a9dce13624d4d03b6a0641611d7a38 Mon Sep 17 00:00:00 2001 From: Casey Lee Date: Sun, 23 Feb 2020 22:34:48 -0800 Subject: [PATCH] cache dir for remote actions --- pkg/container/docker_run.go | 2 +- pkg/container/docker_run_test.go | 52 ------------------ pkg/runner/expression.go | 11 ++++ pkg/runner/run_context.go | 31 +++++++---- pkg/runner/step_context.go | 91 +++++++++++++++----------------- 5 files changed, 76 insertions(+), 111 deletions(-) delete mode 100644 pkg/container/docker_run_test.go diff --git a/pkg/container/docker_run.go b/pkg/container/docker_run.go index 81a6464..f964b41 100644 --- a/pkg/container/docker_run.go +++ b/pkg/container/docker_run.go @@ -171,7 +171,7 @@ func (cr *containerReference) remove() common.Executor { Force: true, }) if err != nil { - return errors.WithStack(err) + logger.Error(errors.WithStack(err)) } logger.Debugf("Removed container: %v", cr.id) diff --git a/pkg/container/docker_run_test.go b/pkg/container/docker_run_test.go deleted file mode 100644 index b68d329..0000000 --- a/pkg/container/docker_run_test.go +++ /dev/null @@ -1,52 +0,0 @@ -package container - -import ( - "bytes" - "context" - "io/ioutil" - "testing" - - "github.com/nektos/act/pkg/common" - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/assert" -) - -type rawFormatter struct{} - -func (f *rawFormatter) Format(entry *logrus.Entry) ([]byte, error) { - return []byte(entry.Message), nil -} - -func TestNewDockerRunExecutor(t *testing.T) { - if testing.Short() { - t.Skip("skipping slower test") - } - - noopLogger := logrus.New() - noopLogger.SetOutput(ioutil.Discard) - - buf := &bytes.Buffer{} - logger := logrus.New() - logger.SetOutput(buf) - logger.SetFormatter(&rawFormatter{}) - - ctx := common.WithLogger(context.Background(), logger) - - runner := NewDockerRunExecutor(NewDockerRunExecutorInput{ - Image: "hello-world", - Stdout: buf, - }) - - puller := NewDockerPullExecutor(NewDockerPullExecutorInput{ - Image: "hello-world", - }) - - pipeline := common.NewPipelineExecutor(puller, runner) - err := pipeline(ctx) - assert.NoError(t, err) - - actual := buf.String() - assert.Contains(t, actual, `docker pull hello-world`) - assert.Contains(t, actual, `docker run image=hello-world entrypoint=[] cmd=[]`) - assert.Contains(t, actual, `Hello from Docker!`) -} diff --git a/pkg/runner/expression.go b/pkg/runner/expression.go index 9de525c..652800e 100644 --- a/pkg/runner/expression.go +++ b/pkg/runner/expression.go @@ -38,6 +38,7 @@ func (sc *StepContext) NewExpressionEvaluator() ExpressionEvaluator { vm := sc.RunContext.newVM() configers := []func(*otto.Otto){ sc.vmEnv(), + sc.vmInputs(), } for _, configer := range configers { configer(vm) @@ -241,6 +242,16 @@ func (sc *StepContext) vmEnv() func(*otto.Otto) { } } +func (sc *StepContext) vmInputs() func(*otto.Otto) { + inputs := make(map[string]string) + for k, v := range sc.Step.With { + inputs[k] = v + } + return func(vm *otto.Otto) { + _ = vm.Set("inputs", inputs) + } +} + func (rc *RunContext) vmJob() func(*otto.Otto) { job := rc.getJobContext() diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 8173725..10fe85c 100644 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -45,7 +45,7 @@ func (rc *RunContext) GetEnv() map[string]string { } func (rc *RunContext) jobContainerName() string { - return createContainerName(filepath.Base(rc.Config.Workdir), rc.Run.String()) + return createContainerName("act", rc.Run.String()) } func (rc *RunContext) startJobContainer() common.Executor { @@ -74,7 +74,7 @@ func (rc *RunContext) startJobContainer() common.Executor { rc.JobContainer = container.NewContainer(&container.NewContainerInput{ Cmd: nil, - Entrypoint: []string{"/bin/cat"}, + Entrypoint: []string{"/usr/bin/tail", "-f", "/dev/null"}, WorkingDir: "/github/workspace", Image: image, Name: name, @@ -83,6 +83,7 @@ func (rc *RunContext) startJobContainer() common.Executor { }, Binds: []string{ fmt.Sprintf("%s:%s", rc.Config.Workdir, "/github/workspace"), + fmt.Sprintf("%s:%s", rc.ActionDir(), "/github/home/.act"), fmt.Sprintf("%s:%s", "/var/run/docker.sock", "/var/run/docker.sock"), }, Stdout: logWriter, @@ -98,10 +99,6 @@ func (rc *RunContext) startJobContainer() common.Executor { Name: "workflow/event.json", Mode: 644, Body: rc.EventJSON, - }, &container.FileEntry{ - Name: "home/.actions/.keep", - Mode: 644, - Body: "", }), )(ctx) } @@ -121,6 +118,18 @@ func (rc *RunContext) stopJobContainer() common.Executor { } } +// ActionDir is for rc +func (rc *RunContext) ActionDir() string { + 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") +} + // Executor returns a pipeline executor for all the steps in the job func (rc *RunContext) Executor() common.Executor { steps := make([]common.Executor, 0) @@ -210,10 +219,14 @@ func createContainerName(parts ...string) string { name := make([]string, 0) pattern := regexp.MustCompile("[^a-zA-Z0-9]") partLen := (30 / len(parts)) - 1 - for _, part := range parts { - name = append(name, trimToLen(pattern.ReplaceAllString(part, "-"), partLen)) + 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)) + } } - return strings.Join(name, "-") + return trimToLen(strings.Trim(strings.Join(name, "-"), "-"), 30) } func trimToLen(s string, l int) string { diff --git a/pkg/runner/step_context.go b/pkg/runner/step_context.go index 2a252d0..9600afd 100644 --- a/pkg/runner/step_context.go +++ b/pkg/runner/step_context.go @@ -49,54 +49,37 @@ func (sc *StepContext) Executor() common.Executor { ) case model.StepTypeUsesActionLocal: + actionDir := filepath.Join(rc.Config.Workdir, step.Uses) return common.NewPipelineExecutor( sc.setupEnv(), - sc.setupAction(), - sc.runAction(), + sc.setupAction(actionDir), + sc.runAction(actionDir), + ) + case model.StepTypeUsesActionRemote: + remoteAction := newRemoteAction(step.Uses) + if remoteAction.Org == "actions" && remoteAction.Repo == "checkout" { + return func(ctx context.Context) error { + common.Logger(ctx).Debugf("Skipping actions/checkout") + return nil + } + } + + actionDir := rc.ActionDir() + return common.NewPipelineExecutor( + common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{ + URL: remoteAction.CloneURL(), + Ref: remoteAction.Ref, + Dir: actionDir, + }), + sc.setupEnv(), + sc.setupAction(actionDir), + sc.runAction(actionDir), ) - /* - case model.StepTypeUsesActionRemote: - remoteAction := newRemoteAction(step.Uses) - if remoteAction.Org == "actions" && remoteAction.Repo == "checkout" { - return func(ctx context.Context) error { - common.Logger(ctx).Debugf("Skipping actions/checkout") - return nil - } - } - cloneDir, err := ioutil.TempDir(rc.Tempdir, remoteAction.Repo) - if err != nil { - return common.NewErrorExecutor(err) - } - return common.NewPipelineExecutor( - common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{ - URL: remoteAction.CloneURL(), - Ref: remoteAction.Ref, - Dir: cloneDir, - }), - sc.setupEnv(), - sc.setupAction(), - applyWith(containerSpec, step), - rc.pullImage(containerSpec), - rc.runContainer(containerSpec), - ) - */ } 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 - } -} - func (sc *StepContext) setupEnv() common.Executor { rc := sc.RunContext job := rc.Run.Job() @@ -160,6 +143,13 @@ func (sc *StepContext) newStepContainer(ctx context.Context, image string, cmd [ for k, v := range sc.Env { envList = append(envList, fmt.Sprintf("%s=%s", k, v)) } + stepEE := sc.NewExpressionEvaluator() + for i, v := range cmd { + cmd[i] = stepEE.Interpolate(v) + } + for i, v := range entrypoint { + entrypoint[i] = stepEE.Interpolate(v) + } stepContainer := container.NewContainer(&container.NewContainerInput{ Cmd: cmd, Entrypoint: entrypoint, @@ -184,8 +174,8 @@ func (sc *StepContext) runUsesContainer() common.Executor { step := sc.Step return func(ctx context.Context) error { image := strings.TrimPrefix(step.Uses, "docker://") - cmd := strings.Fields(rc.ExprEval.Interpolate(step.With["args"])) - entrypoint := strings.Fields(rc.ExprEval.Interpolate(step.With["entrypoint"])) + cmd := strings.Fields(step.With["args"]) + entrypoint := strings.Fields(step.With["entrypoint"]) stepContainer := sc.newStepContainer(ctx, image, cmd, entrypoint) return common.NewPipelineExecutor( @@ -199,10 +189,7 @@ func (sc *StepContext) runUsesContainer() common.Executor { } } -func (sc *StepContext) setupAction() common.Executor { - rc := sc.RunContext - step := sc.Step - actionDir := filepath.Join(rc.Config.Workdir, step.Uses) +func (sc *StepContext) setupAction(actionDir string) common.Executor { return func(ctx context.Context) error { f, err := os.Open(filepath.Join(actionDir, "action.yml")) if os.IsNotExist(err) { @@ -220,7 +207,7 @@ func (sc *StepContext) setupAction() common.Executor { } } -func (sc *StepContext) runAction() common.Executor { +func (sc *StepContext) runAction(actionDir string) common.Executor { rc := sc.RunContext step := sc.Step return func(ctx context.Context) error { @@ -236,7 +223,13 @@ func (sc *StepContext) runAction() common.Executor { switch action.Runs.Using { case model.ActionRunsUsingNode12: - return rc.execJobContainer([]string{"node", action.Runs.Main}, sc.Env)(ctx) + basePath := "." + if strings.HasPrefix(actionDir, rc.Config.Workdir) { + basePath = fmt.Sprintf("/github/workspace/%s", strings.TrimPrefix(actionDir, rc.Config.Workdir)) + } else if strings.HasPrefix(actionDir, rc.ActionDir()) { + basePath = fmt.Sprintf("/github/home/.act/%s", strings.TrimPrefix(actionDir, rc.ActionDir())) + } + return rc.execJobContainer([]string{"node", fmt.Sprintf("%s/%s", basePath, action.Runs.Main)}, sc.Env)(ctx) case model.ActionRunsUsingDocker: var prepImage common.Executor var image string @@ -245,7 +238,7 @@ func (sc *StepContext) runAction() common.Executor { } else { image = fmt.Sprintf("%s:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(step.Uses, "-"), "latest") image = strings.TrimLeft(image, "-") - contextDir := filepath.Join(rc.Config.Workdir, step.Uses, action.Runs.Main) + contextDir := filepath.Join(actionDir, action.Runs.Main) prepImage = container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{ ContextDir: contextDir, ImageTag: image,