@@ -1,5 +0,0 @@
|
||||
package runner
|
||||
|
||||
type environmentApplier interface {
|
||||
applyEnvironment(map[string]string)
|
||||
}
|
@@ -45,10 +45,13 @@ func (rc *RunContext) Close(ctx context.Context) error {
|
||||
|
||||
// Executor returns a pipeline executor for all the steps in the job
|
||||
func (rc *RunContext) Executor() common.Executor {
|
||||
rc.setupTempDir()
|
||||
steps := make([]common.Executor, 0)
|
||||
steps = append(steps, rc.setupTempDir())
|
||||
|
||||
for _, step := range rc.Run.Job().Steps {
|
||||
for i, step := range rc.Run.Job().Steps {
|
||||
if step.ID == "" {
|
||||
step.ID = fmt.Sprintf("%d", i)
|
||||
}
|
||||
steps = append(steps, rc.newStepExecutor(step))
|
||||
}
|
||||
return common.NewPipelineExecutor(steps...).Finally(rc.Close)
|
||||
@@ -64,17 +67,16 @@ func mergeMaps(maps ...map[string]string) map[string]string {
|
||||
return rtnMap
|
||||
}
|
||||
|
||||
func (rc *RunContext) setupTempDir() common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
var err error
|
||||
tempBase := ""
|
||||
if runtime.GOOS == "darwin" {
|
||||
tempBase = "/tmp"
|
||||
}
|
||||
rc.Tempdir, err = ioutil.TempDir(tempBase, "act-")
|
||||
log.Debugf("Setup tempdir %s", rc.Tempdir)
|
||||
return err
|
||||
func (rc *RunContext) setupTempDir() error {
|
||||
var err error
|
||||
tempBase := ""
|
||||
if runtime.GOOS == "darwin" {
|
||||
tempBase = "/tmp"
|
||||
}
|
||||
rc.Tempdir, err = ioutil.TempDir(tempBase, "act-")
|
||||
os.Chmod(rc.Tempdir, 0755)
|
||||
log.Debugf("Setup tempdir %s", rc.Tempdir)
|
||||
return err
|
||||
}
|
||||
|
||||
func (rc *RunContext) pullImage(containerSpec *model.ContainerSpec) common.Executor {
|
||||
@@ -111,7 +113,7 @@ func (rc *RunContext) runContainer(containerSpec *model.ContainerSpec) common.Ex
|
||||
Image: containerSpec.Image,
|
||||
WorkingDir: "/github/workspace",
|
||||
Env: envList,
|
||||
Name: rc.createContainerName(),
|
||||
Name: containerSpec.Name,
|
||||
Binds: []string{
|
||||
fmt.Sprintf("%s:%s", rc.Config.Workdir, "/github/workspace"),
|
||||
fmt.Sprintf("%s:%s", rc.Tempdir, "/github/home"),
|
||||
@@ -155,8 +157,9 @@ func (rc *RunContext) createGithubTarball() (io.Reader, error) {
|
||||
|
||||
}
|
||||
|
||||
func (rc *RunContext) createContainerName() string {
|
||||
containerName := regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(rc.Run.String(), "-")
|
||||
func (rc *RunContext) createContainerName(stepID string) string {
|
||||
containerName := fmt.Sprintf("%s-%s", stepID, rc.Tempdir)
|
||||
containerName = regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(containerName, "-")
|
||||
|
||||
prefix := fmt.Sprintf("%s-", trimToLen(filepath.Base(rc.Config.Workdir), 10))
|
||||
suffix := ""
|
||||
|
@@ -2,28 +2,29 @@ package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/nektos/act/pkg/model"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gotest.tools/assert"
|
||||
)
|
||||
|
||||
func TestGraphEvent(t *testing.T) {
|
||||
runnerConfig := &Config{
|
||||
WorkflowPath: "multi.workflow",
|
||||
WorkingDir: "testdata",
|
||||
EventName: "push",
|
||||
}
|
||||
runner, err := NewRunner(runnerConfig)
|
||||
planner, err := model.NewWorkflowPlanner("testdata/basic")
|
||||
assert.NilError(t, err)
|
||||
|
||||
graph, err := runner.GraphEvent("push")
|
||||
plan := planner.PlanEvent("push")
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, graph, [][]string{{"build"}})
|
||||
assert.Equal(t, len(plan.Stages), 2, "stages")
|
||||
assert.Equal(t, len(plan.Stages[0].Runs), 1, "stage0.runs")
|
||||
assert.Equal(t, len(plan.Stages[1].Runs), 1, "stage1.runs")
|
||||
assert.Equal(t, plan.Stages[0].Runs[0].JobID, "build", "jobid")
|
||||
assert.Equal(t, plan.Stages[1].Runs[0].JobID, "test", "jobid")
|
||||
|
||||
graph, err = runner.GraphEvent("release")
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, graph, [][]string{{"deploy"}})
|
||||
plan = planner.PlanEvent("release")
|
||||
assert.Equal(t, len(plan.Stages), 0, "stages")
|
||||
}
|
||||
|
||||
func TestRunEvent(t *testing.T) {
|
||||
@@ -36,30 +37,36 @@ func TestRunEvent(t *testing.T) {
|
||||
eventName string
|
||||
errorMessage string
|
||||
}{
|
||||
{"basic.workflow", "push", ""},
|
||||
{"pipe.workflow", "push", ""},
|
||||
{"fail.workflow", "push", "exit with `FAILURE`: 1"},
|
||||
{"buildfail.workflow", "push", "COPY failed"},
|
||||
{"regex.workflow", "push", "exit with `NEUTRAL`: 78"},
|
||||
{"gitref.workflow", "push", ""},
|
||||
{"env.workflow", "push", ""},
|
||||
{"detect_event.workflow", "", ""},
|
||||
{"basic", "push", ""},
|
||||
{"fail", "push", "exit with `FAILURE`: 1"},
|
||||
{"runs-on", "push", ""},
|
||||
{"job-container", "push", ""},
|
||||
{"uses-docker-url", "push", ""},
|
||||
{"remote-action-docker", "push", ""},
|
||||
{"remote-action-js", "push", ""},
|
||||
{"local-action-docker-url", "push", ""},
|
||||
{"local-action-dockerfile", "push", ""},
|
||||
}
|
||||
log.SetLevel(log.DebugLevel)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
for _, table := range tables {
|
||||
table := table
|
||||
t.Run(table.workflowPath, func(t *testing.T) {
|
||||
runnerConfig := &RunnerConfig{
|
||||
Ctx: context.Background(),
|
||||
WorkflowPath: table.workflowPath,
|
||||
WorkingDir: "testdata",
|
||||
EventName: table.eventName,
|
||||
runnerConfig := &Config{
|
||||
Workdir: "testdata",
|
||||
EventName: table.eventName,
|
||||
}
|
||||
runner, err := NewRunner(runnerConfig)
|
||||
runner, err := New(runnerConfig)
|
||||
assert.NilError(t, err, table.workflowPath)
|
||||
|
||||
err = runner.RunEvent()
|
||||
planner, err := model.NewWorkflowPlanner(fmt.Sprintf("testdata/%s", table.workflowPath))
|
||||
assert.NilError(t, err, table.workflowPath)
|
||||
|
||||
plan := planner.PlanEvent(table.eventName)
|
||||
|
||||
err = runner.NewPlanExecutor(plan)(ctx)
|
||||
if table.errorMessage == "" {
|
||||
assert.NilError(t, err, table.workflowPath)
|
||||
} else {
|
||||
|
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/nektos/act/pkg/common"
|
||||
"github.com/nektos/act/pkg/container"
|
||||
"github.com/nektos/act/pkg/model"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
@@ -18,6 +19,7 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
|
||||
job := rc.Run.Job()
|
||||
containerSpec := new(model.ContainerSpec)
|
||||
containerSpec.Env = rc.StepEnv(step)
|
||||
containerSpec.Name = rc.createContainerName(step.ID)
|
||||
|
||||
switch step.Type() {
|
||||
case model.StepTypeRun:
|
||||
@@ -45,15 +47,26 @@ func (rc *RunContext) newStepExecutor(step *model.Step) common.Executor {
|
||||
)
|
||||
|
||||
case model.StepTypeUsesActionLocal:
|
||||
containerSpec.Image = fmt.Sprintf("%s:%s", containerSpec.Name, "latest")
|
||||
return common.NewPipelineExecutor(
|
||||
rc.setupAction(containerSpec, filepath.Join(rc.Config.Workdir, step.Uses)),
|
||||
rc.pullImage(containerSpec),
|
||||
rc.runContainer(containerSpec),
|
||||
)
|
||||
case model.StepTypeUsesActionRemote:
|
||||
remoteAction := newRemoteAction(step.Uses)
|
||||
cloneDir, err := ioutil.TempDir(rc.Tempdir, remoteAction.Repo)
|
||||
if err != nil {
|
||||
return common.NewErrorExecutor(err)
|
||||
}
|
||||
containerSpec.Image = fmt.Sprintf("%s:%s", remoteAction.Repo, remoteAction.Ref)
|
||||
return common.NewPipelineExecutor(
|
||||
rc.cloneAction(step.Uses),
|
||||
rc.setupAction(containerSpec, step.Uses),
|
||||
common.NewGitCloneExecutor(common.NewGitCloneExecutorInput{
|
||||
URL: remoteAction.CloneURL(),
|
||||
Ref: remoteAction.Ref,
|
||||
Dir: cloneDir,
|
||||
}),
|
||||
rc.setupAction(containerSpec, filepath.Join(cloneDir, remoteAction.Path)),
|
||||
rc.pullImage(containerSpec),
|
||||
rc.runContainer(containerSpec),
|
||||
)
|
||||
@@ -174,8 +187,8 @@ func (rc *RunContext) setupAction(containerSpec *model.ContainerSpec, actionDir
|
||||
}
|
||||
|
||||
for inputID, input := range action.Inputs {
|
||||
envKey := fmt.Sprintf("INPUT_%s", strings.ToUpper(inputID))
|
||||
envKey = regexp.MustCompile("[^A-Z0-9]").ReplaceAllString(envKey, "_")
|
||||
envKey := regexp.MustCompile("[^A-Z0-9-]").ReplaceAllString(strings.ToUpper(inputID), "_")
|
||||
envKey = fmt.Sprintf("INPUT_%s", envKey)
|
||||
if _, ok := containerSpec.Env[envKey]; !ok {
|
||||
containerSpec.Env[envKey] = input.Default
|
||||
}
|
||||
@@ -183,23 +196,54 @@ func (rc *RunContext) setupAction(containerSpec *model.ContainerSpec, actionDir
|
||||
|
||||
switch action.Runs.Using {
|
||||
case model.ActionRunsUsingNode12:
|
||||
containerSpec.Image = "node:12"
|
||||
containerSpec.Args = action.Runs.Main
|
||||
containerSpec.Image = "node:12-alpine"
|
||||
if strings.HasPrefix(actionDir, rc.Config.Workdir) {
|
||||
containerSpec.Args = fmt.Sprintf("node /github/workspace/%s/%s", strings.TrimPrefix(actionDir, rc.Config.Workdir), action.Runs.Main)
|
||||
} else if strings.HasPrefix(actionDir, rc.Tempdir) {
|
||||
containerSpec.Args = fmt.Sprintf("node /github/home/%s/%s", strings.TrimPrefix(actionDir, rc.Tempdir), action.Runs.Main)
|
||||
}
|
||||
case model.ActionRunsUsingDocker:
|
||||
if strings.HasPrefix(action.Runs.Image, "docker://") {
|
||||
containerSpec.Image = strings.TrimPrefix(action.Runs.Image, "docker://")
|
||||
containerSpec.Entrypoint = strings.Join(action.Runs.Entrypoint, " ")
|
||||
containerSpec.Args = strings.Join(action.Runs.Args, " ")
|
||||
} else {
|
||||
// TODO: docker build
|
||||
contextDir := filepath.Join(actionDir, action.Runs.Main)
|
||||
return container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
|
||||
ContextDir: contextDir,
|
||||
ImageTag: containerSpec.Image,
|
||||
})(ctx)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (rc *RunContext) cloneAction(action string) common.Executor {
|
||||
return func(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
type remoteAction struct {
|
||||
Org string
|
||||
Repo string
|
||||
Path string
|
||||
Ref string
|
||||
}
|
||||
|
||||
func (ra *remoteAction) CloneURL() string {
|
||||
return fmt.Sprintf("https://github.com/%s/%s", ra.Org, ra.Repo)
|
||||
}
|
||||
|
||||
func newRemoteAction(action string) *remoteAction {
|
||||
r := regexp.MustCompile(`^([^/@]+)/([^/@]+)(/([^@]*))?(@(.*))?$`)
|
||||
matches := r.FindStringSubmatch(action)
|
||||
|
||||
ra := new(remoteAction)
|
||||
ra.Org = matches[1]
|
||||
ra.Repo = matches[2]
|
||||
ra.Path = ""
|
||||
ra.Ref = "master"
|
||||
if len(matches) >= 5 {
|
||||
ra.Path = matches[4]
|
||||
}
|
||||
if len(matches) >= 7 {
|
||||
ra.Ref = matches[6]
|
||||
}
|
||||
return ra
|
||||
}
|
||||
|
1
pkg/runner/testdata/actions/action1/Dockerfile
vendored
Normal file
1
pkg/runner/testdata/actions/action1/Dockerfile
vendored
Normal file
@@ -0,0 +1 @@
|
||||
FROM ubuntu:18.04
|
4
pkg/runner/testdata/actions/action1/action.yml
vendored
Normal file
4
pkg/runner/testdata/actions/action1/action.yml
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
name: 'action1'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
8
pkg/runner/testdata/actions/docker-local/Dockerfile
vendored
Normal file
8
pkg/runner/testdata/actions/docker-local/Dockerfile
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Container image that runs your code
|
||||
FROM alpine:3.10
|
||||
|
||||
# Copies your code file from your action repository to the filesystem path `/` of the container
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
|
||||
# Code file to execute when the docker container starts up (`entrypoint.sh`)
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
15
pkg/runner/testdata/actions/docker-local/action.yml
vendored
Normal file
15
pkg/runner/testdata/actions/docker-local/action.yml
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
name: 'Hello World'
|
||||
description: 'Greet someone and record the time'
|
||||
inputs:
|
||||
who-to-greet: # id of input
|
||||
description: 'Who to greet'
|
||||
required: true
|
||||
default: 'World'
|
||||
outputs:
|
||||
time: # id of output
|
||||
description: 'The time we greeted you'
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
args:
|
||||
- ${{ inputs.who-to-greet }}
|
5
pkg/runner/testdata/actions/docker-local/entrypoint.sh
vendored
Executable file
5
pkg/runner/testdata/actions/docker-local/entrypoint.sh
vendored
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh -l
|
||||
|
||||
echo "Hello $1"
|
||||
time=$(date)
|
||||
echo ::set-output name=time::$time
|
16
pkg/runner/testdata/actions/docker-url/action.yml
vendored
Normal file
16
pkg/runner/testdata/actions/docker-url/action.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: docker-url
|
||||
author: nektos
|
||||
description: testing
|
||||
inputs:
|
||||
who-to-greet:
|
||||
description: who to greet
|
||||
required: true
|
||||
default: World
|
||||
runs:
|
||||
using: docker
|
||||
#image: docker://alpine:3.8
|
||||
image: docker://node:12-alpine
|
||||
env:
|
||||
TEST: enabled
|
||||
args:
|
||||
- env
|
17
pkg/runner/testdata/basic/push.yml
vendored
Normal file
17
pkg/runner/testdata/basic/push.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
name: basic
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: ./actions/action1
|
||||
with:
|
||||
args: echo 'build'
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- uses: docker://ubuntu:18.04
|
||||
with:
|
||||
args: echo ${GITHUB_REF} | grep nektos/act
|
12
pkg/runner/testdata/fail/push.yml
vendored
Normal file
12
pkg/runner/testdata/fail/push.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
name: fail
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:10.16-jessie
|
||||
env:
|
||||
TEST_ENV: test-value
|
||||
steps:
|
||||
- run: echo ${TEST_ENV} | grep bazooka
|
12
pkg/runner/testdata/job-container/push.yml
vendored
Normal file
12
pkg/runner/testdata/job-container/push.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
name: job-container
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: node:10.16-jessie
|
||||
env:
|
||||
TEST_ENV: test-value
|
||||
steps:
|
||||
- run: echo ${TEST_ENV} | grep test-value
|
8
pkg/runner/testdata/local-action-docker-url/push.yml
vendored
Normal file
8
pkg/runner/testdata/local-action-docker-url/push.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
name: local-action-docker-url
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: ./actions/docker-url
|
10
pkg/runner/testdata/local-action-dockerfile/push.yml
vendored
Normal file
10
pkg/runner/testdata/local-action-dockerfile/push.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
name: local-action-dockerfile
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: ./actions/docker-local
|
||||
with:
|
||||
who-to-greet: 'Mona the Octocat'
|
10
pkg/runner/testdata/remote-action-docker/push.yml
vendored
Normal file
10
pkg/runner/testdata/remote-action-docker/push.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
name: remote-action-docker
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/hello-world-docker-action@master
|
||||
with:
|
||||
who-to-greet: 'Mona the Octocat'
|
10
pkg/runner/testdata/remote-action-js/push.yml
vendored
Normal file
10
pkg/runner/testdata/remote-action-js/push.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
name: remote-action-js
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/hello-world-javascript-action@master
|
||||
with:
|
||||
who-to-greet: 'Mona the Octocat'
|
8
pkg/runner/testdata/runs-on/push.yml
vendored
Normal file
8
pkg/runner/testdata/runs-on/push.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
name: runs-on
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- run: echo ${GITHUB_ACTOR} | grep nektos/act
|
11
pkg/runner/testdata/uses-docker-url/push.yml
vendored
Normal file
11
pkg/runner/testdata/uses-docker-url/push.yml
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
name: uses-docker-url
|
||||
on: push
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: docker://alpine:3.8
|
||||
with:
|
||||
somekey: somevalue
|
||||
args: echo ${INPUT_SOMEKEY} | grep somevalue
|
Reference in New Issue
Block a user