Skip to content

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.

go get gitlab.com/phpboyscout/go/workspace
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. Detect and DetectFromCWD both take an afero.Fs, so the walk runs entirely in memory in tests — no temp directories, no touching the real disk. Pass afero.NewOsFs() in production, afero.NewMemMapFs() in tests. See test filesystem walks in memory.
  • Markers are ordered; first match wins. DefaultMarkers is {".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 with ErrNotFound, so a stray path can never scan unboundedly. Override with WithMaxDepth. 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 a depfootprint test.

Start here

API reference lives on pkg.go.dev.