diff --git a/act/container/parse_env_file.go b/act/container/parse_env_file.go index bfa261ca..ec8f1545 100644 --- a/act/container/parse_env_file.go +++ b/act/container/parse_env_file.go @@ -13,6 +13,9 @@ import ( "strings" "gitea.com/gitea/runner/act/common" + + "golang.org/x/text/encoding/unicode" + "golang.org/x/text/transform" ) func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Executor { @@ -28,11 +31,19 @@ func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Ex if err != nil && err != io.EOF { return err } - s := bufio.NewScanner(reader) + // Decode by BOM: Windows PowerShell 5.1 redirection writes UTF-16, and some + // tools emit a UTF-8 BOM. Without a BOM the file is read as UTF-8, as before. + decoded := transform.NewReader(reader, unicode.BOMOverride(unicode.UTF8.NewDecoder())) + + s := bufio.NewScanner(decoded) // Default 64 KiB max token size is too small for realistic env-file lines; allow up to 16 MiB. s.Buffer(make([]byte, 0, 64*1024), 16*1024*1024) for s.Scan() { line := s.Text() + // GitHub's runner ignores blank lines + if strings.TrimSpace(line) == "" { + continue + } singleLineEnv := strings.Index(line, "=") multiLineEnv := strings.Index(line, "<<") if singleLineEnv != -1 && (multiLineEnv == -1 || singleLineEnv < multiLineEnv) { diff --git a/act/container/parse_env_file_test.go b/act/container/parse_env_file_test.go index 6a8525a6..891b0f37 100644 --- a/act/container/parse_env_file_test.go +++ b/act/container/parse_env_file_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "golang.org/x/text/encoding" + "golang.org/x/text/encoding/unicode" ) func newTestHostEnv(t *testing.T) (*HostEnvironment, string) { @@ -64,6 +66,63 @@ func TestParseEnvFileLineExceedsBufferReportsScannerError(t *testing.T) { assert.Contains(t, err.Error(), "reading env file") } +// Regression test: a blank line used to fail the job at "Complete Job", after +// every step had already been recorded as successful. +func TestParseEnvFileBlankLines(t *testing.T) { + e, envPath := newTestHostEnv(t) + require.NoError(t, os.WriteFile(envPath, []byte("\nFOO=bar\n\n \nBAZ=qux\n\n"), 0o600)) + + env := map[string]string{} + require.NoError(t, parseEnvFile(e, envPath, &env)(context.Background())) + assert.Equal(t, "bar", env["FOO"]) + assert.Equal(t, "qux", env["BAZ"]) +} + +// blank lines inside a heredoc value are content, not separators +func TestParseEnvFileMultiLineKeepsBlankLines(t *testing.T) { + e, envPath := newTestHostEnv(t) + require.NoError(t, os.WriteFile(envPath, []byte("FOO<