From f63a63fa6cf654dcdc647d5714d4bf102bbf8611 Mon Sep 17 00:00:00 2001 From: Timothy Rule <34501912+trulede@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:56:35 +0100 Subject: [PATCH] fix: improve error message when searching for Taskfile. (#2682) --- errors/errors_taskfile.go | 11 +++++++---- internal/fsext/fs.go | 17 ++++++++++------- setup.go | 11 ++++------- taskfile/node_file.go | 8 +++++++- taskrc/taskrc.go | 4 ++++ 5 files changed, 32 insertions(+), 19 deletions(-) diff --git a/errors/errors_taskfile.go b/errors/errors_taskfile.go index ad665d29..0bc2f09f 100644 --- a/errors/errors_taskfile.go +++ b/errors/errors_taskfile.go @@ -11,14 +11,17 @@ import ( // TaskfileNotFoundError is returned when no appropriate Taskfile is found when // searching the filesystem. type TaskfileNotFoundError struct { - URI string - Walk bool - AskInit bool + URI string + Walk bool + AskInit bool + OwnerChange bool } func (err TaskfileNotFoundError) Error() string { var walkText string - if err.Walk { + if err.OwnerChange { + walkText = " (or any of the parent directories until ownership changed)." + } else if err.Walk { walkText = " (or any of the parent directories)." } if err.AskInit { diff --git a/internal/fsext/fs.go b/internal/fsext/fs.go index 8c8e3af6..cbeeede9 100644 --- a/internal/fsext/fs.go +++ b/internal/fsext/fs.go @@ -108,10 +108,10 @@ func SearchAll(entrypoint, dir string, possibleFilenames []string) ([]string, er } } paths, err := SearchNPathRecursively(dir, possibleFilenames, -1) - if err != nil { - return nil, err - } - return append(entrypoints, paths...), nil + // The call to SearchNPathRecursively is ambiguous and may return + // os.ErrPermission if its search ends, however it may have still + // returned valid paths. Caller may choose to ignore that error. + return append(entrypoints, paths...), err } // SearchPath will check if a file at the given path exists or not. If it does, @@ -188,9 +188,12 @@ func SearchNPathRecursively(path string, possibleFilenames []string, n int) ([]s return nil, err } - // Error if we reached the root directory and still haven't found a file - // OR if the user id of the directory changes - if path == parentPath || (parentOwner != owner) { + // If the user id of the directory changes indicate a permission error, otherwise + // the calling code will infer an error condition based on the accumulated + // contents of paths. + if parentOwner != owner { + return paths, os.ErrPermission + } else if path == parentPath { return paths, nil } diff --git a/setup.go b/setup.go index fbbe64e7..d9f57d46 100644 --- a/setup.go +++ b/setup.go @@ -16,7 +16,6 @@ import ( "github.com/go-task/task/v3/internal/env" "github.com/go-task/task/v3/internal/execext" "github.com/go-task/task/v3/internal/filepathext" - "github.com/go-task/task/v3/internal/fsext" "github.com/go-task/task/v3/internal/logger" "github.com/go-task/task/v3/internal/output" "github.com/go-task/task/v3/internal/version" @@ -60,12 +59,10 @@ func (e *Executor) getRootNode() (taskfile.Node, error) { taskfile.WithCert(e.Cert), taskfile.WithCertKey(e.CertKey), ) - if os.IsNotExist(err) { - return nil, errors.TaskfileNotFoundError{ - URI: fsext.DefaultDir(e.Entrypoint, e.Dir), - Walk: true, - AskInit: true, - } + var taskNotFoundError errors.TaskfileNotFoundError + if errors.As(err, &taskNotFoundError) { + taskNotFoundError.AskInit = true + return nil, taskNotFoundError } if err != nil { return nil, err diff --git a/taskfile/node_file.go b/taskfile/node_file.go index bbfb2857..7e015a05 100644 --- a/taskfile/node_file.go +++ b/taskfile/node_file.go @@ -22,7 +22,13 @@ func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) resolvedEntrypoint, err := fsext.Search(entrypoint, dir, DefaultTaskfiles) if err != nil { if errors.Is(err, os.ErrNotExist) { - return nil, errors.TaskfileNotFoundError{URI: entrypoint, Walk: false} + if entrypoint == "" { + return nil, errors.TaskfileNotFoundError{URI: entrypoint, Walk: true} + } else { + return nil, errors.TaskfileNotFoundError{URI: entrypoint, Walk: false} + } + } else if errors.Is(err, os.ErrPermission) { + return nil, errors.TaskfileNotFoundError{URI: entrypoint, Walk: true, OwnerChange: true} } return nil, err } diff --git a/taskrc/taskrc.go b/taskrc/taskrc.go index bfa91093..c4c921f7 100644 --- a/taskrc/taskrc.go +++ b/taskrc/taskrc.go @@ -6,6 +6,7 @@ import ( "slices" "strings" + "github.com/go-task/task/v3/errors" "github.com/go-task/task/v3/internal/fsext" "github.com/go-task/task/v3/taskrc/ast" ) @@ -62,6 +63,9 @@ func GetConfig(dir string) (*ast.TaskRC, error) { return config, err } entrypoints, err := fsext.SearchAll("", absDir, defaultTaskRCs) + if errors.Is(err, os.ErrPermission) { + err = nil + } if err != nil { return config, err }