diff --git a/pkg/container/docker_run.go b/pkg/container/docker_run.go index f6e2743f..8e30a808 100644 --- a/pkg/container/docker_run.go +++ b/pkg/container/docker_run.go @@ -188,7 +188,7 @@ func (cr *containerReference) GetContainerArchive(ctx context.Context, srcPath s } func (cr *containerReference) UpdateFromEnv(srcPath string, env *map[string]string) common.Executor { - return cr.extractEnv(srcPath, env).IfNot(common.Dryrun) + return parseEnvFile(cr, srcPath, env).IfNot(common.Dryrun) } func (cr *containerReference) UpdateFromImageEnv(env *map[string]string) common.Executor { @@ -503,59 +503,6 @@ func (cr *containerReference) create(capAdd []string, capDrop []string) common.E } } -var singleLineEnvPattern, multiLineEnvPattern *regexp.Regexp - -func (cr *containerReference) extractEnv(srcPath string, env *map[string]string) common.Executor { - if singleLineEnvPattern == nil { - // Single line pattern matches: - // SOME_VAR=data=moredata - // SOME_VAR=datamoredata - singleLineEnvPattern = regexp.MustCompile(`^([^=]*)\=(.*)$`) - multiLineEnvPattern = regexp.MustCompile(`^([^<]+)<<([\w-]+)$`) - } - - localEnv := *env - return func(ctx context.Context) error { - envTar, _, err := cr.cli.CopyFromContainer(ctx, cr.id, srcPath) - if err != nil { - return nil - } - defer envTar.Close() - - reader := tar.NewReader(envTar) - _, err = reader.Next() - if err != nil && err != io.EOF { - return fmt.Errorf("failed to read tar archive: %w", err) - } - s := bufio.NewScanner(reader) - multiLineEnvKey := "" - multiLineEnvDelimiter := "" - multiLineEnvContent := "" - for s.Scan() { - line := s.Text() - if singleLineEnv := singleLineEnvPattern.FindStringSubmatch(line); singleLineEnv != nil { - localEnv[singleLineEnv[1]] = singleLineEnv[2] - } - if line == multiLineEnvDelimiter { - localEnv[multiLineEnvKey] = multiLineEnvContent - multiLineEnvKey, multiLineEnvDelimiter, multiLineEnvContent = "", "", "" - } - if multiLineEnvKey != "" && multiLineEnvDelimiter != "" { - if multiLineEnvContent != "" { - multiLineEnvContent += "\n" - } - multiLineEnvContent += line - } - if multiLineEnvStart := multiLineEnvPattern.FindStringSubmatch(line); multiLineEnvStart != nil { - multiLineEnvKey = multiLineEnvStart[1] - multiLineEnvDelimiter = multiLineEnvStart[2] - } - } - env = &localEnv - return nil - } -} - func (cr *containerReference) extractFromImageEnv(env *map[string]string) common.Executor { envMap := *env return func(ctx context.Context) error { diff --git a/pkg/container/host_environment.go b/pkg/container/host_environment.go index b404e86d..30cd5005 100644 --- a/pkg/container/host_environment.go +++ b/pkg/container/host_environment.go @@ -341,51 +341,7 @@ func (e *HostEnvironment) Exec(command []string /*cmdline string, */, env map[st } func (e *HostEnvironment) UpdateFromEnv(srcPath string, env *map[string]string) common.Executor { - localEnv := *env - return func(ctx context.Context) error { - envTar, err := e.GetContainerArchive(ctx, srcPath) - if err != nil { - return nil - } - defer envTar.Close() - reader := tar.NewReader(envTar) - _, err = reader.Next() - if err != nil && err != io.EOF { - return err - } - s := bufio.NewScanner(reader) - for s.Scan() { - line := s.Text() - singleLineEnv := strings.Index(line, "=") - multiLineEnv := strings.Index(line, "<<") - if singleLineEnv != -1 && (multiLineEnv == -1 || singleLineEnv < multiLineEnv) { - localEnv[line[:singleLineEnv]] = line[singleLineEnv+1:] - } else if multiLineEnv != -1 { - multiLineEnvContent := "" - multiLineEnvDelimiter := line[multiLineEnv+2:] - delimiterFound := false - for s.Scan() { - content := s.Text() - if content == multiLineEnvDelimiter { - delimiterFound = true - break - } - if multiLineEnvContent != "" { - multiLineEnvContent += "\n" - } - multiLineEnvContent += content - } - if !delimiterFound { - return fmt.Errorf("invalid format delimiter '%v' not found before end of file", multiLineEnvDelimiter) - } - localEnv[line[:multiLineEnv]] = multiLineEnvContent - } else { - return fmt.Errorf("invalid format '%v', expected a line with '=' or '<<'", line) - } - } - env = &localEnv - return nil - } + return parseEnvFile(e, srcPath, env) } func (e *HostEnvironment) UpdateFromPath(env *map[string]string) common.Executor { diff --git a/pkg/container/parse_env_file.go b/pkg/container/parse_env_file.go new file mode 100644 index 00000000..ee79b7e7 --- /dev/null +++ b/pkg/container/parse_env_file.go @@ -0,0 +1,60 @@ +package container + +import ( + "archive/tar" + "bufio" + "context" + "fmt" + "io" + "strings" + + "github.com/nektos/act/pkg/common" +) + +func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Executor { + localEnv := *env + return func(ctx context.Context) error { + envTar, err := e.GetContainerArchive(ctx, srcPath) + if err != nil { + return nil + } + defer envTar.Close() + reader := tar.NewReader(envTar) + _, err = reader.Next() + if err != nil && err != io.EOF { + return err + } + s := bufio.NewScanner(reader) + for s.Scan() { + line := s.Text() + singleLineEnv := strings.Index(line, "=") + multiLineEnv := strings.Index(line, "<<") + if singleLineEnv != -1 && (multiLineEnv == -1 || singleLineEnv < multiLineEnv) { + localEnv[line[:singleLineEnv]] = line[singleLineEnv+1:] + } else if multiLineEnv != -1 { + multiLineEnvContent := "" + multiLineEnvDelimiter := line[multiLineEnv+2:] + delimiterFound := false + for s.Scan() { + content := s.Text() + if content == multiLineEnvDelimiter { + delimiterFound = true + break + } + if multiLineEnvContent != "" { + multiLineEnvContent += "\n" + } + multiLineEnvContent += content + } + if !delimiterFound { + return fmt.Errorf("invalid format delimiter '%v' not found before end of file", multiLineEnvDelimiter) + } + localEnv[line[:multiLineEnv]] = multiLineEnvContent + } else { + return fmt.Errorf("invalid format '%v', expected a line with '=' or '<<'", line) + } + } + env = &localEnv + return nil + } +} diff --git a/pkg/runner/runner_test.go b/pkg/runner/runner_test.go index 9cb4ff40..812ae322 100644 --- a/pkg/runner/runner_test.go +++ b/pkg/runner/runner_test.go @@ -171,6 +171,9 @@ func TestRunEvent(t *testing.T) { {workdir, "issue-598", "push", "", platforms}, {workdir, "if-env-act", "push", "", platforms}, {workdir, "env-and-path", "push", "", platforms}, + {workdir, "environment-files", "push", "", platforms}, + {workdir, "GITHUB_STATE", "push", "", platforms}, + {workdir, "environment-files-parser-bug", "push", "", platforms}, {workdir, "non-existent-action", "push", "Job 'nopanic' failed", platforms}, {workdir, "outputs", "push", "", platforms}, {workdir, "networking", "push", "", platforms}, diff --git a/pkg/runner/testdata/GITHUB_STATE/push.yml b/pkg/runner/testdata/GITHUB_STATE/push.yml new file mode 100644 index 00000000..179c5a72 --- /dev/null +++ b/pkg/runner/testdata/GITHUB_STATE/push.yml @@ -0,0 +1,22 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + steps: + - uses: nektos/act-test-actions/script@main + with: + pre: | + env + echo mystate0=mystateval > $GITHUB_STATE + echo "::save-state name=mystate1::mystateval" + main: | + env + echo mystate2=mystateval > $GITHUB_STATE + echo "::save-state name=mystate3::mystateval" + post: | + env + # Enable once https://github.com/nektos/act/issues/1459 is fixed + # [ "$STATE_mystate0" = "mystateval" ] + # [ "$STATE_mystate1" = "mystateval" ] + [ "$STATE_mystate2" = "mystateval" ] + [ "$STATE_mystate3" = "mystateval" ] \ No newline at end of file diff --git a/pkg/runner/testdata/environment-files-parser-bug/push.yaml b/pkg/runner/testdata/environment-files-parser-bug/push.yaml new file mode 100644 index 00000000..a64546c0 --- /dev/null +++ b/pkg/runner/testdata/environment-files-parser-bug/push.yaml @@ -0,0 +1,13 @@ +on: push +jobs: + _: + runs-on: ubuntu-latest + steps: + - run: | + echo "test< $GITHUB_ENV + echo "x=Thats really Weird" >> $GITHUB_ENV + echo "World" >> $GITHUB_ENV + - if: env.test != 'x=Thats really Weird' + run: exit 1 + - if: env.x == 'Thats really Weird' # This assert is triggered by the broken impl of act + run: exit 1 \ No newline at end of file diff --git a/pkg/runner/testdata/environment-files/push.yaml b/pkg/runner/testdata/environment-files/push.yaml new file mode 100644 index 00000000..a6ac36c7 --- /dev/null +++ b/pkg/runner/testdata/environment-files/push.yaml @@ -0,0 +1,101 @@ +name: environment-files +on: push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: "Append to $GITHUB_PATH" + run: | + echo "$HOME/someFolder" >> $GITHUB_PATH + - name: "Append some more to $GITHUB_PATH" + run: | + echo "$HOME/someOtherFolder" >> $GITHUB_PATH + - name: "Check PATH" + run: | + echo "${PATH}" + if [[ ! "${PATH}" =~ .*"$HOME/"someOtherFolder.*"$HOME/"someFolder.* ]]; then + echo "${PATH} doesn't match .*someOtherFolder.*someFolder.*" + exit 1 + fi + - name: "Prepend" + run: | + if ls | grep -q 'called ls' ; then + echo 'ls was overridden already?' + exit 2 + fi + path_add=$(mktemp -d) + cat > $path_add/ls <> $GITHUB_PATH + - name: "Verify prepend" + run: | + if ! ls | grep -q 'called ls' ; then + echo 'ls was not overridden' + exit 2 + fi + - name: "Write single line env to $GITHUB_ENV" + run: | + echo "KEY=value" >> $GITHUB_ENV + - name: "Check single line env" + run: | + if [[ "${KEY}" != "value" ]]; then + echo "${KEY} doesn't == 'value'" + exit 1 + fi + - name: "Write single line env with more than one 'equals' signs to $GITHUB_ENV" + run: | + echo "KEY=value=anothervalue" >> $GITHUB_ENV + - name: "Check single line env" + run: | + if [[ "${KEY}" != "value=anothervalue" ]]; then + echo "${KEY} doesn't == 'value=anothervalue'" + exit 1 + fi + - name: "Write multiline env to $GITHUB_ENV" + run: | + echo 'KEY2<> $GITHUB_ENV + echo value2 >> $GITHUB_ENV + echo 'EOF' >> $GITHUB_ENV + - name: "Check multiline line env" + run: | + if [[ "${KEY2}" != "value2" ]]; then + echo "${KEY2} doesn't == 'value'" + exit 1 + fi + - name: "Write multiline env with UUID to $GITHUB_ENV" + run: | + echo 'KEY3<> $GITHUB_ENV + echo value3 >> $GITHUB_ENV + echo 'ghadelimiter_b8273c6d-d535-419a-a010-b0aaac240e36' >> $GITHUB_ENV + - name: "Check multiline env with UUID to $GITHUB_ENV" + run: | + if [[ "${KEY3}" != "value3" ]]; then + echo "${KEY3} doesn't == 'value3'" + exit 1 + fi + - name: "Write single line output to $GITHUB_OUTPUT" + id: write-single-output + run: | + echo "KEY=value" >> $GITHUB_OUTPUT + - name: "Check single line output" + run: | + if [[ "${{ steps.write-single-output.outputs.KEY }}" != "value" ]]; then + echo "${{ steps.write-single-output.outputs.KEY }} doesn't == 'value'" + exit 1 + fi + - name: "Write multiline output to $GITHUB_OUTPUT" + id: write-multi-output + run: | + echo 'KEY2<> $GITHUB_OUTPUT + echo value2 >> $GITHUB_OUTPUT + echo 'EOF' >> $GITHUB_OUTPUT + - name: "Check multiline output" + run: | + if [[ "${{ steps.write-multi-output.outputs.KEY2 }}" != "value2" ]]; then + echo "${{ steps.write-multi-output.outputs.KEY2 }} doesn't == 'value2'" + exit 1 + fi \ No newline at end of file