mirror of
https://github.com/go-task/task.git
synced 2025-11-23 22:24:45 +02:00
203 lines
6.7 KiB
Go
203 lines
6.7 KiB
Go
package fsext
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/go-task/task/v3/internal/filepathext"
|
|
"github.com/go-task/task/v3/internal/sysinfo"
|
|
)
|
|
|
|
// DefaultDir will return the default directory given an entrypoint or
|
|
// directory. If the directory is set, it will ensure it is an absolute path and
|
|
// return it. If the entrypoint is set, but the directory is not, it will leave
|
|
// the directory blank. If both are empty, it will default the directory to the
|
|
// current working directory.
|
|
func DefaultDir(entrypoint, dir string) string {
|
|
// If the directory is set, ensure it is an absolute path
|
|
if dir != "" {
|
|
var err error
|
|
dir, err = filepath.Abs(dir)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return dir
|
|
}
|
|
|
|
// If the entrypoint and dir are empty, we default the directory to the current working directory
|
|
if entrypoint == "" {
|
|
wd, err := os.Getwd()
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return wd
|
|
}
|
|
|
|
// If the entrypoint is set, but the directory is not, we leave the directory blank
|
|
return ""
|
|
}
|
|
|
|
// ResolveDir returns an absolute path to the directory that the task should be
|
|
// run in. If the entrypoint and dir are BOTH set, then the Taskfile will not
|
|
// sit inside the directory specified by dir and we should ensure that the dir
|
|
// is absolute. Otherwise, the dir will always be the parent directory of the
|
|
// resolved entrypoint, so we should return that parent directory.
|
|
func ResolveDir(entrypoint, resolvedEntrypoint, dir string) (string, error) {
|
|
if entrypoint != "" && dir != "" {
|
|
return filepath.Abs(dir)
|
|
}
|
|
return filepath.Dir(resolvedEntrypoint), 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
|
|
// possible filenames. Otherwise, it walks up the file tree starting at the
|
|
// given directory and performs a search in each directory for the possible
|
|
// filenames until it finds a match or reaches the root directory. If the
|
|
// entrypoint and directory are both empty, it defaults the directory to the
|
|
// current working directory and performs a recursive search starting there. If
|
|
// a match is found, the absolute path to the file is returned with its
|
|
// directory. If no match is found, an error is returned.
|
|
func Search(entrypoint, dir string, possibleFilenames []string) (string, error) {
|
|
var err error
|
|
if entrypoint != "" {
|
|
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return entrypoint, nil
|
|
}
|
|
if dir == "" {
|
|
dir, err = os.Getwd()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
entrypoint, err = SearchPathRecursively(dir, possibleFilenames)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return entrypoint, nil
|
|
}
|
|
|
|
// SearchAll 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
|
|
// possible filenames and add it to a list of matches. It then walks up the file
|
|
// tree starting at the given directory and performs a search in each directory
|
|
// for the possible filenames until it finds a match or reaches the root
|
|
// directory. If the entrypoint and directory are both empty, it defaults the
|
|
// directory to the current working directory and performs a recursive search
|
|
// starting there. If matches are found, the absolute path to each file is added
|
|
// to the list and returned.
|
|
func SearchAll(entrypoint, dir string, possibleFilenames []string) ([]string, error) {
|
|
var err error
|
|
var entrypoints []string
|
|
if entrypoint != "" {
|
|
entrypoint, err = SearchPath(entrypoint, possibleFilenames)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
entrypoints = append(entrypoints, entrypoint)
|
|
}
|
|
if dir == "" {
|
|
dir, err = os.Getwd()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
paths, err := SearchNPathRecursively(dir, possibleFilenames, -1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return append(entrypoints, paths...), nil
|
|
}
|
|
|
|
// SearchPath will check if a file at the given path exists or not. If it does,
|
|
// it will return the path to it. If it does not, it will search for any files
|
|
// at the given path with any of the given possible names. If any of these match
|
|
// a file, the first matching path will be returned. If no files are found, an
|
|
// error will be returned.
|
|
func SearchPath(path string, possibleFilenames []string) (string, error) {
|
|
// Get file info about the path
|
|
fi, err := os.Stat(path)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// If the path exists and is a regular file, device, symlink, or named pipe,
|
|
// return the absolute path to it
|
|
if fi.Mode().IsRegular() ||
|
|
fi.Mode()&os.ModeDevice != 0 ||
|
|
fi.Mode()&os.ModeSymlink != 0 ||
|
|
fi.Mode()&os.ModeNamedPipe != 0 {
|
|
return filepath.Abs(path)
|
|
}
|
|
|
|
// If the path is a directory, check if any of the possible names exist
|
|
// in that directory
|
|
for _, filename := range possibleFilenames {
|
|
alt := filepathext.SmartJoin(path, filename)
|
|
if _, err := os.Stat(alt); err == nil {
|
|
return filepath.Abs(alt)
|
|
}
|
|
}
|
|
|
|
return "", os.ErrNotExist
|
|
}
|
|
|
|
// SearchPathRecursively walks up the directory tree starting at the given
|
|
// path, calling the Search function in each directory until it finds a matching
|
|
// file or reaches the root directory. On supported operating systems, it will
|
|
// also check if the user ID of the directory changes and abort if it does.
|
|
func SearchPathRecursively(path string, possibleFilenames []string) (string, error) {
|
|
paths, err := SearchNPathRecursively(path, possibleFilenames, 1)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if len(paths) == 0 {
|
|
return "", os.ErrNotExist
|
|
}
|
|
return paths[0], nil
|
|
}
|
|
|
|
// SearchNPathRecursively walks up the directory tree starting at the given
|
|
// path, calling the Search function in each directory and adding each matching
|
|
// file that it finds to a list until it reaches the root directory or the
|
|
// length of the list exceeds n. On supported operating systems, it will also
|
|
// check if the user ID of the directory changes and abort if it does.
|
|
func SearchNPathRecursively(path string, possibleFilenames []string, n int) ([]string, error) {
|
|
var paths []string
|
|
|
|
owner, err := sysinfo.Owner(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for n == -1 || len(paths) < n {
|
|
fpath, err := SearchPath(path, possibleFilenames)
|
|
if err == nil {
|
|
paths = append(paths, fpath)
|
|
}
|
|
|
|
// Get the parent path/user id
|
|
parentPath := filepath.Dir(path)
|
|
parentOwner, err := sysinfo.Owner(parentPath)
|
|
if err != nil {
|
|
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) {
|
|
return paths, nil
|
|
}
|
|
|
|
owner = parentOwner
|
|
path = parentPath
|
|
}
|
|
|
|
return paths, nil
|
|
}
|