project

package
v7.8.0 Latest Latest
Warning

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

Go to latest
Published: Aug 11, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Index

Constants

View Source
const (

	// StoreRootEnv names the environment variable that overrides the configured
	// central store root with an explicit one, so a test harness or an agent can
	// point tk — `tk serve` above all — at a throwaway store without touching the
	// real one. It is an environment variable rather than a cobra flag on purpose:
	// pkg/ticket and internal/mcp resolve config directly and never see cobra's
	// flags, and the env is the lever a harness has over a subprocess it spawns.
	StoreRootEnv = "TK_STORE_ROOT"
)

Variables

This section is empty.

Functions

func CentralProjectDir

func CentralProjectDir(projectName string) (string, error)

CentralProjectDir returns <centralRoot>/tickets/<projectName>.

func CentralRegistered added in v7.7.1

func CentralRegistered(cfg Config, name string) bool

CentralRegistered reports whether a project is registered with the central store. Presence in the merged config is not enough: `store: central` lives in the shared config alone — saveLocal writes only `path` per project once a central root is set — so a project whose shared config is missing (never cloned, or lost in a sync) merges to an entry with an empty store. Write authorization, read resolution, and the unregistered markers all key on this one predicate so they describe the same set of projects.

func CentralStoreRoot

func CentralStoreRoot() (string, error)

CentralStoreRoot returns the central ticket store root directory, or the TK_STORE_ROOT override when set — which is answered without reading any config file, so no configured store is touched. Returns an error if central_root is not configured — run `tk init` first.

func ConfigPath

func ConfigPath() (string, error)

ConfigPath returns ~/.ticket/config.yaml, or the override root's own .ticket/config.yaml when TK_STORE_ROOT is set. Keeping the local config inside the override root mirrors the production layout and keeps an isolated run from reading or writing the machine's shared one.

func DetectProjectPath

func DetectProjectPath(cwd string) string

DetectProjectPath returns git top-level directory if available; otherwise cwd.

func IsConfigured

func IsConfigured() bool

IsConfigured returns true if ~/.ticket/config.yaml exists and has central_root set, or if TK_STORE_ROOT is set — an override is configuration in itself, so an isolated run needs no local config file at all.

A set-but-unusable override still reports configured: it is a broken store root, not a missing configuration, and the CLI refuses it before this gate with an error naming the variable. Reporting it unconfigured here would send the reader to `tk init`, and falling through to the local config would resolve the real store.

func ResolveName

func ResolveName(cfg Config, cwd string, explicit string) (name string, source string)

ResolveName resolves project name with precedence: 1) explicit override 2) config path mapping 3) git remote name 4) directory name

Names are sanitized to prevent path traversal (no ".." or path separators).

func ResolveWorkDir

func ResolveWorkDir(ticketsDir string, cfg Config) string

ResolveWorkDir returns the project's real repo working directory for a tickets directory. With the central store, ticketsDir is <centralRoot>/tickets/<project> and its parent is the central tickets dir, NOT the repo — so the project's recorded `path` from config is used when available. Falls back to the parent of ticketsDir (correct for a local .tickets/ at the repo root) when the project has no configured path.

func Save

func Save(cfg Config) error

Save writes the config to both local and shared files, splitting fields appropriately. Local gets top-level fields + per-project path. Shared gets per-project store, auto_link, auto_close, registered_at.

func SharedConfigPath

func SharedConfigPath() (string, error)

SharedConfigPath returns <central_root>/config.yaml.

func SpawnCommand added in v7.8.0

func SpawnCommand() (string, error)

SpawnCommand returns the TUI's spawn_command template, plus the error that made it unreadable. It reads ~/.ticket/config.yaml directly for the same reason VerifyAllow does: the template is handed to `sh -c`, so whoever supplies it runs code as the machine owner, and neither the synced shared config nor a TK_STORE_ROOT root the caller named may be that source.

An empty template is the caller's signal to use the built-in default, so a sandbox with no home config gets the machine owner's default rather than a template of its own.

func StoreRootOverride added in v7.8.0

func StoreRootOverride() (string, bool, error)

StoreRootOverride returns the TK_STORE_ROOT store root, whether the variable is set at all, and the error a set-but-unusable value is. A value that is not an absolute path is that error and never falls back to the configured root: silently resolving the real central store from a broken override is the exact failure the override exists to prevent.

Presence is tested with LookupEnv rather than by comparing the value to "", because the empty string is a set-but-unusable value, not an absent one: a harness exporting a variable that expanded to nothing has named a store root tk cannot resolve, and treating it as unset would resolve the configured one.

func ValidName added in v7.7.1

