diff --git a/errors/errors_taskfile.go b/errors/errors_taskfile.go index ad665d29f8..771b5a7f82 100644 --- a/errors/errors_taskfile.go +++ b/errors/errors_taskfile.go @@ -11,9 +11,8 @@ 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 } func (err TaskfileNotFoundError) Error() string { @@ -21,9 +20,6 @@ func (err TaskfileNotFoundError) Error() string { if err.Walk { walkText = " (or any of the parent directories)." } - 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) } diff --git a/internal/fsext/fs.go b/internal/fsext/fs.go index 8c8e3af6b9..fcf614bbb5 100644 --- a/internal/fsext/fs.go +++ b/internal/fsext/fs.go @@ -49,6 +49,24 @@ func ResolveDir(entrypoint, resolvedEntrypoint, dir string) (string, error) { return filepath.Dir(resolvedEntrypoint), nil } +// GetSearchPath returns the absolute path to search for the entrypoint. If the +// entrypoint is set, it returns the absolute path to the entrypoint. If the dir +// is set, it returns the absolute path to the dir. If both are empty, it returns +// the current working directory. +func GetSearchPath(entrypoint, dir string) (string, error) { + if entrypoint != "" { + return filepath.Abs(entrypoint) + } + if dir != "" { + return filepath.Abs(dir) + } + wd, err := os.Getwd() + if err != nil { + return "", err + } + return wd, nil +} + // Search looks for files with the given possible filenames using the given // entrypoint and directory. If the entrypoint is set, it checks if the // entrypoint matches a file or if it matches a directory containing one of the diff --git a/internal/fsext/fs_test.go b/internal/fsext/fs_test.go index 06f59eb8b4..beec319144 100644 --- a/internal/fsext/fs_test.go +++ b/internal/fsext/fs_test.go @@ -59,6 +59,78 @@ func TestDefaultDir(t *testing.T) { } } +func TestGetSearchPath(t *testing.T) { + t.Parallel() + + wd, err := os.Getwd() + require.NoError(t, err) + + tests := []struct { + name string + entrypoint string + dir string + expectedPath string + }{ + { + name: "return absolute entrypoint if only absolute entrypoint is set", + entrypoint: "/dir/to/Taskfile.yml", + dir: "", + expectedPath: "/dir/to/Taskfile.yml", + }, + { + name: "return absolute path of entrypoint if only relative entrypoint is set", + entrypoint: "./dir/to/Taskfile.yml", + dir: "", + expectedPath: filepath.Join(wd, "dir", "to", "Taskfile.yml"), + }, + { + name: "return absolute dir if only absolute dir is set", + entrypoint: "", + dir: "/dir/to", + expectedPath: "/dir/to", + }, + { + name: "return absolute path of dir if only relative dir is set", + entrypoint: "", + dir: "./dir/to", + expectedPath: filepath.Join(wd, "dir", "to"), + }, + { + name: "return absolute entrypoint if both absolute entrypoint and dir are set", + entrypoint: "/dir/to/another/Taskfile.yml", + dir: "/dir/to", + expectedPath: "/dir/to/another/Taskfile.yml", + }, + { + name: "return absolute path of entrypoint if both relative entrypoint and dir are set", + entrypoint: "./dir/to/another/Taskfile.yml", + dir: "./dir/to", + expectedPath: filepath.Join(wd, "dir", "to", "another", "Taskfile.yml"), + }, + { + name: "return absolute path of entrypoint if relative entrypoint and absolute dir are set", + entrypoint: "./dir/to/another/Taskfile.yml", + dir: "/dir/to", + expectedPath: filepath.Join(wd, "dir", "to", "another", "Taskfile.yml"), + }, + { + name: "return working directory if both are empty", + entrypoint: "", + dir: "", + expectedPath: wd, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + path, err := GetSearchPath(tt.entrypoint, tt.dir) + require.NoError(t, err) + require.Equal(t, tt.expectedPath, path) + }) + } +} + func TestSearch(t *testing.T) { t.Parallel() diff --git a/setup.go b/setup.go index 2fc3a6bf1e..2ac45454e7 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" @@ -56,18 +55,15 @@ func (e *Executor) Setup() error { func (e *Executor) getRootNode() (taskfile.Node, error) { node, err := taskfile.NewRootNode(e.Entrypoint, e.Dir, e.Insecure, e.Timeout) - if os.IsNotExist(err) { - return nil, errors.TaskfileNotFoundError{ - URI: fsext.DefaultDir(e.Entrypoint, e.Dir), - Walk: true, - AskInit: true, - } - } if err != nil { + // if the error is a TaskfileNotFoundError and no entrypoint or dir is set, we can suggest to run `task --init` + if errors.As(err, &errors.TaskfileNotFoundError{}) && e.Entrypoint == "" && e.Dir == "" { + return nil, fmt.Errorf("%w. Run `task --init` to create a new Taskfile", err) + } return nil, err } e.Dir = node.Dir() - return node, err + return node, nil } func (e *Executor) readTaskfile(node taskfile.Node) error { diff --git a/taskfile/node_file.go b/taskfile/node_file.go index bbfb28575e..c5fd45c75a 100644 --- a/taskfile/node_file.go +++ b/taskfile/node_file.go @@ -20,10 +20,17 @@ type FileNode struct { func NewFileNode(entrypoint, dir string, opts ...NodeOption) (*FileNode, error) { // Find the entrypoint file 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 errors.Is(err, os.ErrNotExist) { + path, err := fsext.GetSearchPath(entrypoint, dir) + if err != nil { + return nil, err + } + return nil, errors.TaskfileNotFoundError{ + URI: path, + Walk: entrypoint == "", } + } + if err != nil { return nil, err }