Description
I wrote a little package called cdup that walks a directory and each of its ancestors looking for a file/dir. This is useful e.g. to find the root of a git dir (look for .git
) or a Go module (look for go.mod
).
I aimed to write it using fs.FS
and use os.DirFS
as a bridge to the OS filesystem as needed, perhaps inside a wrapper function.
I may just be holding it wrong, but I encountered a few surprising bumps.
A typical invocation is cdup.Find(pwd, ".git")
. The obvious but wrong translation of this is cdup.FindIn(os.DirFS("/"), pwd, ".git")
.
What is the correct root path to pass to os.DirFS
on Windows? filepath.VolumeName
seems like the right API to use. (I think?)
Then we must convert pwd
to a relative path using filepath.Rel
. On Windows, we also need to call filepath.ToSlash
.
So I believe that the correct code is something like:
root := "/"
if vol := filepath.VolumeName(dir); vol != "" {
root = vol
}
rel := filepath.Rel(root, dir)
rel = filepath.ToSlash(rel)
cdup.FindIn(os.DirFS(root), rel, ".git")
This is a surprising amount of non-obvious work for what seems like it should be a trivial bridge between fs.FS and the OS filesystem.
I'm not sure exactly what the fix should be here. Docs/examples? New package filepath API to get the OS fs root for a path? New package os API to construct a root filesystem (and expose the root for use with filepath.Rel)?