Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
* text=auto
*.mdx -linguist-detectable

# Keep LF line endings in testdata for consistent checksums across platforms
testdata/** text eol=lf
18 changes: 5 additions & 13 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,19 @@ jobs:
test:
name: Test
strategy:
fail-fast: false
matrix:
go-version: [1.24.x, 1.25.x]
platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{matrix.platform}}
steps:
- name: Check out code
uses: actions/checkout@v6

- name: Set up Go ${{matrix.go-version}}
uses: actions/setup-go@v6
with:
go-version: ${{matrix.go-version}}
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v6

- name: Download Go modules
run: go mod download
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
15 changes: 9 additions & 6 deletions compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,18 +198,21 @@ func (c *Compiler) ResetCache() {
}

func (c *Compiler) getSpecialVars(t *ast.Task, call *Call) (map[string]string, error) {
// Use filepath.ToSlash for all paths to ensure consistent forward slashes
// across platforms. This prevents issues with backslashes being interpreted
// as escape sequences when paths are used in shell commands on Windows.
allVars := map[string]string{
"TASK_EXE": filepath.ToSlash(os.Args[0]),
"ROOT_TASKFILE": filepathext.SmartJoin(c.Dir, c.Entrypoint),
"ROOT_DIR": c.Dir,
"USER_WORKING_DIR": c.UserWorkingDir,
"ROOT_TASKFILE": filepath.ToSlash(filepathext.SmartJoin(c.Dir, c.Entrypoint)),
"ROOT_DIR": filepath.ToSlash(c.Dir),
"USER_WORKING_DIR": filepath.ToSlash(c.UserWorkingDir),
"TASK_VERSION": version.GetVersion(),
}
if t != nil {
allVars["TASK"] = t.Task
allVars["TASK_DIR"] = filepathext.SmartJoin(c.Dir, t.Dir)
allVars["TASKFILE"] = t.Location.Taskfile
allVars["TASKFILE_DIR"] = filepath.Dir(t.Location.Taskfile)
allVars["TASK_DIR"] = filepath.ToSlash(filepathext.SmartJoin(c.Dir, t.Dir))
allVars["TASKFILE"] = filepath.ToSlash(t.Location.Taskfile)
allVars["TASKFILE_DIR"] = filepath.ToSlash(filepath.Dir(t.Location.Taskfile))
} else {
allVars["TASK"] = ""
allVars["TASK_DIR"] = ""
Expand Down
25 changes: 13 additions & 12 deletions errors/errors_taskfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package errors
import (
"fmt"
"net/http"
"path/filepath"
"time"

"github.com/Masterminds/semver/v3"
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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),
)
}

Expand All @@ -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),
)
}

Expand All @@ -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),
)
}

Expand All @@ -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,
)
Expand All @@ -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),
)
}

Expand All @@ -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),
)
}

Expand All @@ -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,
)
Expand Down
1 change: 1 addition & 0 deletions executor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion internal/fingerprint/glob.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fingerprint

import (
"os"
"path/filepath"
"sort"

"github.com/go-task/task/v3/internal/execext"
Expand Down Expand Up @@ -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)
Expand Down
Loading
Loading