package config import ( "fmt" "os" "path/filepath" ) // RootDir walks from the working directory upward, looking for searchName as a // regular file. depth limits how many parent directories are checked (0 = CWD only). func RootDir(searchName string, depth ...int) (string, error) { cwd, err := os.Getwd() if err != nil { return "", err } depthVal := 0 if len(depth) > 0 { depthVal = depth[0] } path, err := walkRootDir(searchName, cwd, depthVal) if err != nil { return "", err } if path == "" { return "", fmt.Errorf("RootDir checked %d directories, no '%s' file found", depthVal+1, searchName) } return filepath.Abs(path) } // MustRootDir is like [RootDir] but panics on error. func MustRootDir(searchName string, depth ...int) string { dir, err := RootDir(searchName, depth...) if err != nil { panic(err) } return dir } func walkRootDir(searchName, path string, reverseDepth int) (string, error) { candidate := filepath.Join(path, searchName) info, err := os.Stat(candidate) if err == nil { if info.IsDir() { return "", fmt.Errorf("RootDir found directory %q, expected a regular file", candidate) } return path, nil } if reverseDepth > 0 { parent := filepath.Dir(path) if parent == path { return "", nil } return walkRootDir(searchName, parent, reverseDepth-1) } return "", nil }