act/actions/runner_exec.go
2019-01-18 13:28:53 -08:00

186 lines
5.2 KiB
Go

package actions
import (
"archive/tar"
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"github.com/nektos/act/common"
"github.com/nektos/act/container"
log "github.com/sirupsen/logrus"
)
func (runner *runnerImpl) newActionExecutor(actionName string) common.Executor {
action, err := runner.workflows.getAction(actionName)
if err != nil {
return common.NewErrorExecutor(err)
}
env := make(map[string]string)
for _, applier := range []environmentApplier{action, runner} {
applier.applyEnvironment(env)
}
env["GITHUB_ACTION"] = actionName
logger := newActionLogger(actionName, runner.config.Dryrun)
log.Debugf("Using '%s' for action '%s'", action.Uses, actionName)
in := container.DockerExecutorInput{
Ctx: runner.config.Ctx,
Logger: logger,
Dryrun: runner.config.Dryrun,
}
var image string
executors := make([]common.Executor, 0)
if imageRef, ok := parseImageReference(action.Uses); ok {
executors = append(executors, container.NewDockerPullExecutor(container.NewDockerPullExecutorInput{
DockerExecutorInput: in,
Image: imageRef,
}))
image = imageRef
} else if contextDir, imageTag, ok := parseImageLocal(runner.config.WorkingDir, action.Uses); ok {
executors = append(executors, container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
DockerExecutorInput: in,
ContextDir: contextDir,
ImageTag: imageTag,
}))
image = imageTag
} else if cloneURL, ref, path, ok := parseImageGithub(action.Uses); ok {
cloneDir := filepath.Join(os.TempDir(), "act", action.Uses)
executors = append(executors, common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{
URL: cloneURL,
Ref: ref,
Dir: cloneDir,
Logger: logger,
Dryrun: runner.config.Dryrun,
}))
contextDir := filepath.Join(cloneDir, path)
imageTag := fmt.Sprintf("%s:%s", filepath.Base(cloneURL.Path), ref)
executors = append(executors, container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
DockerExecutorInput: in,
ContextDir: contextDir,
ImageTag: imageTag,
}))
image = imageTag
} else {
return common.NewErrorExecutor(fmt.Errorf("unable to determine executor type for image '%s'", action.Uses))
}
ghReader, err := runner.createGithubTarball()
if err != nil {
return common.NewErrorExecutor(err)
}
envList := make([]string, 0)
for k, v := range env {
envList = append(envList, fmt.Sprintf("%s=%s", k, v))
}
executors = append(executors, container.NewDockerRunExecutor(container.NewDockerRunExecutorInput{
DockerExecutorInput: in,
Cmd: action.Args,
Entrypoint: action.Runs,
Image: image,
WorkingDir: "/github/workspace",
Env: envList,
Name: runner.createContainerName(actionName),
Binds: []string{
fmt.Sprintf("%s:%s", runner.config.WorkingDir, "/github/workspace"),
fmt.Sprintf("%s:%s", runner.tempDir, "/github/home"),
fmt.Sprintf("%s:%s", "/var/run/docker.sock", "/var/run/docker.sock"),
},
Content: map[string]io.Reader{"/github": ghReader},
ReuseContainers: runner.config.ReuseContainers,
}))
return common.NewPipelineExecutor(executors...)
}
func (runner *runnerImpl) applyEnvironment(env map[string]string) {
repoPath := runner.config.WorkingDir
_, workflowName, _ := runner.workflows.getWorkflow(runner.config.EventName)
env["HOME"] = "/github/home"
env["GITHUB_ACTOR"] = "nektos/act"
env["GITHUB_EVENT_PATH"] = "/github/workflow/event.json"
env["GITHUB_WORKSPACE"] = "/github/workspace"
env["GITHUB_WORKFLOW"] = workflowName
env["GITHUB_EVENT_NAME"] = runner.config.EventName
_, rev, err := common.FindGitRevision(repoPath)
if err != nil {
log.Warningf("unable to get git revision: %v", err)
} else {
env["GITHUB_SHA"] = rev
}
repo, err := common.FindGithubRepo(repoPath)
if err != nil {
log.Warningf("unable to get git repo: %v", err)
} else {
env["GITHUB_REPOSITORY"] = repo
}
ref, err := common.FindGitRef(repoPath)
if err != nil {
log.Warningf("unable to get git ref: %v", err)
} else {
env["GITHUB_REF"] = ref
}
}
func (runner *runnerImpl) createGithubTarball() (io.Reader, error) {
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
var files = []struct {
Name string
Mode int64
Body string
}{
{"workflow/event.json", 0644, runner.eventJSON},
}
for _, file := range files {
log.Debugf("Writing entry to tarball %s len:%d", file.Name, len(runner.eventJSON))
hdr := &tar.Header{
Name: file.Name,
Mode: file.Mode,
Size: int64(len(runner.eventJSON)),
}
if err := tw.WriteHeader(hdr); err != nil {
return nil, err
}
if _, err := tw.Write([]byte(runner.eventJSON)); err != nil {
return nil, err
}
}
if err := tw.Close(); err != nil {
return nil, err
}
return &buf, nil
}
func (runner *runnerImpl) createContainerName(actionName string) string {
containerName := regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-")
prefix := fmt.Sprintf("%s-", trimToLen(filepath.Base(runner.config.WorkingDir), 10))
suffix := ""
containerName = trimToLen(containerName, 30-(len(prefix)+len(suffix)))
return fmt.Sprintf("%s%s%s", prefix, containerName, suffix)
}
func trimToLen(s string, l int) string {
if len(s) > l {
return s[:l]
}
return s
}