diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index e07302a9..dbb96f85 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 5e5aa60c..43ff4dbe 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 708df45f..a0e4dd5d 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/act/common/git.go b/act/common/git.go index d25b8bdc..1c12b9ea 100644 --- a/act/common/git.go +++ b/act/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/act/common/git_test.go b/act/common/git_test.go index d4b7698f..0edb4507 100644 --- a/act/common/git_test.go +++ b/act/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/act/model/action.go b/act/model/action.go index a994427a..8a6c0459 100644 --- a/act/model/action.go +++ b/act/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/act/runner/expression_test.go b/act/runner/expression_test.go index cf09450b..d0795f4d 100644 --- a/act/runner/expression_test.go +++ b/act/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/act/runner/res/trampoline.js b/act/runner/res/trampoline.js new file mode 100644 index 00000000..8f45c662 --- /dev/null +++ b/act/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/act/runner/run_context.go b/act/runner/run_context.go index 25ea234b..8ffda163 100644 --- a/act/runner/run_context.go +++ b/act/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/act/runner/run_context_test.go b/act/runner/run_context_test.go index 58269d14..440619ec 100644 --- a/act/runner/run_context_test.go +++ b/act/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/act/runner/runner.go b/act/runner/runner.go index 531e6c07..92203797 100644 --- a/act/runner/runner.go +++ b/act/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/act/runner/step_context.go b/act/runner/step_context.go index 4855cda2..7d4ffcff 100644 --- a/act/runner/step_context.go +++ b/act/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 { diff --git a/cmd/root.go b/cmd/root.go index 0468b827..6cf1c32c 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")