diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5513905c7d..5a2b91c601 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -16,7 +16,7 @@ jobs: name: Lint strategy: matrix: - go-version: [1.25.10, 1.26.x] + go-version: [1.26.x] runs-on: ubuntu-latest steps: - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ceaab866d5..eb4cd19c35 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: [1.25.10, 1.26.x] + go-version: [1.26.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{matrix.platform}} steps: diff --git a/checksum_benchmark_test.go b/checksum_benchmark_test.go index 7677f7fa32..37c0567216 100644 --- a/checksum_benchmark_test.go +++ b/checksum_benchmark_test.go @@ -1,5 +1,4 @@ //go:build fsbench -// +build fsbench package task_test diff --git a/errors/error_taskfile_decode.go b/errors/error_taskfile_decode.go index 2781555dd2..b1fc42a64d 100644 --- a/errors/error_taskfile_decode.go +++ b/errors/error_taskfile_decode.go @@ -25,8 +25,7 @@ type ( func NewTaskfileDecodeError(err error, node *yaml.Node) *TaskfileDecodeError { // If the error is already a DecodeError, return it - taskfileInvalidErr := &TaskfileDecodeError{} - if errors.As(err, &taskfileInvalidErr) { + if taskfileInvalidErr, ok := errors.AsType[*TaskfileDecodeError](err); ok { return taskfileInvalidErr } return &TaskfileDecodeError{ @@ -45,8 +44,7 @@ func (err *TaskfileDecodeError) Error() string { fmt.Fprintln(buf, color.RedString("err: %s", err.Message)) } else { // Extract the errors from the TypeError - te := &yaml.TypeError{} - if errors.As(err.Err, &te) { + if te, ok := errors.AsType[*yaml.TypeError](err.Err); ok { if len(te.Errors) > 1 { fmt.Fprintln(buf, color.RedString("errs:")) for _, message := range te.Errors { diff --git a/errors/errors.go b/errors/errors.go index 1ed50e8741..836a90c226 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -66,6 +66,13 @@ func As(err error, target any) bool { return errors.As(err, target) } +// AsType wraps the standard errors.AsType function so that we don't need to alias +// that package. It returns the first error in err's tree that matches type T, and +// whether such an error was found. +func AsType[T error](err error) (T, bool) { + return errors.AsType[T](err) +} + // Unwrap wraps the standard errors.Unwrap function so that we don't need to alias that package. func Unwrap(err error) error { return errors.Unwrap(err) diff --git a/errors/errors_task.go b/errors/errors_task.go index 454b3a8fb7..46fe0cb42c 100644 --- a/errors/errors_task.go +++ b/errors/errors_task.go @@ -47,8 +47,7 @@ func (err *TaskRunError) Code() int { } func (err *TaskRunError) TaskExitCode() int { - var exit interp.ExitStatus - if errors.As(err.Err, &exit) { + if exit, ok := errors.AsType[interp.ExitStatus](err.Err); ok { return int(exit) } return err.Code() diff --git a/experiments/errors.go b/experiments/errors.go index 68a5f8b872..850220c4f9 100644 --- a/experiments/errors.go +++ b/experiments/errors.go @@ -16,7 +16,7 @@ type InvalidValueError struct { func (err InvalidValueError) Error() string { return fmt.Sprintf( - "task: Experiment %q has an invalid value %q (allowed values: %s)", + "task: Experiment %q has an invalid value %d (allowed values: %s)", err.Name, err.Value, strings.Join(slicesext.Convert(err.AllowedValues, strconv.Itoa), ", "), diff --git a/go.mod b/go.mod index 0cf1f5a1ba..ca3ce15dd0 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/go-task/task/v3 -go 1.25.10 +go 1.26.4 require ( charm.land/bubbles/v2 v2.1.0 diff --git a/internal/fingerprint/gitignore.go b/internal/fingerprint/gitignore.go index 1b89349e91..168358d48e 100644 --- a/internal/fingerprint/gitignore.go +++ b/internal/fingerprint/gitignore.go @@ -2,9 +2,11 @@ package fingerprint import ( "bufio" + "cmp" + "maps" "os" "path/filepath" - "sort" + "slices" "strings" "sync" "time" @@ -95,17 +97,14 @@ func filterGitignored(files map[string]bool, dir string) map[string]bool { // Shallow dirs first (lower priority): the matcher scans patterns last to // first, so deeper rules win and can negate shallower ones. - dirs := make([]string, 0, len(dirSet)) - for d := range dirSet { - dirs = append(dirs, d) - } - sort.Slice(dirs, func(i, j int) bool { - di := strings.Count(dirs[i], string(filepath.Separator)) - dj := strings.Count(dirs[j], string(filepath.Separator)) - if di != dj { - return di < dj - } - return dirs[i] < dirs[j] + dirs := slices.Collect(maps.Keys(dirSet)) + slices.SortFunc(dirs, func(a, b string) int { + da := strings.Count(a, string(filepath.Separator)) + db := strings.Count(b, string(filepath.Separator)) + if da != db { + return cmp.Compare(da, db) + } + return cmp.Compare(a, b) }) var patterns []gitignore.Pattern diff --git a/internal/fingerprint/glob.go b/internal/fingerprint/glob.go index 14ac4cf716..b50061f2c3 100644 --- a/internal/fingerprint/glob.go +++ b/internal/fingerprint/glob.go @@ -3,7 +3,7 @@ package fingerprint import ( "os" "path/filepath" - "sort" + "slices" "github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/filepathext" @@ -65,6 +65,6 @@ func collectKeys(m map[string]bool) []string { keys = append(keys, filepath.ToSlash(k)) } } - sort.Strings(keys) + slices.Sort(keys) return keys } diff --git a/internal/fsext/glob.go b/internal/fsext/glob.go index 903bf75973..faf0a2d36e 100644 --- a/internal/fsext/glob.go +++ b/internal/fsext/glob.go @@ -4,7 +4,7 @@ import ( "io/fs" "os" "path/filepath" - "sort" + "slices" "strings" "github.com/go-task/task/v3/errors" @@ -74,6 +74,6 @@ func FastRecursiveGlob(pattern string) ([]string, bool, error) { if err != nil { return nil, true, err } - sort.Strings(results) + slices.Sort(results) return results, true, nil } diff --git a/internal/sort/sorter.go b/internal/sort/sorter.go index 1419597358..0d4c34004e 100644 --- a/internal/sort/sorter.go +++ b/internal/sort/sorter.go @@ -1,8 +1,8 @@ package sort import ( + "cmp" "slices" - "sort" "strings" ) @@ -29,16 +29,16 @@ func AlphaNumericWithRootTasksFirst(items []string, namespaces []string) []strin if len(namespaces) > 0 { return AlphaNumeric(items, namespaces) } - sort.Slice(items, func(i, j int) bool { - iContainsColon := strings.Contains(items[i], ":") - jContainsColon := strings.Contains(items[j], ":") - if iContainsColon == jContainsColon { - return items[i] < items[j] + slices.SortFunc(items, func(a, b string) int { + aContainsColon := strings.Contains(a, ":") + bContainsColon := strings.Contains(b, ":") + if aContainsColon == bContainsColon { + return cmp.Compare(a, b) } - if !iContainsColon && jContainsColon { - return true + if !aContainsColon && bContainsColon { + return -1 } - return false + return 1 }) return items } diff --git a/setup.go b/setup.go index f24f8b8eb3..ad567ef96e 100644 --- a/setup.go +++ b/setup.go @@ -59,8 +59,7 @@ func (e *Executor) getRootNode() (taskfile.Node, error) { taskfile.WithCert(e.Cert), taskfile.WithCertKey(e.CertKey), ) - var taskNotFoundError errors.TaskfileNotFoundError - if errors.As(err, &taskNotFoundError) { + if taskNotFoundError, ok := errors.AsType[errors.TaskfileNotFoundError](err); ok { taskNotFoundError.AskInit = true return nil, taskNotFoundError } diff --git a/signals_test.go b/signals_test.go index 6faa8aef08..575310fa8f 100644 --- a/signals_test.go +++ b/signals_test.go @@ -1,5 +1,4 @@ //go:build signals -// +build signals // This file contains tests for signal handling on Unix. // Based on code from https://github.com/marco-m/timeit @@ -154,9 +153,8 @@ func TestSignalSentToProcessGroup(t *testing.T) { err := sut.Wait() - var wantErr *exec.ExitError const wantExitStatus = 201 - if errors.As(err, &wantErr) { + if wantErr, ok := errors.AsType[*exec.ExitError](err); ok { if wantErr.ExitCode() != wantExitStatus { t.Errorf( "waiting for child process: got exit status %v; want %d\n"+ @@ -166,7 +164,7 @@ func TestSignalSentToProcessGroup(t *testing.T) { } } else { t.Errorf("waiting for child process: got unexpected error type %v (%T); want (%T)", - err, err, wantErr) + err, err, (*exec.ExitError)(nil)) } gotLines := strings.SplitAfter(out.String(), "\n") diff --git a/task.go b/task.go index 98d340c976..35e0379fb3 100644 --- a/task.go +++ b/task.go @@ -277,8 +277,7 @@ func (e *Executor) RunTask(ctx context.Context, call *Call) error { e.Logger.VerboseErrf(logger.Yellow, "task: error cleaning status on error: %v\n", err2) } - var exitCode interp.ExitStatus - if errors.As(err, &exitCode) { + if exitCode, ok := errors.AsType[interp.ExitStatus](err); ok { if t.IgnoreError { e.Logger.VerboseErrf(logger.Yellow, "task: task error ignored: %v\n", err) continue @@ -382,8 +381,7 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in defer reacquire() err := e.RunTask(ctx, &Call{Task: cmd.Task, Vars: cmd.Vars, Silent: cmd.Silent, Indirect: true}) - var exitCode interp.ExitStatus - if errors.As(err, &exitCode) && cmd.IgnoreError { + if _, ok := errors.AsType[interp.ExitStatus](err); ok && cmd.IgnoreError { e.Logger.VerboseErrf(logger.Yellow, "task: [%s] task error ignored: %v\n", t.Name(), err) return nil } @@ -426,8 +424,7 @@ func (e *Executor) runCommand(ctx context.Context, t *ast.Task, call *Call, i in if closeErr := closer(err); closeErr != nil { e.Logger.Errf(logger.Red, "task: unable to close writer: %v\n", closeErr) } - var exitCode interp.ExitStatus - if errors.As(err, &exitCode) && cmd.IgnoreError { + if _, ok := errors.AsType[interp.ExitStatus](err); ok && cmd.IgnoreError { e.Logger.VerboseErrf(logger.Yellow, "task: [%s] command error ignored: %v\n", t.Name(), err) return nil } diff --git a/taskfile/reader.go b/taskfile/reader.go index 1b8556c1dd..703277470a 100644 --- a/taskfile/reader.go +++ b/taskfile/reader.go @@ -413,8 +413,7 @@ func (r *Reader) readNode(ctx context.Context, node Node) (*ast.Taskfile, error) var tf ast.Taskfile if err := yaml.Unmarshal(b, &tf); err != nil { // Decode the taskfile and add the file info the any errors - taskfileDecodeErr := &errors.TaskfileDecodeError{} - if errors.As(err, &taskfileDecodeErr) { + if taskfileDecodeErr, ok := errors.AsType[*errors.TaskfileDecodeError](err); ok { snippet := NewSnippet(b, WithLine(taskfileDecodeErr.Line), WithColumn(taskfileDecodeErr.Column), diff --git a/watch_test.go b/watch_test.go index 8bef86ed49..6d3c89632e 100644 --- a/watch_test.go +++ b/watch_test.go @@ -1,5 +1,4 @@ //go:build watch -// +build watch package task_test