func ValidName(name string) bool

ValidName reports whether a project name is safe to join into a filesystem path. Names containing path separators or ".." traverse out of the root they are joined into, and filepath.Join cleans those segments instead of failing; "" and "." collapse onto the root itself, rooting a project store at a directory no project owns. Names reach here from config, git remotes, and directory basenames as well as from the project half of a namespaced ticket ID.

func VerifyAllow added in v7.8.0

func VerifyAllow() ([]string, error)

VerifyAllow returns the argv[0] allow-list a ticket's verify commands are checked against, plus the error that made it unreadable. It reads ~/.ticket/config.yaml directly rather than the merged config on purpose: the shared config lives inside the synced tickets repo, so whatever can push a malicious verify command there could widen the list meant to refuse it in the same push.

It reads the home path directly rather than ConfigPath for the same reason: TK_STORE_ROOT deliberately does not move this one setting. The override root belongs to whoever set the variable — a harness, or an agent — so relocating the allow-list there would let a sandbox widen it (`verify_allow: [sh]`), and a fresh sandbox with no config at all would silently restore the defaults over a list the machine owner had narrowed. The allow-list always comes from the machine owner's own config, so a sandbox can neither widen nor narrow it.

Three states are kept apart so the control fails closed. A local config that cannot be read or parsed — partial write, merge conflict markers, bad permissions — returns an empty list and the error, so nothing runs and the refusal can say why rather than silently restoring defaults over a list the user had narrowed. A `verify_allow` that is present but empty returns empty, which is how a user refuses everything. Only a genuinely absent key falls back to defaultVerifyAllow.

Types

type Config

type Config struct {
	CentralRoot  string `yaml:"central_root,omitempty" json:"central_root,omitempty"`
	GitEmail     string `yaml:"git_email,omitempty" json:"git_email,omitempty"`
	GitName      string `yaml:"git_name,omitempty" json:"git_name,omitempty"`
	DefaultStore string `yaml:"default_store,omitempty" json:"default_store,omitempty"`
	SyncInterval string `yaml:"sync_interval,omitempty" json:"sync_interval,omitempty"`
	// SpawnCommand: read it via project.SpawnCommand(). The value here is the
	// merged one, whose local half is the override root's config under
	// TK_STORE_ROOT, and it exists for Save's round-trip only — never as a read
	// path, because the template is the string handed to `sh -c` as the machine
	// owner.
	SpawnCommand string `yaml:"spawn_command,omitempty" json:"spawn_command,omitempty"`
	// VerifyAllow: read it via project.VerifyAllow(), for the same reason and on
	// the same terms — merged from a half the override root may own, kept here so
	// Save round-trips it, and never a read path, because the list decides which
	// programs run as the machine owner.
	VerifyAllow VerifyAllowList          `yaml:"verify_allow,omitempty" json:"verify_allow,omitempty"`
	Projects    map[string]ProjectConfig `yaml:"projects"`
}

Config stores tk project configuration (merged view of local + shared).

func Load

func Load() (Config, error)

Load reads both local (~/.ticket/config.yaml) and shared (<central_root>/config.yaml) configs, merging them into a single Config. Local fields (central_root, git_email, git_name, default_store, sync_interval, spawn_command, per-project path) come from local config. Shared fields (per-project store, auto_link, auto_close, registered_at) come from shared config. Missing files are not errors — returns what's available.

func (*Config) UpsertProject

func (cfg *Config) UpsertProject(name string, project ProjectConfig)

UpsertProject inserts or updates a project entry.

type ProjectConfig

type ProjectConfig struct {
	Path         string `yaml:"path,omitempty" json:"path,omitempty"`
	Store        string `yaml:"store,omitempty" json:"store,omitempty"`
	AutoLink     bool   `yaml:"auto_link" json:"auto_link"`
	AutoClose    bool   `yaml:"auto_close" json:"auto_close"`
	RegisteredAt string `yaml:"registered_at,omitempty" json:"registered_at,omitempty"`
}

ProjectConfig stores per-project settings.

type VerifyAllowList added in v7.8.0

type VerifyAllowList []string

VerifyAllowList is the verify_allow setting. Absent and present-but-empty mean different things — fall back to the default, versus refuse everything — so it defines IsZero rather than letting omitempty collapse them: a save round-trip (project registration, for one) must not turn a user's explicit `verify_allow: []` back into an unset key, which would silently restore the defaults.

func (VerifyAllowList) IsZero added in v7.8.0

func (l VerifyAllowList) IsZero() bool

IsZero reports whether omitempty should drop the field. Only an absent list is dropped.

Jump to

Keyboard shortcuts

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