workspace¶
Where is the project root? Nearly every CLI tool needs to answer that before it does
anything else. workspace answers it the way git and go do — walk up from a starting
directory until a marker file appears.
ws, err := workspace.DetectFromCWD(afero.NewOsFs(), workspace.DefaultMarkers)
if err != nil {
return errors.Wrap(err, "not inside a project")
}
fmt.Println("Project root:", ws.Root) // e.g. /home/user/project
fmt.Println("Detected via:", ws.Marker) // e.g. go.mod
Why¶
- The filesystem is injected.
DetectandDetectFromCWDboth take anafero.Fs, so the walk runs entirely in memory in tests — no temp directories, no touching the real disk. Passafero.NewOsFs()in production,afero.NewMemMapFs()in tests. See test filesystem walks in memory. - Markers are ordered; first match wins.
DefaultMarkersis{".gtb/manifest.yaml", "go.mod", ".git"}— a generated-project manifest outranks the Go module root, which outranks the git root. Bring your own set for any ecosystem — see detect other project types. - The walk is bounded. It climbs at most
DefaultMaxDepth(100) parents before giving up withErrNotFound, so a stray path can never scan unboundedly. Override withWithMaxDepth. The full mechanism is in the marker walk. - No framework weight. The entire dependency graph is afero,
cockroachdb/errors, and the standard library — enforced by adepfootprinttest.
Start here¶
- Getting started — detect a project root in a few lines.
- Detect other project types — Node, Rust, Python, monorepos.
- Test filesystem walks in memory — the afero seam.
- The marker walk — precedence, depth bound, and root stop.
API reference lives on pkg.go.dev.