workspace

package
v1.10.5 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 2, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package workspace provides project root detection by walking up from a starting directory to find marker files (.gtb/manifest.yaml, go.mod, .git).

This is a utility package — it has no integration with Props or the command lifecycle. Tool authors use it to scope commands to the current project context.

Usage

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)

Index

Examples

Constants

View Source
const DefaultMaxDepth = 100

DefaultMaxDepth is the maximum number of parent directories to search before giving up. Prevents runaway scanning on deeply nested paths.

Variables

View Source
var DefaultMarkers = []string{
	".gtb/manifest.yaml",
	"go.mod",
	".git",
}

DefaultMarkers is the standard set of marker files used to detect project boundaries. Checked in order — the first match wins.

View Source
var ErrNotFound = errors.New("workspace not found: no marker file detected")

ErrNotFound is returned when no marker file is found before reaching the filesystem root or the max depth.

Functions

This section is empty.

Types

type Option

type Option func(*detectConfig)

Option configures the Detect function.

func WithMaxDepth

func WithMaxDepth(depth int) Option

WithMaxDepth sets the maximum number of parent directories to search. Default: DefaultMaxDepth (100).

type Workspace

type Workspace struct {
	// Root is the absolute path to the project root directory.
	Root string
	// Marker is the marker file or directory that was found
	// (e.g. ".gtb/manifest.yaml", "go.mod", ".git").
	Marker string
}

Workspace represents a detected project boundary.

func Detect

func Detect(fs afero.Fs, startDir string, markers []string, opts ...Option) (*Workspace, error)

Detect walks up from startDir looking for any of the given marker files. Returns the first match. Returns ErrNotFound if no marker is found before reaching the filesystem root or the max depth.

Markers are checked in order at each directory level — the first match wins. This means ".gtb/manifest.yaml" takes precedence over "go.mod" when using DefaultMarkers.

Example
package main

import (
	"fmt"

	"github.com/spf13/afero"

	"github.com/phpboyscout/go-tool-base/pkg/workspace"
)

func main() {
	fs := afero.NewMemMapFs()

	// Create a project with a go.mod
	_ = afero.WriteFile(fs, "/home/user/project/go.mod", []byte("module example"), 0o644)
	_ = fs.MkdirAll("/home/user/project/pkg/cmd", 0o755)

	// Detect from a nested directory
	ws, err := workspace.Detect(fs, "/home/user/project/pkg/cmd", workspace.DefaultMarkers)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}

	fmt.Println("Root:", ws.Root)
	fmt.Println("Marker:", ws.Marker)
}
Output:
Root: /home/user/project
Marker: go.mod
Example (CustomMarkers)
package main

import (
	"fmt"

	"github.com/spf13/afero"

	"github.com/phpboyscout/go-tool-base/pkg/workspace"
)

func main() {
	fs := afero.NewMemMapFs()

	_ = afero.WriteFile(fs, "/project/package.json", []byte("{}"), 0o644)
	_ = fs.MkdirAll("/project/src/components", 0o755)

	ws, err := workspace.Detect(fs, "/project/src/components", []string{"package.json"})
	if err != nil {
		return
	}

	fmt.Println("Root:", ws.Root)
}
Output:
Root: /project

func DetectFromCWD

func DetectFromCWD(fs afero.Fs, markers []string, opts ...Option) (*Workspace, error)

DetectFromCWD is a convenience that calls Detect starting from the current working directory.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL