github.com/grafana/tanka@v0.26.1-0.20240506093700-c22cfc35c21a/pkg/jsonnet/jpath/dirs.go (about) 1 package jpath 2 3 import ( 4 "os" 5 "path/filepath" 6 "runtime" 7 ) 8 9 // Dirs returns the project-root (root) and environment directory (base) 10 func Dirs(path string) (root string, base string, err error) { 11 root, err = FindRoot(path) 12 if err != nil { 13 return "", "", err 14 } 15 16 base, err = FindBase(path, root) 17 if err != nil { 18 return root, "", err 19 } 20 21 return root, base, err 22 } 23 24 // FindRoot returns the absolute path of the project root, being the directory 25 // that directly holds `tkrc.yaml` if it exists, otherwise the directory that 26 // directly holds `jsonnetfile.json` 27 func FindRoot(path string) (dir string, err error) { 28 start, err := FsDir(path) 29 if err != nil { 30 return "", err 31 } 32 33 // root path based on os 34 stop := "/" 35 if runtime.GOOS == "windows" { 36 stop = filepath.VolumeName(start) + "\\" 37 } 38 39 // try tkrc.yaml first 40 root, err := FindParentFile("tkrc.yaml", start, stop) 41 if err == nil { 42 return root, nil 43 } 44 45 // otherwise use jsonnetfile.json 46 root, err = FindParentFile("jsonnetfile.json", start, stop) 47 if _, ok := err.(ErrorFileNotFound); ok { 48 return "", ErrorNoRoot 49 } else if err != nil { 50 return "", err 51 } 52 53 return root, nil 54 } 55 56 // FindBase returns the absolute path of the environments base directory, the 57 // one which directly holds the entrypoint file. 58 func FindBase(path string, root string) (string, error) { 59 dir, err := FsDir(path) 60 if err != nil { 61 return "", err 62 } 63 64 filename, err := Filename(path) 65 if err != nil { 66 return "", err 67 } 68 69 base, err := FindParentFile(filename, dir, root) 70 71 if _, ok := err.(ErrorFileNotFound); ok { 72 return "", ErrorNoBase{filename: filename} 73 } else if err != nil { 74 return "", err 75 } 76 77 return base, nil 78 } 79 80 // FindParentFile traverses the parent directory tree for the given `file`, 81 // starting from `start` and ending in `stop`. If the file is not found an error is returned. 82 func FindParentFile(file, start, stop string) (string, error) { 83 files, err := os.ReadDir(start) 84 if err != nil { 85 return "", err 86 } 87 88 if dirContainsFile(files, file) { 89 return start, nil 90 } else if start == stop { 91 return "", ErrorFileNotFound{file} 92 } 93 return FindParentFile(file, filepath.Dir(start), stop) 94 } 95 96 // dirContainsFile returns whether a file is included in a directory. 97 func dirContainsFile(files []os.DirEntry, filename string) bool { 98 for _, f := range files { 99 if f.Name() == filename { 100 return true 101 } 102 } 103 return false 104 } 105 106 // FsDir returns the most inner directory of path, as reported by the local 107 // filesystem 108 func FsDir(path string) (string, error) { 109 path, err := filepath.Abs(path) 110 if err != nil { 111 return "", err 112 } 113 114 fi, err := os.Stat(path) 115 if err != nil { 116 return "", err 117 } 118 119 if fi.IsDir() { 120 return path, nil 121 } 122 123 return filepath.Dir(path), nil 124 }