Detect other project types¶
DefaultMarkers targets Go projects (.gtb/manifest.yaml, go.mod, .git). For any
other ecosystem, pass your own marker slice — the walk is otherwise identical.
Supply your own markers¶
Both Detect and DetectFromCWD take the marker list as a plain []string:
// Node
ws, err := workspace.Detect(fs, startDir, []string{"package.json"})
// Rust
ws, err := workspace.Detect(fs, startDir, []string{"Cargo.toml"})
// Python
ws, err := workspace.Detect(fs, startDir, []string{"pyproject.toml", "setup.py"})
Each entry is joined onto the directory being checked and Stat-ed, so a marker can be a
nested path (.gtb/manifest.yaml) as well as a top-level file or directory (.git).
Order matters — first match wins¶
Within a single directory the markers are checked in the order you give them, and the first that exists wins. Put the most specific marker first:
// A generated-project manifest should outrank a bare git root.
markers := []string{".myapp/project.toml", ".git"}
This is exactly why DefaultMarkers lists .gtb/manifest.yaml before go.mod before
.git: a generated project is more specific than "any Go module", which is more specific
than "any git repo".
Stop at the first boundary you hit¶
The walk climbs one directory at a time and returns as soon as any marker matches at the
current level. In a monorepo where an inner package has its own go.mod and the repo root
has .git, detecting from inside the package with DefaultMarkers stops at the package's
go.mod — the nearest boundary — not the repo root. Choose the marker set that matches the
boundary you actually want, or see
find the repository root, not the nearest module for the case
where both boundaries carry the same marker.
Bound the climb¶
By default the walk gives up after DefaultMaxDepth (100) parents. Tighten it when you
only ever expect a shallow layout:
If no marker is found within the bound, you get workspace.ErrNotFound. Note that the
depth counts parents only — the start directory is always checked, and a negative
depth checks nothing at all and always returns ErrNotFound. Clamp any depth that comes
from a flag or a config key to zero before passing it in.
Filter out empty entries before you pass the list¶
A marker list assembled at runtime — split from a config value, read from a file — can
easily contain an empty string, and an empty string matches the directory being inspected.
Detection then returns the start directory itself with Marker: "", which looks like
success:
The other input edge cases, and what each one does, are listed in what happens when an argument is wrong.