cache dir for remote actions

This commit is contained in:
Casey Lee 2020-02-23 22:34:48 -08:00
parent 94591c58d7
commit 88041afb87
No known key found for this signature in database
GPG key ID: 1899120ECD0A1784
5 changed files with 76 additions and 111 deletions

View file

@ -171,7 +171,7 @@ func (cr *containerReference) remove() common.Executor {
Force: true, Force: true,
}) })
if err != nil { if err != nil {
return errors.WithStack(err) logger.Error(errors.WithStack(err))
} }
logger.Debugf("Removed container: %v", cr.id) logger.Debugf("Removed container: %v", cr.id)

View file

@ -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!`)
}

View file

@ -38,6 +38,7 @@ func (sc *StepContext) NewExpressionEvaluator() ExpressionEvaluator {
vm := sc.RunContext.newVM() vm := sc.RunContext.newVM()
configers := []func(*otto.Otto){ configers := []func(*otto.Otto){
sc.vmEnv(), sc.vmEnv(),
sc.vmInputs(),
} }
for _, configer := range configers { for _, configer := range configers {
configer(vm) 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) { func (rc *RunContext) vmJob() func(*otto.Otto) {
job := rc.getJobContext() job := rc.getJobContext()

View file

@ -45,7 +45,7 @@ func (rc *RunContext) GetEnv() map[string]string {
} }
func (rc *RunContext) jobContainerName() 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 { func (rc *RunContext) startJobContainer() common.Executor {
@ -74,7 +74,7 @@ func (rc *RunContext) startJobContainer() common.Executor {
rc.JobContainer = container.NewContainer(&container.NewContainerInput{ rc.JobContainer = container.NewContainer(&container.NewContainerInput{
Cmd: nil, Cmd: nil,
Entrypoint: []string{"/bin/cat"}, Entrypoint: []string{"/usr/bin/tail", "-f", "/dev/null"},
WorkingDir: "/github/workspace", WorkingDir: "/github/workspace",
Image: image, Image: image,
Name: name, Name: name,
@ -83,6 +83,7 @@ func (rc *RunContext) startJobContainer() common.Executor {
}, },
Binds: []string{ Binds: []string{
fmt.Sprintf("%s:%s", rc.Config.Workdir, "/github/workspace"), 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"), fmt.Sprintf("%s:%s", "/var/run/docker.sock", "/var/run/docker.sock"),
}, },
Stdout: logWriter, Stdout: logWriter,
@ -98,10 +99,6 @@ func (rc *RunContext) startJobContainer() common.Executor {
Name: "workflow/event.json", Name: "workflow/event.json",
Mode: 644, Mode: 644,
Body: rc.EventJSON, Body: rc.EventJSON,
}, &container.FileEntry{
Name: "home/.actions/.keep",
Mode: 644,
Body: "",
}), }),
)(ctx) )(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 // Executor returns a pipeline executor for all the steps in the job
func (rc *RunContext) Executor() common.Executor { func (rc *RunContext) Executor() common.Executor {
steps := make([]common.Executor, 0) steps := make([]common.Executor, 0)
@ -210,10 +219,14 @@ func createContainerName(parts ...string) string {
name := make([]string, 0) name := make([]string, 0)
pattern := regexp.MustCompile("[^a-zA-Z0-9]") pattern := regexp.MustCompile("[^a-zA-Z0-9]")
partLen := (30 / len(parts)) - 1 partLen := (30 / len(parts)) - 1
for _, part := range parts { for i, part := range parts {
name = append(name, trimToLen(pattern.ReplaceAllString(part, "-"), partLen)) 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 { func trimToLen(s string, l int) string {

View file

@ -49,54 +49,37 @@ func (sc *StepContext) Executor() common.Executor {
) )
case model.StepTypeUsesActionLocal: case model.StepTypeUsesActionLocal:
actionDir := filepath.Join(rc.Config.Workdir, step.Uses)
return common.NewPipelineExecutor( return common.NewPipelineExecutor(
sc.setupEnv(), sc.setupEnv(),
sc.setupAction(), sc.setupAction(actionDir),
sc.runAction(), 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)) 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 { func (sc *StepContext) setupEnv() common.Executor {
rc := sc.RunContext rc := sc.RunContext
job := rc.Run.Job() job := rc.Run.Job()
@ -160,6 +143,13 @@ func (sc *StepContext) newStepContainer(ctx context.Context, image string, cmd [
for k, v := range sc.Env { for k, v := range sc.Env {
envList = append(envList, fmt.Sprintf("%s=%s", k, v)) 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{ stepContainer := container.NewContainer(&container.NewContainerInput{
Cmd: cmd, Cmd: cmd,
Entrypoint: entrypoint, Entrypoint: entrypoint,
@ -184,8 +174,8 @@ func (sc *StepContext) runUsesContainer() common.Executor {
step := sc.Step step := sc.Step
return func(ctx context.Context) error { return func(ctx context.Context) error {
image := strings.TrimPrefix(step.Uses, "docker://") image := strings.TrimPrefix(step.Uses, "docker://")
cmd := strings.Fields(rc.ExprEval.Interpolate(step.With["args"])) cmd := strings.Fields(step.With["args"])
entrypoint := strings.Fields(rc.ExprEval.Interpolate(step.With["entrypoint"])) entrypoint := strings.Fields(step.With["entrypoint"])
stepContainer := sc.newStepContainer(ctx, image, cmd, entrypoint) stepContainer := sc.newStepContainer(ctx, image, cmd, entrypoint)
return common.NewPipelineExecutor( return common.NewPipelineExecutor(
@ -199,10 +189,7 @@ func (sc *StepContext) runUsesContainer() common.Executor {
} }
} }
func (sc *StepContext) setupAction() common.Executor { func (sc *StepContext) setupAction(actionDir string) common.Executor {
rc := sc.RunContext
step := sc.Step
actionDir := filepath.Join(rc.Config.Workdir, step.Uses)
return func(ctx context.Context) error { return func(ctx context.Context) error {
f, err := os.Open(filepath.Join(actionDir, "action.yml")) f, err := os.Open(filepath.Join(actionDir, "action.yml"))
if os.IsNotExist(err) { 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 rc := sc.RunContext
step := sc.Step step := sc.Step
return func(ctx context.Context) error { return func(ctx context.Context) error {
@ -236,7 +223,13 @@ func (sc *StepContext) runAction() common.Executor {
switch action.Runs.Using { switch action.Runs.Using {
case model.ActionRunsUsingNode12: 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: case model.ActionRunsUsingDocker:
var prepImage common.Executor var prepImage common.Executor
var image string var image string
@ -245,7 +238,7 @@ func (sc *StepContext) runAction() common.Executor {
} else { } else {
image = fmt.Sprintf("%s:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(step.Uses, "-"), "latest") image = fmt.Sprintf("%s:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(step.Uses, "-"), "latest")
image = strings.TrimLeft(image, "-") 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{ prepImage = container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
ContextDir: contextDir, ContextDir: contextDir,
ImageTag: image, ImageTag: image,