refactor: share UpdateFromEnv logic (#1457)
* refactor: share UpdateFromEnv logic * Add test for GITHUB_OUTPUT Co-authored-by: Ben Randall <veleek@gmail.com> * Add GITHUB_STATE test * Add test for the old broken parser Co-authored-by: Ben Randall <veleek@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
4c2524ab4d
commit
57bf4d27a2
7 changed files with 201 additions and 99 deletions
|
@ -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 {
|
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 {
|
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 {
|
func (cr *containerReference) extractFromImageEnv(env *map[string]string) common.Executor {
|
||||||
envMap := *env
|
envMap := *env
|
||||||
return func(ctx context.Context) error {
|
return func(ctx context.Context) error {
|
||||||
|
|
|
@ -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 {
|
func (e *HostEnvironment) UpdateFromEnv(srcPath string, env *map[string]string) common.Executor {
|
||||||
localEnv := *env
|
return parseEnvFile(e, srcPath, 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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *HostEnvironment) UpdateFromPath(env *map[string]string) common.Executor {
|
func (e *HostEnvironment) UpdateFromPath(env *map[string]string) common.Executor {
|
||||||
|
|
60
pkg/container/parse_env_file.go
Normal file
60
pkg/container/parse_env_file.go
Normal file
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -171,6 +171,9 @@ func TestRunEvent(t *testing.T) {
|
||||||
{workdir, "issue-598", "push", "", platforms},
|
{workdir, "issue-598", "push", "", platforms},
|
||||||
{workdir, "if-env-act", "push", "", platforms},
|
{workdir, "if-env-act", "push", "", platforms},
|
||||||
{workdir, "env-and-path", "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, "non-existent-action", "push", "Job 'nopanic' failed", platforms},
|
||||||
{workdir, "outputs", "push", "", platforms},
|
{workdir, "outputs", "push", "", platforms},
|
||||||
{workdir, "networking", "push", "", platforms},
|
{workdir, "networking", "push", "", platforms},
|
||||||
|
|
22
pkg/runner/testdata/GITHUB_STATE/push.yml
vendored
Normal file
22
pkg/runner/testdata/GITHUB_STATE/push.yml
vendored
Normal file
|
@ -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" ]
|
13
pkg/runner/testdata/environment-files-parser-bug/push.yaml
vendored
Normal file
13
pkg/runner/testdata/environment-files-parser-bug/push.yaml
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
on: push
|
||||||
|
jobs:
|
||||||
|
_:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- run: |
|
||||||
|
echo "test<<World" > $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
|
101
pkg/runner/testdata/environment-files/push.yaml
vendored
Normal file
101
pkg/runner/testdata/environment-files/push.yaml
vendored
Normal file
|
@ -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 <<LS
|
||||||
|
#!/bin/sh
|
||||||
|
echo 'called ls'
|
||||||
|
LS
|
||||||
|
chmod +x $path_add/ls
|
||||||
|
echo $path_add >> $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<<EOF' >> $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<<ghadelimiter_b8273c6d-d535-419a-a010-b0aaac240e36' >> $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<<EOF' >> $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
|
Loading…
Reference in a new issue