unit tests

Signed-off-by: Casey Lee <cplee@nektos.com>
This commit is contained in:
Casey Lee
2020-02-10 15:27:05 -08:00
parent be75ee20b1
commit 0582306861
466 changed files with 94683 additions and 62526 deletions

View File

@@ -1,5 +0,0 @@
package runner
type environmentApplier interface {
applyEnvironment(map[string]string)
}

View File

@@ -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 := ""

View File

@@ -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 {

View File

@@ -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
}

View File

@@ -0,0 +1 @@
FROM ubuntu:18.04

View File

@@ -0,0 +1,4 @@
name: 'action1'
runs:
using: 'docker'
image: 'Dockerfile'

View 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"]

View 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 }}

View File

@@ -0,0 +1,5 @@
#!/bin/sh -l
echo "Hello $1"
time=$(date)
echo ::set-output name=time::$time

View 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
View 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
View 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

View 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

View File

@@ -0,0 +1,8 @@
name: local-action-docker-url
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: ./actions/docker-url

View 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'

View 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'

View 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
View File

@@ -0,0 +1,8 @@
name: runs-on
on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo ${GITHUB_ACTOR} | grep nektos/act

View 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