diff --git a/act/runner/run_context.go b/act/runner/run_context.go index 131888e0..c45e199f 100644 --- a/act/runner/run_context.go +++ b/act/runner/run_context.go @@ -12,11 +12,13 @@ import ( "fmt" "io" "os" + "path" "path/filepath" "regexp" "runtime" "strings" "text/template" + "time" "github.com/docker/docker/api/types/network" "github.com/docker/go-connections/nat" @@ -585,6 +587,42 @@ func (rc *RunContext) startJobContainer() common.Executor { } } +func (rc *RunContext) sh(ctx context.Context, script string) (stdout, stderr string, err error) { + timeed, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + hout := &bytes.Buffer{} + herr := &bytes.Buffer{} + + env := map[string]string{} + for k, v := range rc.Env { + env[k] = v + } + + base, err := common.RandName(8) + if err != nil { + return "", "", err + } + name := base + ".sh" + oldStdout, oldStderr := rc.JobContainer.ReplaceLogWriter(hout, herr) + err = rc.JobContainer.Copy(rc.JobContainer.GetActPath(), &container.FileEntry{ + Name: name, + Mode: 0o644, + Body: script, + }). + Then(rc.execJobContainer([]string{"sh", path.Join(rc.JobContainer.GetActPath(), name)}, + env, "", "")). + Finally(func(context.Context) error { + rc.JobContainer.ReplaceLogWriter(oldStdout, oldStderr) + return nil + })(timeed) + if err != nil { + return "", "", err + } + stdout = hout.String() + stderr = herr.String() + return stdout, stderr, nil +} + func (rc *RunContext) execJobContainer(cmd []string, env map[string]string, user, workdir string) common.Executor { return func(ctx context.Context) error { return rc.JobContainer.Exec(cmd, env, user, workdir)(ctx) diff --git a/act/runner/runner_test.go b/act/runner/runner_test.go index 74ff8418..b59afbed 100644 --- a/act/runner/runner_test.go +++ b/act/runner/runner_test.go @@ -227,12 +227,12 @@ func TestRunEvent(t *testing.T) { tables := []TestJobFileInfo{ // Shells {workdir, "shells/defaults", "push", "", platforms, secrets}, - // TODO: figure out why it fails - // {workdir, "shells/custom", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, }, // custom image with pwsh - {workdir, "shells/pwsh", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, secrets}, // custom image with pwsh + {workdir, "shells/custom", "push", "", platforms, secrets}, {workdir, "shells/bash", "push", "", platforms, secrets}, - {workdir, "shells/python", "push", "", map[string]string{"ubuntu-latest": "node:16-buster"}, secrets}, // slim doesn't have python + {workdir, "shells/node", "push", "", platforms, secrets}, + {workdir, "shells/python", "push", "", platforms, secrets}, {workdir, "shells/sh", "push", "", platforms, secrets}, + {workdir, "shells/pwsh", "push", "", platforms, secrets}, // Local action {workdir, "local-action-docker-url", "push", "", platforms, secrets}, diff --git a/act/runner/step_run.go b/act/runner/step_run.go index 376cc090..e4d32e59 100644 --- a/act/runner/step_run.go +++ b/act/runner/step_run.go @@ -194,9 +194,20 @@ func (sr *stepRun) setupShell(ctx context.Context) { if err != nil { step.Shell = shellWithFallback[1] } - } else if containerImage := rc.containerImage(ctx); containerImage != "" { - // Currently only linux containers are supported, use sh by default like actions/runner - step.Shell = "sh" + } else { + shell_fallback := ` +if command -v bash >/dev/null; then + echo -n bash +else + echo -n sh +fi +` + stdout, _, err := rc.sh(ctx, shell_fallback) + if err != nil { + common.Logger(ctx).Error("fail to run %q: %v", shell_fallback, err) + return + } + step.Shell = stdout } } } diff --git a/act/runner/testdata/shells/bash/push.yml b/act/runner/testdata/shells/bash/push.yml index 49704b2b..a8d8ab37 100644 --- a/act/runner/testdata/shells/bash/push.yml +++ b/act/runner/testdata/shells/bash/push.yml @@ -14,7 +14,8 @@ jobs: fi check-container: runs-on: ubuntu-latest - container: node:16-buster-slim + container: + image: code.forgejo.org/oci/node:22-bookworm steps: - shell: ${{ env.MY_SHELL }} run: | diff --git a/act/runner/testdata/shells/custom/push.yml b/act/runner/testdata/shells/custom/push.yml index cbc03955..3b491223 100644 --- a/act/runner/testdata/shells/custom/push.yml +++ b/act/runner/testdata/shells/custom/push.yml @@ -3,12 +3,6 @@ jobs: check: runs-on: ubuntu-latest steps: - # prints version and exits, it's not valid (for github) if {0} is not included - - shell: pwsh -v '. {0}' - run: '' - check-container: - runs-on: ubuntu-latest - container: catthehacker/ubuntu:pwsh-latest - steps: - - shell: pwsh -v '. {0}' - run: '' + - shell: cat {0} + run: | + exit 1 diff --git a/act/runner/testdata/shells/defaults/push.yml b/act/runner/testdata/shells/defaults/push.yml index 0b0204a1..6255922b 100644 --- a/act/runner/testdata/shells/defaults/push.yml +++ b/act/runner/testdata/shells/defaults/push.yml @@ -1,6 +1,6 @@ on: push jobs: - check: # GHA uses `bash` as default for runners + check: # use `bash` as default for runners runs-on: ubuntu-latest steps: - run: | @@ -9,12 +9,13 @@ jobs: else exit 1 fi - check-container: # GHA uses `sh` as default for containers + fallback: # uses `sh` as fallback default if `bash` is not available runs-on: ubuntu-latest - container: alpine:latest + container: + image: code.forgejo.org/oci/alpine:latest steps: - run: | - if [ -z ${BASH+x} ]; then + if [ -z "${BASH}" ]; then echo "I'm sh!" else exit 1 diff --git a/act/runner/testdata/shells/node/push.yml b/act/runner/testdata/shells/node/push.yml index dae1fe9f..0bd368d9 100644 --- a/act/runner/testdata/shells/node/push.yml +++ b/act/runner/testdata/shells/node/push.yml @@ -8,13 +8,6 @@ jobs: - shell: ${{ env.MY_SHELL }} run: | console.log(process.version) - check-container: - runs-on: ubuntu-latest - container: node:16-buster-slim - steps: - - shell: ${{ env.MY_SHELL }} - run: | - console.log(process.version) check-job-default: runs-on: ubuntu-latest defaults: diff --git a/act/runner/testdata/shells/pwsh/push.yml b/act/runner/testdata/shells/pwsh/push.yml index 25ce66b4..8f70d463 100644 --- a/act/runner/testdata/shells/pwsh/push.yml +++ b/act/runner/testdata/shells/pwsh/push.yml @@ -4,19 +4,16 @@ env: jobs: check: runs-on: ubuntu-latest - steps: - - shell: ${{ env.MY_SHELL }} - run: | - $PSVersionTable - check-container: - runs-on: ubuntu-latest - container: catthehacker/ubuntu:pwsh-latest + container: + image: code.forgejo.org/oci/ci:1 steps: - shell: ${{ env.MY_SHELL }} run: | $PSVersionTable check-job-default: runs-on: ubuntu-latest + container: + image: code.forgejo.org/oci/ci:1 defaults: run: shell: ${{ env.MY_SHELL }} diff --git a/act/runner/testdata/shells/python/push.yml b/act/runner/testdata/shells/python/push.yml index 6480e6f0..3e8c160a 100644 --- a/act/runner/testdata/shells/python/push.yml +++ b/act/runner/testdata/shells/python/push.yml @@ -4,14 +4,8 @@ env: jobs: check: runs-on: ubuntu-latest - steps: - - shell: ${{ env.MY_SHELL }} - run: | - import platform - print(platform.python_version()) - check-container: - runs-on: ubuntu-latest - container: node:16-buster + container: + image: code.forgejo.org/oci/python:slim steps: - shell: ${{ env.MY_SHELL }} run: | @@ -19,6 +13,8 @@ jobs: print(platform.python_version()) check-job-default: runs-on: ubuntu-latest + container: + image: code.forgejo.org/oci/python:slim defaults: run: shell: ${{ env.MY_SHELL }} diff --git a/act/runner/testdata/shells/sh/push.yml b/act/runner/testdata/shells/sh/push.yml index 0914ca2f..63f902c4 100644 --- a/act/runner/testdata/shells/sh/push.yml +++ b/act/runner/testdata/shells/sh/push.yml @@ -7,18 +7,7 @@ jobs: steps: - shell: ${{ env.MY_SHELL }} run: | - if [ -z ${BASH+x} ]; then - echo "I'm sh!" - else - exit 1 - fi - check-container: - runs-on: ubuntu-latest - container: alpine:latest - steps: - - shell: ${{ env.MY_SHELL }} - run: | - if [ -z ${BASH+x} ]; then + if [ -z "${BASH}" ]; then echo "I'm sh!" else exit 1 @@ -30,7 +19,7 @@ jobs: shell: ${{ env.MY_SHELL }} steps: - run: | - if [ -z ${BASH+x} ]; then + if [ -z "${BASH}" ]; then echo "I'm sh!" else exit 1