diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e07302a..dbb96f8 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -7,6 +7,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: actions/setup-go@v1 + with: + go-version: 1.16 - uses: golangci/golangci-lint-action@v2 env: CGO_ENABLED: 0 @@ -24,7 +27,7 @@ jobs: uses: docker/setup-qemu-action@v1 - uses: actions/setup-go@v1 with: - go-version: 1.14 + go-version: 1.16 - run: go test -cover -coverprofile=coverage.txt -covermode=atomic ./... env: CGO_ENABLED: 0 @@ -42,7 +45,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v1 with: - go-version: 1.14 + go-version: 1.16 - name: Install MacOS Docker uses: docker-practice/actions-setup-docker@master - run: go test -v -timeout 1h -cover ./... @@ -58,6 +61,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - uses: actions/setup-go@v1 + with: + go-version: 1.16 - name: GoReleaser uses: goreleaser/goreleaser-action@v2 with: @@ -90,6 +96,9 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 + - uses: actions/setup-go@v1 + with: + go-version: 1.16 - name: GoReleaser uses: goreleaser/goreleaser-action@v1 with: diff --git a/.github/workflows/test-expressions.yml b/.github/workflows/test-expressions.yml index 5e5aa60..43ff4db 100644 --- a/.github/workflows/test-expressions.yml +++ b/.github/workflows/test-expressions.yml @@ -1,5 +1,5 @@ -name: "Test how expressions are handled on Github" +name: "Test how expressions are handled on GitHub" on: push env: diff --git a/.github/workflows/test-if.yml b/.github/workflows/test-if.yml index 708df45..a0e4dd5 100644 --- a/.github/workflows/test-if.yml +++ b/.github/workflows/test-if.yml @@ -1,5 +1,5 @@ -name: "Test what expressions result in true and false on Github" +name: "Test what expressions result in true and false on GitHub" on: push env: diff --git a/Makefile b/Makefile index 06744a5..8b9d3a7 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PREFIX ?= /usr/local -VERSION ?= $(shell git describe --tags --dirty | cut -c 2-) +VERSION ?= $(shell git describe --tags --dirty --always | cut -c 2-) IS_SNAPSHOT = $(if $(findstring -, $(VERSION)),true,false) MAJOR_VERSION = $(word 1, $(subst ., ,$(VERSION))) MINOR_VERSION = $(word 2, $(subst ., ,$(VERSION))) diff --git a/README.md b/README.md index 8374e15..f4c8716 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ It will save that information to `~/.actrc`, please refer to [Configuration](#co -l, --list list workflows -P, --platform stringArray custom image to use per platform (e.g. -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04) --privileged use privileged mode - -p, --pull pull docker image(s) if already present + -p, --pull pull docker image(s) even if already present -q, --quiet disable logging of output from steps -r, --reuse reuse action containers to maintain state -s, --secret stringArray secret to make available to actions with optional value (e.g. -s mysecret=foo or -s mysecret) diff --git a/cmd/root.go b/cmd/root.go index 0468b82..6cf1c32 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -27,7 +27,7 @@ func Execute(ctx context.Context, version string) { input := new(Input) var rootCmd = &cobra.Command{ Use: "act [event name to run]\nIf no event name passed, will default to \"on: push\"", - Short: "Run Github actions locally by specifying the event name (e.g. `push`) or an action name directly.", + Short: "Run GitHub actions locally by specifying the event name (e.g. `push`) or an action name directly.", Args: cobra.MaximumNArgs(1), RunE: newRunCommand(ctx, input), PersistentPreRun: setupLogging, @@ -43,7 +43,7 @@ func Execute(ctx context.Context, version string) { rootCmd.Flags().StringArrayVarP(&input.platforms, "platform", "P", []string{}, "custom image to use per platform (e.g. -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04)") rootCmd.Flags().BoolVarP(&input.reuseContainers, "reuse", "r", false, "reuse action containers to maintain state") rootCmd.Flags().BoolVarP(&input.bindWorkdir, "bind", "b", false, "bind working directory to container, rather than copy") - rootCmd.Flags().BoolVarP(&input.forcePull, "pull", "p", false, "pull docker image(s) if already present") + rootCmd.Flags().BoolVarP(&input.forcePull, "pull", "p", false, "pull docker image(s) even if already present") rootCmd.Flags().BoolVarP(&input.autodetectEvent, "detect-event", "", false, "Use first event type from workflow as event that triggered the workflow") rootCmd.Flags().StringVarP(&input.eventPath, "eventpath", "e", "", "path to event JSON file") rootCmd.Flags().StringVar(&input.defaultBranch, "defaultbranch", "", "the name of the main branch") diff --git a/go.mod b/go.mod index a2ffe39..1594222 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/nektos/act -go 1.14 +go 1.16 require ( github.com/AlecAivazis/survey/v2 v2.2.7 diff --git a/pkg/common/git.go b/pkg/common/git.go index d25b8bd..1c12b9e 100644 --- a/pkg/common/git.go +++ b/pkg/common/git.go @@ -189,6 +189,7 @@ type NewGitCloneExecutorInput struct { Dir string } +// CloneIfRequired ... func CloneIfRequired(refName plumbing.ReferenceName, input NewGitCloneExecutorInput, logger log.FieldLogger) (*git.Repository, error) { r, err := git.PlainOpen(input.Dir) if err != nil { diff --git a/pkg/common/git_test.go b/pkg/common/git_test.go index d4b7698..0edb450 100644 --- a/pkg/common/git_test.go +++ b/pkg/common/git_test.go @@ -161,7 +161,7 @@ func TestGitFindRef(t *testing.T) { t.Run(name, func(t *testing.T) { dir := filepath.Join(basedir, name) require.NoError(t, os.MkdirAll(dir, 0755)) - require.NoError(t, gitCmd("-C", dir, "init")) + require.NoError(t, gitCmd("-C", dir, "init", "--initial-branch=master")) require.NoError(t, cleanGitHooks(dir)) tt.Prepare(t, dir) ref, err := FindGitRef(dir) diff --git a/pkg/model/action.go b/pkg/model/action.go index a994427..8a6c045 100644 --- a/pkg/model/action.go +++ b/pkg/model/action.go @@ -38,6 +38,16 @@ const ( ActionRunsUsingDocker = "docker" ) +// ActionRuns are a field in Action +type ActionRuns struct { + Using ActionRunsUsing `yaml:"using"` + Env map[string]string `yaml:"env"` + Main string `yaml:"main"` + Image string `yaml:"image"` + Entrypoint []string `yaml:"entrypoint"` + Args []string `yaml:"args"` +} + // Action describes a metadata file for GitHub actions. The metadata filename must be either action.yml or action.yaml. The data in the metadata file defines the inputs, outputs and main entrypoint for your action. type Action struct { Name string `yaml:"name"` @@ -45,15 +55,8 @@ type Action struct { Description string `yaml:"description"` Inputs map[string]Input `yaml:"inputs"` Outputs map[string]Output `yaml:"outputs"` - Runs struct { - Using ActionRunsUsing `yaml:"using"` - Env map[string]string `yaml:"env"` - Main string `yaml:"main"` - Image string `yaml:"image"` - Entrypoint []string `yaml:"entrypoint"` - Args []string `yaml:"args"` - } `yaml:"runs"` - Branding struct { + Runs ActionRuns `yaml:"runs"` + Branding struct { Color string `yaml:"color"` Icon string `yaml:"icon"` } `yaml:"branding"` diff --git a/pkg/runner/expression_test.go b/pkg/runner/expression_test.go index cf09450..d0795f4 100644 --- a/pkg/runner/expression_test.go +++ b/pkg/runner/expression_test.go @@ -211,7 +211,7 @@ func updateTestExpressionWorkflow(t *testing.T, tables []struct { } workflow := fmt.Sprintf(` -name: "Test how expressions are handled on Github" +name: "Test how expressions are handled on GitHub" on: push env: diff --git a/pkg/runner/res/trampoline.js b/pkg/runner/res/trampoline.js new file mode 100644 index 0000000..8f45c66 --- /dev/null +++ b/pkg/runner/res/trampoline.js @@ -0,0 +1,14 @@ +const { spawnSync } = require('child_process') +const spawnArguments={ + cwd: process.env['INPUT_CWD'], + stdio: [ + process.stdin, + process.stdout, + process.stderr, + ] +} +const child=spawnSync( + '/bin/sh', + [ '-c' ].concat(process.env['INPUT_COMMAND']), + spawnArguments) +process.exit(child.status) diff --git a/pkg/runner/run_context.go b/pkg/runner/run_context.go index 25ea234..8ffda16 100644 --- a/pkg/runner/run_context.go +++ b/pkg/runner/run_context.go @@ -316,7 +316,7 @@ func (rc *RunContext) EvalBool(expr string) (bool, error) { interpolatedPart, isString := rc.ExprEval.InterpolateWithStringCheck(part) - // This peculiar transformation has to be done because the Github parser + // This peculiar transformation has to be done because the GitHub parser // treats false returned from contexts as a string, not a boolean. // Hence env.SOMETHING will be evaluated to true in an if: expression // regardless if SOMETHING is set to false, true or any other string. diff --git a/pkg/runner/run_context_test.go b/pkg/runner/run_context_test.go index 58269d1..440619e 100644 --- a/pkg/runner/run_context_test.go +++ b/pkg/runner/run_context_test.go @@ -169,7 +169,7 @@ func updateTestIfWorkflow(t *testing.T, tables []struct { } workflow := fmt.Sprintf(` -name: "Test what expressions result in true and false on Github" +name: "Test what expressions result in true and false on GitHub" on: push env: diff --git a/pkg/runner/runner.go b/pkg/runner/runner.go index 531e6c0..9220379 100644 --- a/pkg/runner/runner.go +++ b/pkg/runner/runner.go @@ -24,7 +24,7 @@ type Config struct { EventPath string // path to JSON file to use for event.json in containers DefaultBranch string // name of the main branch for this repository ReuseContainers bool // reuse containers to maintain state - ForcePull bool // force pulling of the image, if already present + ForcePull bool // force pulling of the image, even if already present LogOutput bool // log the output from docker run Env map[string]string // env for containers Secrets map[string]string // list of secrets diff --git a/pkg/runner/step_context.go b/pkg/runner/step_context.go index 4855cda..7d4ffcf 100644 --- a/pkg/runner/step_context.go +++ b/pkg/runner/step_context.go @@ -2,7 +2,10 @@ package runner import ( "context" + // Go told me to? + _ "embed" "fmt" + "io/ioutil" "os" "path" "path/filepath" @@ -296,12 +299,55 @@ func (sc *StepContext) runUsesContainer() common.Executor { } } +//go:embed res/trampoline.js +var trampoline []byte + func (sc *StepContext) setupAction(actionDir string, actionPath string) common.Executor { return func(ctx context.Context) error { f, err := os.Open(filepath.Join(actionDir, actionPath, "action.yml")) if os.IsNotExist(err) { f, err = os.Open(filepath.Join(actionDir, actionPath, "action.yaml")) if err != nil { + if _, err2 := os.Stat(filepath.Join(actionDir, actionPath, "Dockerfile")); err2 == nil { + sc.Action = &model.Action{ + Name: "(Synthetic)", + Runs: model.ActionRuns{ + Using: "docker", + Image: "Dockerfile", + }, + } + log.Debugf("Using synthetic action %v for Dockerfile", sc.Action) + return nil + } + if sc.Step.With != nil { + if val, ok := sc.Step.With["args"]; ok { + err2 := ioutil.WriteFile(filepath.Join(actionDir, actionPath, "trampoline.js"), trampoline, 0400) + if err2 != nil { + return err + } + sc.Action = &model.Action{ + Name: "(Synthetic)", + Inputs: map[string]model.Input{ + "cwd": { + Description: "(Actual working directory)", + Required: false, + Default: filepath.Join(actionDir, actionPath), + }, + "command": { + Description: "(Actual program)", + Required: false, + Default: val, + }, + }, + Runs: model.ActionRuns{ + Using: "node12", + Main: "trampoline.js", + }, + } + log.Debugf("Using synthetic action %v", sc.Action) + return nil + } + } return err } } else if err != nil {