From 05358e62571781c987d09e111a4f0f5ffbffcbc2 Mon Sep 17 00:00:00 2001 From: Valentin Maerten Date: Sat, 31 Jan 2026 09:32:08 +0100 Subject: [PATCH] fix(ci): fix Windows test failures - Normalize paths to forward slashes in glob.go for consistent sorting - Use filepath.ToSlash in error messages to avoid double-escaped backslashes - Add goldie.WithEqualFn for cross-platform line ending normalization - Simplify CI workflow (go run ./cmd/task test) --- .github/workflows/test.yml | 5 +---- errors/errors_taskfile.go | 25 +++++++++++++------------ executor_test.go | 1 + formatter_test.go | 1 + internal/fingerprint/glob.go | 4 +++- task_test.go | 13 +++++++++++++ 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9174b0a2..14ac1be4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,8 +31,5 @@ jobs: env: GOPROXY: https://proxy.golang.org - - name: Build - run: go build -o ./bin/task -v ./cmd/task - - name: Test - run: ./bin/task test --output=group --output-group-begin='::group::{{.TASK}}' --output-group-end='::endgroup::' + run: go run ./cmd/task test diff --git a/errors/errors_taskfile.go b/errors/errors_taskfile.go index ad665d29..6fbaefb5 100644 --- a/errors/errors_taskfile.go +++ b/errors/errors_taskfile.go @@ -3,6 +3,7 @@ package errors import ( "fmt" "net/http" + "path/filepath" "time" "github.com/Masterminds/semver/v3" @@ -24,7 +25,7 @@ func (err TaskfileNotFoundError) Error() string { if err.AskInit { walkText += " Run `task --init` to create a new Taskfile." } - return fmt.Sprintf(`task: No Taskfile found at %q%s`, err.URI, walkText) + return fmt.Sprintf(`task: No Taskfile found at %q%s`, filepath.ToSlash(err.URI), walkText) } func (err TaskfileNotFoundError) Code() int { @@ -51,7 +52,7 @@ type TaskfileInvalidError struct { } func (err TaskfileInvalidError) Error() string { - return fmt.Sprintf("task: Failed to parse %s:\n%v", err.URI, err.Err) + return fmt.Sprintf("task: Failed to parse %s:\n%v", filepath.ToSlash(err.URI), err.Err) } func (err TaskfileInvalidError) Code() int { @@ -70,7 +71,7 @@ func (err TaskfileFetchFailedError) Error() string { if err.HTTPStatusCode != 0 { statusText = fmt.Sprintf(" with status code %d (%s)", err.HTTPStatusCode, http.StatusText(err.HTTPStatusCode)) } - return fmt.Sprintf(`task: Download of %q failed%s`, err.URI, statusText) + return fmt.Sprintf(`task: Download of %q failed%s`, filepath.ToSlash(err.URI), statusText) } func (err TaskfileFetchFailedError) Code() int { @@ -86,7 +87,7 @@ type TaskfileNotTrustedError struct { func (err *TaskfileNotTrustedError) Error() string { return fmt.Sprintf( `task: Taskfile %q not trusted by user`, - err.URI, + filepath.ToSlash(err.URI), ) } @@ -103,7 +104,7 @@ type TaskfileNotSecureError struct { func (err *TaskfileNotSecureError) Error() string { return fmt.Sprintf( `task: Taskfile %q cannot be downloaded over an insecure connection. You can override this by using the --insecure flag`, - err.URI, + filepath.ToSlash(err.URI), ) } @@ -120,7 +121,7 @@ type TaskfileCacheNotFoundError struct { func (err *TaskfileCacheNotFoundError) Error() string { return fmt.Sprintf( `task: Taskfile %q was not found in the cache. Remove the --offline flag to use a remote copy or download it using the --download flag`, - err.URI, + filepath.ToSlash(err.URI), ) } @@ -141,12 +142,12 @@ func (err *TaskfileVersionCheckError) Error() string { if err.SchemaVersion == nil { return fmt.Sprintf( `task: Missing schema version in Taskfile %q`, - err.URI, + filepath.ToSlash(err.URI), ) } return fmt.Sprintf( "task: Invalid schema version in Taskfile %q:\nSchema version (%s) %s", - err.URI, + filepath.ToSlash(err.URI), err.SchemaVersion.String(), err.Message, ) @@ -166,7 +167,7 @@ type TaskfileNetworkTimeoutError struct { func (err *TaskfileNetworkTimeoutError) Error() string { return fmt.Sprintf( `task: Network connection timed out after %s while attempting to download Taskfile %q`, - err.Timeout, err.URI, + err.Timeout, filepath.ToSlash(err.URI), ) } @@ -183,8 +184,8 @@ type TaskfileCycleError struct { func (err TaskfileCycleError) Error() string { return fmt.Sprintf("task: include cycle detected between %s <--> %s", - err.Source, - err.Destination, + filepath.ToSlash(err.Source), + filepath.ToSlash(err.Destination), ) } @@ -203,7 +204,7 @@ type TaskfileDoesNotMatchChecksum struct { func (err *TaskfileDoesNotMatchChecksum) Error() string { return fmt.Sprintf( "task: The checksum of the Taskfile at %q does not match!\ngot: %q\nwant: %q", - err.URI, + filepath.ToSlash(err.URI), err.ActualChecksum, err.ExpectedChecksum, ) diff --git a/executor_test.go b/executor_test.go index 6e3ff3e1..f72104a6 100644 --- a/executor_test.go +++ b/executor_test.go @@ -165,6 +165,7 @@ func (tt *ExecutorTest) run(t *testing.T) { // Create a golden fixture file for the output g := goldie.New(t, goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")), + goldie.WithEqualFn(NormalizedEqual), ) // Call setup and check for errors diff --git a/formatter_test.go b/formatter_test.go index 7221ff76..b92c8d55 100644 --- a/formatter_test.go +++ b/formatter_test.go @@ -127,6 +127,7 @@ func (tt *FormatterTest) run(t *testing.T) { // Create a golden fixture file for the output g := goldie.New(t, goldie.WithFixtureDir(filepath.Join(e.Dir, "testdata")), + goldie.WithEqualFn(NormalizedEqual), ) // Call setup and check for errors diff --git a/internal/fingerprint/glob.go b/internal/fingerprint/glob.go index bf12afdd..fd3cafce 100644 --- a/internal/fingerprint/glob.go +++ b/internal/fingerprint/glob.go @@ -2,6 +2,7 @@ package fingerprint import ( "os" + "path/filepath" "sort" "github.com/go-task/task/v3/internal/execext" @@ -50,7 +51,8 @@ func collectKeys(m map[string]bool) []string { keys := make([]string, 0, len(m)) for k, v := range m { if v { - keys = append(keys, k) + // Normalize path separators for consistent sorting across platforms + keys = append(keys, filepath.ToSlash(k)) } } sort.Strings(keys) diff --git a/task_test.go b/task_test.go index 9d54af97..401b8e4c 100644 --- a/task_test.go +++ b/task_test.go @@ -308,6 +308,19 @@ func PPSortedLines(t *testing.T, b []byte) []byte { return []byte(strings.Join(lines, "\n") + "\n") } +// normalizeLineEndings converts CRLF and CR to LF for cross-platform comparison +func normalizeLineEndings(b []byte) []byte { + b = bytes.ReplaceAll(b, []byte("\r\n"), []byte("\n")) + b = bytes.ReplaceAll(b, []byte("\r"), []byte("\n")) + return b +} + +// NormalizedEqual compares two byte slices after normalizing line endings. +// This is used as a custom goldie.EqualFn for cross-platform golden file tests. +func NormalizedEqual(actual, expected []byte) bool { + return bytes.Equal(normalizeLineEndings(actual), normalizeLineEndings(expected)) +} + // SyncBuffer is a threadsafe buffer for testing. // Some times replace stdout/stderr with a buffer to capture output. // stdout and stderr are threadsafe, but a regular bytes.Buffer is not.