Documentation
¶
Overview ¶
Package props defines the Props dependency container, the central type-safe dependency injection mechanism used throughout GTB.
Props is a concrete struct that aggregates tool metadata, logger, configuration, filesystem (afero.Fs), embedded assets, version info, and error handler into a single value threaded through commands and services. It replaces context.Context-based DI with compile-time type safety.
The package also provides the feature flag system via SetFeatures, Enable, and Disable functional options, controlling which built-in commands (update, init, docs, mcp) are registered at startup. Tool release source metadata for GitHub and GitLab backends is managed through ReleaseSource.
Index ¶
- Constants
- Variables
- func BoolPtr(v bool) *bool
- func ConfigFileSources(snap *config.Snapshot) []string
- func DeclareLinks(prefix string, names ...string)
- func DeclareLinksOn(r features.Registry, prefix string, names ...string) error
- func IsValidConfigLayer(name ConfigLayer) bool
- func LinkedNames(set features.Set, prefix string) []string
- func MarshalYAML(v any) ([]byte, error)
- func RegisterFeature(d FeatureDescriptor)
- func SlogLogger(p *Props) *slog.Logger
- func StatesOf(fs []Feature) []features.State
- func ValidateConfigFormat(format string) error
- func ValidateConfigLayers(layers []ConfigLayer) error
- func ValidateConfigSpec(spec ConfigSpec) error
- func ViewOrNil(p *Props) config.Reader
- type AssetMap
- type AssetProvider
- type Assets
- func (a *Assets) Exists(name string) (fs.FS, error)
- func (a *Assets) For(names ...string) *Assets
- func (a *Assets) Get(name string) fs.FS
- func (a *Assets) Glob(pattern string) ([]string, error)
- func (a *Assets) Merge(others ...*Assets) *Assets
- func (a *Assets) Mount(f fs.FS, prefix string)
- func (a *Assets) Names() []string
- func (a *Assets) Open(name string) (fs.File, error)
- func (a *Assets) ReadDir(name string) ([]fs.DirEntry, error)
- func (a *Assets) Register(name string, fs fs.FS)
- func (a *Assets) SetLogger(l logger.Logger)
- func (a *Assets) Slice() []fs.FS
- func (a *Assets) Stat(name string) (fs.FileInfo, error)
- type BootstrapPolicy
- type ConfigLayer
- type ConfigProvider
- type ConfigSource
- type ConfigSourceState
- type ConfigSourceStatus
- type ConfigSpec
- type DeliveryMode
- type Enumerator
- type EventType
- type Feature
- type FeatureDescriptor
- type FeatureID
- type FeatureKind
- type FeatureState
- type IO
- type KeyCase
- type LoggerProvider
- type MCPConfig
- type MCPMode
- type NoopCollector
- func (NoopCollector) BackendInfo() string
- func (NoopCollector) Close(context.Context) error
- func (NoopCollector) Drop() error
- func (NoopCollector) Enabled() bool
- func (NoopCollector) Flush(context.Context) error
- func (NoopCollector) Track(EventType, string, map[string]string)
- func (NoopCollector) TrackCommand(string, int64, int, map[string]string)
- func (NoopCollector) TrackCommandExtended(string, []string, int64, int, string, map[string]string)
- type Option
- func WithAssets(a *Assets) Option
- func WithCollector(c TelemetryCollector) Option
- func WithConfig(store *config.Store) Option
- func WithErrorHandler(eh errorhandling.ErrorHandler) Option
- func WithFeatures(s features.Snapshot) Option
- func WithFlags(e features.Evaluator) Option
- func WithResolver(r features.Resolver) Option
- func WithSet(s features.Set) Option
- func WithVersion(v version.Info) Option
- type Props
- func (p *Props) ApplyDefaults()
- func (p *Props) GetAssets() *Assets
- func (p *Props) GetCollector() TelemetryCollector
- func (p *Props) GetConfig() *config.Store
- func (p *Props) GetConfigFS() config.FS
- func (p *Props) GetConfigView() *config.View
- func (p *Props) GetErrorHandler() errorhandling.ErrorHandler
- func (p *Props) GetFS() afero.Fs
- func (p *Props) GetFeatures() features.Set
- func (p *Props) GetFlags() features.Evaluator
- func (p *Props) GetIO() IO
- func (p *Props) GetLogLevel() *slog.LevelVar
- func (p *Props) GetLogger() logger.Logger
- func (p *Props) GetTool() Tool
- func (p *Props) GetVersion() version.Info
- func (p *Props) Validate() error
- type ReleaseSource
- type SigningConfig
- type SlackHelp
- type StdIO
- type TeamsHelp
- type TelemetryCollector
- type TelemetryConfig
- type Tool
- type ToolMetadataProvider
- type UpdatePolicy
Examples ¶
Constants ¶
const ( // ChatLinkPrefix prefixes a chat provider link: "chat-claude". ChatLinkPrefix = "chat-" // ForgeLinkPrefix prefixes a forge adapter link: "forge-github". ForgeLinkPrefix = "forge-" )
Link prefixes name the features a generated project declares for the adapters it blank-imports (#81): one per chat provider the author chose and one per forge adapter, so the running tool's feature set records the author's exact choice rather than only which modules happen to be linked.
const ( EventCommandInvocation = telemetrytypes.EventCommandInvocation EventCommandError = telemetrytypes.EventCommandError EventFeatureUsed = telemetrytypes.EventFeatureUsed EventUpdateCheck = telemetrytypes.EventUpdateCheck EventUpdateApplied = telemetrytypes.EventUpdateApplied EventDeletionRequest = telemetrytypes.EventDeletionRequest )
const ( // DeliveryAtLeastOnce deletes spill files after successful send. // Possible duplicates if the ack is lost; no data loss. DeliveryAtLeastOnce = telemetrytypes.DeliveryAtLeastOnce // DeliveryAtMostOnce deletes spill files before sending. // Possible data loss; no duplicates. DeliveryAtMostOnce = telemetrytypes.DeliveryAtMostOnce )
const ( UpdateCmd = FeatureID("update") InitCmd = FeatureID("init") McpCmd = FeatureID("mcp") DocsCmd = FeatureID("docs") AiCmd = FeatureID("ai") DoctorCmd = FeatureID("doctor") ConfigCmd = FeatureID("config") ChangelogCmd = FeatureID("changelog") ManCmd = FeatureID("man") )
const FrameworkBundle = "framework"
FrameworkBundle is the name under which the framework's baseline asset bundle (log defaults, the framework init template) registers.
const PackagePath = "gitlab.com/phpboyscout/go-tool-base/pkg/props"
PackagePath is this package's import path, the ConstPackage every built-in feature descriptor carries. Declared once so a generated import cannot drift from the package it names.
const ReleaseSourceStatic = "static"
ReleaseSourceStatic is the ReleaseSource.Type of the static release channel: releases discovered and retrieved from BaseURL with no forge involved.
const TelemetryCmd = FeatureID("telemetry")
TelemetryCmd is the feature flag for the telemetry command group. Default disabled — tool authors must explicitly enable it.
Variables ¶
var ( // ErrUnknownConfigLayer is a declared layer the framework does not know. ErrUnknownConfigLayer = errors.NewSentinel("gtb.props.unknown_config_layer", "unknown config layer") // ErrDuplicateConfigLayer is a layer declared more than once. ErrDuplicateConfigLayer = errors.NewSentinel("gtb.props.duplicate_config_layer", "duplicate config layer") // ErrConfigLayerOrder is a declared order that breaks one of spec 0204 // D1's constraints. ErrConfigLayerOrder = errors.NewSentinel("gtb.props.config_layer_order", "config layer order") // ErrConfigSource is a source slot declared in a way spec 0204 D15 // refuses. ErrConfigSource = errors.NewSentinel("gtb.props.config_source", "config source declaration") // ErrConfigFormat is an own config format that is not writable. ErrConfigFormat = errors.NewSentinel("gtb.props.config_format", "config format cannot be a tool's own") // ErrConfigKeyCase is a key case other than folded or exact. ErrConfigKeyCase = errors.NewSentinel("gtb.props.config_key_case", "unknown config key case") )
var ( // ErrInvalidDescriptor reports a descriptor missing a required field. ErrInvalidDescriptor = features.ErrInvalidDescriptor // ErrDuplicateFeature reports a second registration for one ID. ErrDuplicateFeature = features.ErrDuplicateFeature // ErrPluginDefaultOn reports a non-builtin feature declaring itself // default-enabled. // // Adding a blank import must change what is *available*, never what is *on*. // Without that rule an import list becomes a behavioural file, and a // downstream that deliberately omits a provider cannot reason about what its // remaining imports switched on behind it. ErrPluginDefaultOn = errors.NewSentinel("gtb.props.plugin_default_on", "props: only builtin features may be default-enabled") )
Functions ¶
func BoolPtr ¶ added in v0.43.0
BoolPtr returns a pointer to v, for the tri-state baselines on Tool (RequireChecksum) that distinguish unset from explicitly false. A generated root command uses it in a struct literal, where a variable would need a package-level declaration.
func ConfigFileSources ¶ added in v0.32.0
ConfigFileSources returns the paths of the loaded file layers in snapshot precedence order, deduplicating multi-document entries. The last element is the highest-precedence loaded file — the same answer Viper's ConfigFileUsed() gave (it reported the last file it loaded), which several consumers (doctor's report, telemetry's data dir, the config command's writable-target resolution) depend on for parity.
func DeclareLinks ¶ added in v0.43.0
DeclareLinks declares one link-kind feature per name under prefix on the default registry. A generated cmd/<name>/chat.go or forge.go calls it from init beside the blank imports it records, so the declaration and the link are the same file. It panics on a duplicate, as any init-time declaration does.
func DeclareLinksOn ¶ added in v0.43.0
DeclareLinksOn is DeclareLinks against a caller's registry. A link defaults on: its presence in the binary is its enablement (spec 0199 OQ3), and it carries no generated-code constant because nothing scaffolds a toggle for it.
func IsValidConfigLayer ¶ added in v0.36.0
func IsValidConfigLayer(name ConfigLayer) bool
IsValidConfigLayer reports whether name is a layer this framework knows.
func LinkedNames ¶ added in v0.43.0
LinkedNames returns the names declared as links under prefix and enabled in set, in the set's order, with the prefix stripped: the chat providers or forge adapters this binary declared it links. Empty when the tool declared none, which a hand-wired tool may not have.
func MarshalYAML ¶ added in v0.46.0
MarshalYAML encodes v as YAML indented by two, the step go/config's format-preserving edits use, so a file the framework writes and the keys later added to it agree.
func RegisterFeature ¶ added in v0.36.0
func RegisterFeature(d FeatureDescriptor)
RegisterFeature declares a feature on the default registry. It is called from the owning package's init, so a blank import is all a consumer needs.
It panics on a bad registration rather than returning an error: this runs at init, where there is no caller to handle a failure, and a silently dropped feature would surface much later as a missing entry in a doctor report or a scaffolded project.
func SlogLogger ¶ added in v0.32.0
SlogLogger adapts p's logger to a *slog.Logger, falling back to a discarding logger when p is nil. The config adapters in pkg/chat and pkg/telemetry both need a slog handle from possibly-nil Props; hosting the conversion here keeps them from carrying identical copies (compare ViewOrNil).
func StatesOf ¶ added in v0.43.0
StatesOf converts the manifest's feature list to the core's states.
func ValidateConfigFormat ¶ added in v0.46.0
ValidateConfigFormat refuses an own format that is not one of the writable four, since init writes the file and config set edits it.
func ValidateConfigLayers ¶ added in v0.46.0
func ValidateConfigLayers(layers []ConfigLayer) error
ValidateConfigLayers refuses a declaration naming an unknown or repeated layer, or breaking one of spec 0204 D1's ordering constraints. Each constraint is a way to make a tool quietly unsafe, so each refusal says which one and why it exists.
func ValidateConfigSpec ¶ added in v0.46.0
func ValidateConfigSpec(spec ConfigSpec) error
ValidateConfigSpec refuses a declaration spec 0204 D1 or D15 refuses: its layers as ValidateConfigLayers does, with each source's name a layer too, and every source a valid, unique, placed slot.
func ViewOrNil ¶ added in v0.32.0
ViewOrNil pins a view of p's store, or returns a true-nil Reader when p or its store is absent. The nil handling is the point: returning a typed (*config.View)(nil) would box a nil pointer into a non-nil interface and silently defeat the cfg == nil guards the read adapters rely on.
Types ¶
type AssetProvider ¶
type AssetProvider interface {
GetAssets() *Assets
}
AssetProvider provides access to embedded assets.
type Assets ¶
type Assets struct {
// contains filtered or unexported fields
}
Assets is a named set of fs.FS bundles that reads as one filesystem: static files shadow by registration order, structured config files merge. It implements fs.FS, fs.ReadDirFS, fs.GlobFS and fs.StatFS.
func NewAssets ¶
NewAssets creates a new Assets wrapper with the given AssetMap pointers. The framework's baseline bundle registers first, so every later bundle — the tool's own, feature bundles applied at root construction — overrides it in the merged structured reads.
func (*Assets) For ¶
For returns a subset of assets identified by the given names — exactly those, with no implicit framework bundle.
func (*Assets) Merge ¶
Merge appends the internal filesystems of the given Assets to the current wrapper and returns it.
func (*Assets) SetLogger ¶ added in v0.43.0
SetLogger wires a logger so malformed merge bundles surface as WARNs instead of vanishing silently. It is deliberately not part of the Assets interface — the framework wires it during root construction via a type assertion, and a nil logger simply keeps the previous silent behaviour.
type BootstrapPolicy ¶ added in v0.29.0
type BootstrapPolicy struct {
// AutoInitialise, when true and the init feature is enabled, runs a
// non-interactive init (writing the default config) instead of failing when
// no config file is found, then continues with the freshly written config.
AutoInitialise bool `json:"auto_initialise,omitempty" yaml:"auto_initialise,omitempty"`
// SkipConfigCheck lists commands whose missing-config hard-fail is relaxed
// to a tolerant load: the pre-run falls back to the embedded default config
// rather than erroring, so the command's own PreRunE can manage bootstrap.
// An entry matches a command when it equals either the command's Name() or
// its full CommandPath() (e.g. "studio" or "krites studio") — the latter
// disambiguates same-named commands at different tree levels. A command may
// also be marked via the setup.SkipConfigCheck annotation; either mechanism
// relaxes the check.
SkipConfigCheck []string `json:"skip_config_check,omitempty" yaml:"skip_config_check,omitempty"`
// AuxiliaryCommands lists commands that take the root pre-run's auxiliary
// fast path: the framework bootstrap — config load, telemetry consent,
// collector wiring, update check — is skipped entirely (only the --debug
// flag is still honoured for logging). Cobra's own generated help,
// completion and __complete commands take this path automatically; the
// list lets a downstream tool extend the set (e.g. for its own
// bootstrap-free plumbing commands) without a framework release.
//
// This is stronger than SkipConfigCheck: an auxiliary command runs with
// props.Config left nil, so it must read nothing from configuration.
// Entries match a command's Name() or full CommandPath(), the same
// semantics as SkipConfigCheck.
AuxiliaryCommands []string `json:"auxiliary_commands,omitempty" yaml:"auxiliary_commands,omitempty"`
}
BootstrapPolicy governs how the framework treats configuration bootstrap and the missing-config gate in the root pre-run. The zero value reproduces the historical behaviour: a missing config file is a hard error when the init feature is enabled.
Neither knob skips the framework bootstrap itself (config load, telemetry, update check) — they only relax the missing-config *outcome*, preserving the "bootstrap always runs" invariant established by the 2026-06-12-bootstrap-prerun-traversal spec.
func (BootstrapPolicy) MatchesAuxiliaryList ¶ added in v0.33.0
func (b BootstrapPolicy) MatchesAuxiliaryList(name, path string) bool
MatchesAuxiliaryList reports whether a command identified by name and path is listed in AuxiliaryCommands, using the same Name()/CommandPath() equality semantics as MatchesSkipList.
func (BootstrapPolicy) MatchesSkipList ¶ added in v0.29.0
func (b BootstrapPolicy) MatchesSkipList(name, path string) bool
MatchesSkipList reports whether a command identified by name and path is listed in SkipConfigCheck. An entry may be a bare Name() or a full CommandPath(); either equality relaxes the missing-config check. Empty entries never match. The annotation half (setup.SkipConfigCheck) is evaluated separately by the root pre-run to avoid a props->setup import cycle.
type ConfigLayer ¶ added in v0.36.0
type ConfigLayer string
ConfigLayer names one layer of the configuration stack.
Which layers a tool wires used to be the framework's decision, inherited wholesale. That stops working as soon as credentials depend on layers: a regulated downstream that must not link a keychain backend, or a CI-only tool with no interactive environment, needs to decline a layer rather than have it wired on its behalf. See spec 0183 D4.
const ( // LayerDefaults is the embedded defaults every tool ships — merged // assets/config.yaml plus each feature bundle's own defaults. LayerDefaults ConfigLayer = "defaults" // LayerFiles is the tool's config files, in ConfigPaths order. LayerFiles ConfigLayer = "files" // LayerProject is a discovered project-local ".<tool>.yaml", subject to the // trust filter. LayerProject ConfigLayer = "project" // LayerEnv is environment variables under the tool's EnvPrefix. LayerEnv ConfigLayer = "env" // LayerFlags is changed CLI flags — the highest-precedence layer. LayerFlags ConfigLayer = "flags" )
func AllConfigLayers ¶ added in v0.36.0
func AllConfigLayers() []ConfigLayer
AllConfigLayers is every layer a tool may declare, in the framework's order. Used to validate a declaration and to enumerate the set in generated output.
func CanonicalConfigLayers ¶ added in v0.46.0
func CanonicalConfigLayers(layers []ConfigLayer) []ConfigLayer
CanonicalConfigLayers returns a copy of layers in the framework's order, which is what a declaration made before spec 0204 resolved to.
func DefaultConfigLayers ¶ added in v0.36.0
func DefaultConfigLayers() []ConfigLayer
DefaultConfigLayers is what a tool wires when it declares nothing: exactly what the framework wired before the set became declarable, in the same order.
The keychain layer is deliberately absent. Wiring it is a decision the host binary makes through a blank import, precisely so a regulated build can omit it and have the linker drop go-keyring entirely; a default that switched it on would take that choice away. See spec 0183 D3 and D9.
type ConfigProvider ¶
ConfigProvider provides access to the live configuration store.
Take this when you need to write configuration, observe changes, or hold a reference across a reload.
type ConfigSource ¶ added in v0.46.0
type ConfigSource struct {
// Name is unique in the stack, a config key segment and a command word:
// it names the slot's layer, its settings and `init config <name>`.
Name string `json:"name" yaml:"name"`
// Kind picks the adapter: vault, consul, aws-s3, file, ...
Kind string `json:"kind" yaml:"kind"`
// Required, when nil, is true: an unreachable or unconfigured slot stops
// the tool (spec 0204 D6).
Required *bool `json:"required,omitempty" yaml:"required,omitempty"`
// Writable, when nil, is the kind's default, which is read-only for every
// kind but keychain (spec 0204 D7, D12).
Writable *bool `json:"writable,omitempty" yaml:"writable,omitempty"`
}
ConfigSource is one declared config source slot.
func (ConfigSource) IsRequired ¶ added in v0.46.0
func (s ConfigSource) IsRequired() bool
IsRequired reports whether the slot's absence stops the tool.
func (ConfigSource) IsWritable ¶ added in v0.46.0
func (s ConfigSource) IsWritable(kindDefault bool) bool
IsWritable reports whether config writes may land in the slot, given its kind's default.
type ConfigSourceState ¶ added in v0.46.0
type ConfigSourceState string
ConfigSourceState is how a declared config source slot fared when the root built the store.
const ( // ConfigSourceBuilt is a slot whose backend is a layer of the store. ConfigSourceBuilt ConfigSourceState = "built" // ConfigSourceUnconfigured is an optional slot nobody configured, left out. ConfigSourceUnconfigured ConfigSourceState = "not configured" // built, left out. ConfigSourceUnavailable ConfigSourceState = "unavailable" )
type ConfigSourceStatus ¶ added in v0.46.0
type ConfigSourceStatus struct {
Slot ConfigSource
State ConfigSourceState
// Writable and Sensitive are the built layer's, after the slot's own
// writable setting and the kind's default.
Writable bool
Sensitive bool
// Credential names the link of the slot's credential chain that answered,
// as its factory reported it; empty when the kind reports none.
Credential string
// Err is why an unavailable slot was left out.
Err string
}
ConfigSourceStatus records one declared slot's outcome (spec 0204 D10), so doctor can report the stack without building it again. A required slot that fails stops the root instead, so it never has a status.
type ConfigSpec ¶ added in v0.46.0
type ConfigSpec struct {
// Layers is the stack by name, lowest precedence first: the order is the
// precedence. Empty resolves to DefaultConfigLayers.
Layers []ConfigLayer `json:"layers,omitempty" yaml:"layers,omitempty"`
// Format is the format of the tool's own config file, the one init writes
// and config set edits: yaml, toml, json or hcl. Empty is yaml. A format
// other than yaml must be linked (spec 0204 D2).
Format string `json:"format,omitempty" yaml:"format,omitempty"`
// Sources are the config source slots the tool declares, each placed by
// name in Layers (spec 0204 D15). Where each connects is runtime
// configuration under config.sources.<name>, never declared here.
Sources []ConfigSource `json:"sources,omitempty" yaml:"sources,omitempty"`
// KeyCase is how every Store the tool builds compares keys. Empty is
// folded (spec 0209 D1).
KeyCase KeyCase `json:"key_case,omitempty" yaml:"key_case,omitempty"`
}
ConfigSpec is how a tool declares its configuration stack. See spec 0204.
func (ConfigSpec) WithKeyCase ¶ added in v0.48.0
func (s ConfigSpec) WithKeyCase(opts []config.StoreOption) []config.StoreOption
WithKeyCase appends the store option the spec's key case needs to opts. It is the one place a Store learns the tool's mode, so no Store the tool builds can disagree with another about a key (spec 0209 D2).
type DeliveryMode ¶
type DeliveryMode = telemetrytypes.DeliveryMode
DeliveryMode controls the delivery guarantee for telemetry events. It is an alias for telemetrytypes.DeliveryMode; see EventType for why the canonical type lives in the leaf package.
type Enumerator ¶ added in v0.43.0
type Enumerator interface {
Descriptors() []features.Descriptor
}
Enumerator is anything that lists descriptors: a features.Snapshot or a features.Set.
type EventType ¶
type EventType = telemetrytypes.EventType
EventType identifies the category of telemetry event. It is an alias for telemetrytypes.EventType — the canonical type lives in the dependency-free pkg/telemetrytypes leaf so that pkg/telemetry can share it without importing pkg/props. Re-exported here (with the constants below) so commands can reference event types through props without importing pkg/telemetry.
type Feature ¶
type Feature struct {
ID FeatureID `json:"cmd" yaml:"cmd"`
Enabled bool `json:"enabled" yaml:"enabled"`
}
Feature represents the state of a feature (Enabled/Disabled).
The ID field is serialised as "cmd", which is deliberate rather than an oversight: the field was named Cmd when every feature was a command, and Tool.Features is persisted in generator manifests and tool configs. Renaming the Go identifier is a compile-time event a downstream fixes once; renaming the wire key would silently stop older manifests loading. So the identifier moved and the tag did not.
func SetFeatures ¶
func SetFeatures(mutators ...FeatureState) []Feature
SetFeatures applies a series of mutators to the standard default set.
Example ¶
package main
import (
"fmt"
"gitlab.com/phpboyscout/go-tool-base/pkg/props"
)
func main() {
features := props.SetFeatures(
props.Disable(props.InitCmd),
props.Enable(props.AiCmd),
props.Enable(props.TelemetryCmd),
)
for _, f := range features {
if f.Enabled {
fmt.Println("enabled:", f.ID)
}
}
}
Output:
type FeatureDescriptor ¶ added in v0.36.0
type FeatureDescriptor struct {
// ID is the feature's identity and its config/manifest name.
ID FeatureID
// ConstName is the exported Go identifier naming this feature's constant,
// as it must appear in generated source (e.g. "AiCmd").
//
// It cannot be derived from ID ("mcp" yields "McpCmd", not "MvpCmd") and
// the generator emits it verbatim, so it is carried rather than computed.
// A feature with no exported constant cannot be scaffolded.
ConstName string
// ConstPackage is the import path of the package declaring ConstName.
//
// Built-in constants live in props, but a plugin's do not (the forge
// features are declared by pkg/setup/forge) and generated source must
// qualify the reference correctly. Carrying the path here is what keeps a
// feature's generator handling a property of its registration rather than a
// special case somewhere downstream.
ConstPackage string
// Kind classifies the feature. See [FeatureKind].
Kind FeatureKind
// Default reports whether the feature is enabled when a tool expresses no
// preference. Only [KindBuiltin] may set it; see [ErrPluginDefaultOn].
Default bool
// Dynamic reports whether a flag backend may override the static state at
// evaluation time (spec 0199 D7). Every built-in is static.
Dynamic bool
// Order places a non-builtin feature among its kind: features with an
// Order sort by it after the built-ins, features without one sort after
// those by kind and ID. The forges use it so a chooser lists them as the
// framework intends (GitHub first, the default backend) rather than
// alphabetically. Zero means unordered.
Order int
}
FeatureDescriptor is everything the framework needs to know about a feature.
It is registered once, by the package that owns the feature, so a blank import is genuinely all it takes to make a feature visible everywhere: the doctor report, config validation, and the generator's handling. It implements features.Descriptor and adds the two generated-code identifiers, which are GTB's concern rather than the core's (spec 0199 D2).
func DescriptorsIn ¶ added in v0.43.0
func DescriptorsIn(s Enumerator) []FeatureDescriptor
DescriptorsIn returns GTB's descriptors in s, in the snapshot's order. It is what a reader with a Set or a Snapshot in hand uses in place of the removed process-wide enumerations (spec 0199 D3).
func (FeatureDescriptor) DefaultOn ¶ added in v0.43.0
func (d FeatureDescriptor) DefaultOn() bool
DefaultOn implements features.Descriptor.
func (FeatureDescriptor) FeatureID ¶ added in v0.43.0
func (d FeatureDescriptor) FeatureID() features.ID
FeatureID implements features.Descriptor.
func (FeatureDescriptor) FeatureKind ¶ added in v0.43.0
func (d FeatureDescriptor) FeatureKind() features.Kind
FeatureKind implements features.Descriptor.
func (FeatureDescriptor) IsDynamic ¶ added in v0.43.0
func (d FeatureDescriptor) IsDynamic() bool
IsDynamic implements features.Descriptor.
func (FeatureDescriptor) Rank ¶ added in v0.43.0
func (d FeatureDescriptor) Rank() (int, bool)
Rank implements features.Ranked: built-ins keep the order the constant block declares, a feature with an Order follows them in that order, and everything else is unranked and sorts by kind and ID.
type FeatureID ¶ added in v0.36.0
FeatureID identifies a built-in feature that can be enabled or disabled.
type FeatureKind ¶ added in v0.36.0
FeatureKind classifies what a feature is, so "every forge" becomes a query rather than a list somebody has to remember to update. It is the core's Kind; the constants are GTB's.
const ( // KindBuiltin is a feature the framework itself ships and enumerates. KindBuiltin FeatureKind = "builtin" // KindForge is a forge integration contributed by a blank-imported package. KindForge FeatureKind = "forge" // KindLink is a feature whose only effect is a blank import: declaring it // is what links the backend (the OS keychain), so its presence in a binary // is its enablement, and the generator toggles it by writing or removing // the file that imports it (spec 0199 OQ3). It has no runtime switch and // never appears in SetFeatures. KindLink FeatureKind = "link" )
type FeatureState ¶
FeatureState is a functional option that mutates the list of features.
func DefaultFeatures ¶
func DefaultFeatures() []FeatureState
DefaultFeatures is the list of features enabled by default.
It is derived from the feature registry rather than hand-written, so a feature declares its own default alongside its identity and the two cannot drift. Only builtin features may be default-enabled — see ErrPluginDefaultOn.
func Disable ¶
func Disable(cmd FeatureID) FeatureState
Disable returns a FeatureState that disables the given command.
func Enable ¶
func Enable(cmd FeatureID) FeatureState
Enable returns a FeatureState that enables the given command.
type IO ¶ added in v0.43.0
type IO interface {
In() io.Reader
Out() io.Writer
Err() io.Writer
// Interactive reports whether In is a terminal a person is typing at.
Interactive() bool
// Accessible reports whether forms should run as line prompts rather than
// a TUI: a screen reader, TERM=dumb, or a test.
Accessible() bool
}
IO is where an invocation reads and writes, and whether that is a person. It is one field on Props rather than three so the terminal is a thing Props has, and so a replacement (a recorded session, a remote terminal, an accessible-mode wrapper) swaps in whole (spec 0198 D1).
type KeyCase ¶ added in v0.48.0
type KeyCase string
KeyCase is a config stack's key mode: folded to lower case, or exact.
type LoggerProvider ¶
LoggerProvider provides access to the application logger.
type MCPConfig ¶ added in v0.43.0
type MCPConfig struct {
Mode MCPMode `json:"mode,omitempty" yaml:"mode,omitempty"`
}
MCPConfig is the tool author's MCP posture, rendered into the generated root from properties.mcp in the manifest. The running binary never reads the manifest; changing the mode is a regenerate and a rebuild.
type MCPMode ¶ added in v0.43.0
type MCPMode string
MCPMode is the publication mode the tool's MCP server uses: compact (three discovery tools) or direct (one native tool per command). The zero value is compact, so a tool that states nothing publishes compactly (spec 0201 D3).
type NoopCollector ¶ added in v0.17.0
type NoopCollector struct{}
NoopCollector is a zero-behaviour TelemetryCollector. It is the default value of Props.Collector so the "always non-nil" invariant holds even before the root bootstrap resolves the real collector (e.g. on the init and help paths) and for Props constructed directly as a struct literal (tests, cmd/e2e).
The concrete *telemetry.Collector from pkg/telemetry supersedes it once the root PersistentPreRunE runs; NoopCollector reports Enabled() == false so the telemetry flush is correctly skipped while it is in place.
func (NoopCollector) BackendInfo ¶ added in v0.17.0
func (NoopCollector) BackendInfo() string
BackendInfo reports the disabled noop backend.
func (NoopCollector) Close ¶ added in v0.17.0
func (NoopCollector) Close(context.Context) error
Close is a no-op and never errors.
func (NoopCollector) Drop ¶ added in v0.17.0
func (NoopCollector) Drop() error
Drop is a no-op and never errors.
func (NoopCollector) Enabled ¶ added in v0.17.0
func (NoopCollector) Enabled() bool
Enabled always reports false: a noop collector never actively collects.
func (NoopCollector) Flush ¶ added in v0.17.0
func (NoopCollector) Flush(context.Context) error
Flush is a no-op and never errors.
func (NoopCollector) Track ¶ added in v0.17.0
func (NoopCollector) Track(EventType, string, map[string]string)
Track is a no-op.
func (NoopCollector) TrackCommand ¶ added in v0.17.0
TrackCommand is a no-op.
func (NoopCollector) TrackCommandExtended ¶ added in v0.17.0
TrackCommandExtended is a no-op.
type Option ¶ added in v0.34.0
type Option func(*Props)
Option configures a Props built by New.
func WithAssets ¶ added in v0.34.0
WithAssets sets the embedded assets bundle.
func WithCollector ¶ added in v0.34.0
func WithCollector(c TelemetryCollector) Option
WithCollector sets the telemetry collector. When omitted, New defaults a NoopCollector so callers can invoke Props.Collector unconditionally.
func WithConfig ¶ added in v0.34.0
WithConfig sets the live configuration store. It is normally left unset at construction — the root pre-run loads and assigns it — so New does not require it (see Validate's init-path exemption).
func WithErrorHandler ¶ added in v0.34.0
func WithErrorHandler(eh errorhandling.ErrorHandler) Option
WithErrorHandler sets the structured error handler. When omitted, New defaults one built from the logger and Tool.Help.
func WithFeatures ¶ added in v0.43.0
WithFeatures names the snapshot the feature set is resolved from. When omitted, New snapshots the default registry, which is every feature this binary's imports declared.
func WithFlags ¶ added in v0.43.0
WithFlags sets the request-time Evaluator. When omitted it is Features.
func WithResolver ¶ added in v0.43.0
WithResolver replaces the default Resolver (defaults, then the tool's states in order) for a consumer with another precedence.
func WithSet ¶ added in v0.43.0
WithSet supplies a resolved Set directly, bypassing resolution; a test or a consumer with its own Set implementation uses this.
func WithVersion ¶ added in v0.34.0
WithVersion sets the runtime/ldflags version info.
type Props ¶
type Props struct {
Tool Tool
Logger logger.Logger
// Config is the live configuration store. It owns reloads, so it is the
// thing to hold; a *config.View taken from it is pinned to one snapshot and
// goes stale the moment the file changes.
//
// Read through Config.View(), which is the read surface. Where several
// values must agree with each other, use Config.With so they all resolve
// against the same snapshot rather than straddling a reload.
Config *config.Store
Assets *Assets
FS afero.Fs
Version version.Info
ErrorHandler errorhandling.ErrorHandler
// Collector is always non-nil once the root command tree is built: the
// bootstrap defaults it to a NoopCollector and later replaces it with the
// resolved *telemetry.Collector. Consumers may call it unconditionally.
Collector TelemetryCollector
// IO is this invocation's streams and whether a person is at them. Nil
// reads as StdIO{}, the process's; the root fills it from the command once
// (spec 0198 D1). Read through GetIO.
IO IO
// Features is what this tool has: the resolved enabled state of every
// feature this binary declared, and the contributions of the enabled ones
// (spec 0199 D3). New resolves it from a snapshot of the default registry
// and Tool.Features; ApplyDefaults does the same for a literal Props. Every
// static decision (which commands exist, what doctor inventories) reads it.
Features features.Set
// Flags answers whether a feature is on for a request. It defaults to
// Features (every answer static); a service wires a features.Dynamic over a
// backend for the features that opt in (spec 0199 D10).
Flags features.Evaluator
// LogLevel is the level the root's --debug and the config's log.level
// move, for a command that builds its own slog handler (the MCP server
// does: its stdout carries the protocol, so it logs to stderr on its own
// handler). Per root, like everything else here (spec 0202 D4). Read
// through GetLogLevel.
LogLevel *slog.LevelVar
// SourceStatuses is how each declared config source slot fared when the
// root built the store, in declared order (spec 0204 D10).
SourceStatuses []ConfigSourceStatus
// contains filtered or unexported fields
}
Props is the primary dependency injection container for GTB applications. When writing functions that accept Props, consider whether a narrow interface (LoggerProvider, ConfigProvider, etc.) would suffice.
func New ¶ added in v0.34.0
New constructs a Props from the required dependencies plus options for the rest, applying the framework defaults and validating the nil-field contract. It is the blessed construction path: the required fields (a named Tool, a Logger, and a filesystem) must be present, while the optional fields (Collector, ErrorHandler, Version) are defaulted when omitted. Config is left nil unless supplied — the pre-run assigns it, and the init path runs with it nil by design (see Validate).
Most of the codebase still builds Props as a struct literal; New exists so a downstream tool (or a test) can get the nil-field contract checked at one place instead of discovering a nil Logger as a panic deep in a command.
Example (Features) ¶
package main
import (
"fmt"
"github.com/spf13/afero"
"gitlab.com/phpboyscout/go-tool-base/pkg/logger"
"gitlab.com/phpboyscout/go-tool-base/pkg/props"
)
func main() {
tool := props.Tool{
Name: "mytool",
Features: props.SetFeatures(
props.Enable(props.AiCmd),
),
}
p, err := props.New(tool, logger.NewNoop(), afero.NewMemMapFs())
if err != nil {
panic(err)
}
fmt.Println("AI:", p.Features.Enabled(props.AiCmd))
fmt.Println("Init:", p.Features.Enabled(props.InitCmd))
}
Output: AI: true Init: true
func (*Props) ApplyDefaults ¶ added in v0.34.0
func (p *Props) ApplyDefaults()
applyDefaults fills in the optional fields that the framework guarantees are non-nil: Collector (a NoopCollector), ErrorHandler (built from the logger and Tool.Help), and Version (an empty, development-flavoured Info). It never touches Config — that is the caller's / pre-run's responsibility. Idempotent and nil-safe on the optional fields, so it is safe to call from both New and the root bootstrap.
func (*Props) GetCollector ¶ added in v0.18.0
func (p *Props) GetCollector() TelemetryCollector
GetCollector returns the telemetry collector. It is always non-nil once the root command tree is built, so consumers may call it unconditionally.
func (*Props) GetConfigFS ¶ added in v0.32.0
GetConfigFS adapts the application filesystem for the config store.
Props.FS stays a full afero.Fs — OsFs live, MemMapFs under test — because that is how the rest of GTB operates. config defines its own narrower interface rather than depending on afero, so the two are bridged here in one place instead of at every construction site.
func (*Props) GetConfigView ¶ added in v0.32.0
GetConfigView returns a read surface pinned to the current snapshot.
Named GetConfigView rather than Config because Props already has a Config field and Go forbids a method sharing its name.
Take one per logical operation and let it go — holding one across a reload silently serves stale values. Reading several related keys from one view is the point: they are guaranteed to come from the same snapshot.
func (*Props) GetErrorHandler ¶
func (p *Props) GetErrorHandler() errorhandling.ErrorHandler
GetErrorHandler returns the error handler.
func (*Props) GetFeatures ¶ added in v0.43.0
GetFeatures returns the resolved feature set. New and ApplyDefaults populate Features; a literal Props that skipped both (a test building one command) resolves here from the default registry, dropping any enable of a feature nothing declared, the way ApplyDefaults would. Nil-safe by design, like GetIO: a command reads its gate through this and never dereferences the field.
func (*Props) GetFlags ¶ added in v0.43.0
GetFlags returns the request-time evaluator, defaulting to the feature set.
func (*Props) GetIO ¶ added in v0.43.0
GetIO returns the invocation's streams, the process's when none were set.
func (*Props) GetLogLevel ¶ added in v0.43.0
GetLogLevel returns the level var the root moves, creating it on a Props nobody defaulted so every reader shares one. A nil Props answers with a var nothing will ever move.
func (*Props) GetVersion ¶
GetVersion returns the version information.
func (*Props) Validate ¶ added in v0.34.0
Validate reports whether the required Props fields are present. It is the enforcement half of the nil-field contract that used to live only in doc comments.
Required: a named Tool, a Logger, and a filesystem — every command path reads these. NOT required: Config. It is deliberately nil on the init path (init is what CREATES the configuration) and until the root pre-run loads it, so a blanket "Config must be non-nil" check would be wrong; code that touches Config on those paths must guard it (Store.View is not nil-receiver-safe — use ViewOrNil). The optional fields are defaulted by applyDefaults, so Validate does not police them.
type ReleaseSource ¶
type ReleaseSource struct {
Type string `json:"type" yaml:"type"`
Host string `json:"host" yaml:"host"`
Owner string `json:"owner" yaml:"owner"`
Repo string `json:"repo" yaml:"repo"`
Private bool `json:"private" yaml:"private"`
// BaseURL is the static release channel's one setting (spec 0203 D1): the
// HTTPS location the pointer and the per-tag manifests are published under.
// Read only when Type is ReleaseSourceStatic; the forge fields are unused then.
BaseURL string `json:"base_url,omitempty" yaml:"base_url,omitempty"`
}
ReleaseSource identifies the platform and repository where the tool's releases are published. Used by the self-update system to check for and download new versions. Supported types: "github", "gitlab", "bitbucket", "gitea", "codeberg", "direct".
Type and Host address the connection, and become a forge.Endpoint at construction. Owner and Repo name what a provider is asked to operate on, and are passed per call rather than baked into the provider.
Provider-specific settings — the direct provider's url_template and its siblings — are NOT here. They live in the tool's configuration under the source's own subtree (direct.url_template, and so on), which is what lets two sources of one type coexist. A Params map used to carry them; spec 0192 removed it when go/forge v0.12.0 stopped reading one.
type SigningConfig ¶ added in v0.12.0
type SigningConfig struct {
// EmbeddedKeys are ASCII-armored public keys baked into the binary,
// typically via //go:embed in an internal trustkeys package. They are
// the embedded trust anchor the SelfUpdater uses to verify the
// signature over checksums.txt. Not serialised — keys are compile-time
// build artefacts, never runtime config.
EmbeddedKeys [][]byte `json:"-" yaml:"-"`
// AnchorOnlyKeys are ASCII-armored public keys that join the trust set,
// and so the embedded-versus-WKD cross-check, but never validate a
// release signature. A rotation-authority key published beside the
// release keys belongs here rather than in EmbeddedKeys.
AnchorOnlyKeys [][]byte `json:"-" yaml:"-"`
// RequireChecksum is the tool author's baseline for checksum enforcement,
// consulted when neither config nor environment supplies one. It is a
// pointer so "unset" is distinguishable from "explicitly false": nil
// leaves the framework default (permissive), and a tool wanting
// fail-closed verification from day one sets it true.
//
// It lives here, beside the other tool-author baselines, rather than as a
// package variable in pkg/setup. The framework constructs the updater, so
// a tool cannot pass an option to it — but it does own its Props, and
// process-wide mutable state is not a configuration mechanism. Runtime
// config (`update.require_checksum`) still overrides it.
RequireChecksum *bool `json:"-" yaml:"-"`
}
SigningConfig holds tool-author configuration for self-update signature verification (Phase 2 of the remote-update-checksum spec). The zero value is safe: with no embedded keys and no external key email configured, signature verification is governed entirely by setup.DefaultRequireSignature (default false) and stays dormant.
type SlackHelp ¶ added in v0.32.0
type SlackHelp struct {
Team string `json:"team" yaml:"team"`
Channel string `json:"channel" yaml:"channel"`
}
SlackHelp provides Slack channel contact details as a support message. It implements the errorhandling.HelpConfig interface, which the go/errorhandling module defines but deliberately ships no implementations for — where a team's support channel lives is a framework/tool concern, not something an error-reporting library should have an opinion about.
Assign it to Tool.Help; the scaffolded root command does this for tools generated with `--help-type slack`.
func (SlackHelp) SupportMessage ¶ added in v0.32.0
SupportMessage renders the Slack contact line, or an empty string when the channel is not fully configured (an empty message suppresses help output).
type StdIO ¶ added in v0.43.0
type StdIO struct {
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
// AccessibleMode asks for line prompts; the root's --accessible flag and
// GTB_ACCESSIBLE set it.
AccessibleMode bool
}
StdIO is the process's streams, or the ones a caller supplies. The zero value is stdin, stdout and stderr.
func (StdIO) Accessible ¶ added in v0.43.0
Accessible reports the explicit switch, or TERM=dumb, which is huh's own rule for switching to line prompts, applied here so the two agree.
func (StdIO) Interactive ¶ added in v0.43.0
Interactive reports whether In is a terminal. A reader that is not an *os.File is not, and neither is a file that is not a terminal: /dev/null is a character device, which is why the old ModeCharDevice check said yes to a stdin nobody was typing at.
type TeamsHelp ¶ added in v0.32.0
type TeamsHelp struct {
Team string `json:"team" yaml:"team"`
Channel string `json:"channel" yaml:"channel"`
}
TeamsHelp provides Microsoft Teams channel contact details as a support message. See SlackHelp for the rationale behind these living in GTB rather than in the errorhandling module.
func (TeamsHelp) SupportMessage ¶ added in v0.32.0
SupportMessage renders the Teams contact line, or an empty string when the channel is not fully configured (an empty message suppresses help output).
type TelemetryCollector ¶
type TelemetryCollector interface {
// Track records a telemetry event. No-op when the collector is disabled.
Track(eventType EventType, name string, extra map[string]string)
// TrackCommand records a command invocation with duration and exit code.
TrackCommand(name string, durationMs int64, exitCode int, extra map[string]string)
// TrackCommandExtended records a command invocation with full context including
// arguments and error message. When ExtendedCollection is disabled on the
// collector, args and errMsg are silently dropped — callers do not need to
// check the flag themselves.
TrackCommandExtended(name string, args []string, durationMs int64, exitCode int, errMsg string, extra map[string]string)
// Flush sends all buffered events to the backend and clears the buffer.
// Checks for and sends spill files before flushing the current buffer.
Flush(ctx context.Context) error
// BackendInfo returns a human-readable description of the active backend
// (e.g. "otlp (otlp-gateway-prod-gb-south-1.grafana.net)", "noop", "file").
BackendInfo() string
// Close flushes pending events and shuts down the backend gracefully.
// Must be called on process exit to ensure backends like OTLP flush
// their internal batch queues.
Close(ctx context.Context) error
// Drop clears all buffered events and deletes any spill files without sending.
Drop() error
// Enabled reports whether the collector is actively collecting (as opposed
// to a no-op disabled collector). Reflects the resolved decision from
// config, env override, or tool-author ForceEnabled.
Enabled() bool
}
TelemetryCollector is the interface through which commands emit telemetry events. Defined here (not in pkg/telemetry) to avoid an import cycle. The concrete implementation is *telemetry.Collector from pkg/telemetry. Props.Collector is always non-nil — when telemetry is disabled it is a noop.
type TelemetryConfig ¶
type TelemetryConfig struct {
// Endpoint is the HTTP JSON endpoint to POST events to.
// Ignored when OTelEndpoint is set or when Backend is non-nil.
Endpoint string `json:"endpoint,omitempty" yaml:"endpoint,omitempty"`
// OTelEndpoint is the OTLP/HTTP base URL (e.g. "https://collector:4318").
// Takes precedence over Endpoint when set.
OTelEndpoint string `json:"otel_endpoint,omitempty" yaml:"otel_endpoint,omitempty"`
// OTelHeaders are HTTP headers sent with every OTLP request (e.g. auth tokens).
OTelHeaders map[string]string `json:"otel_headers,omitempty" yaml:"otel_headers,omitempty"`
// OTelInsecure disables TLS for the OTLP endpoint — use only for local collectors.
OTelInsecure bool `json:"otel_insecure,omitempty" yaml:"otel_insecure,omitempty"`
// Backend is an optional factory for a custom telemetry backend.
// Typed as func(*Props) any to avoid importing pkg/telemetry from pkg/props.
// The returned value must implement telemetry.Backend — pkg/cmd/root performs
// the type assertion. Takes precedence over Endpoint and OTelEndpoint when set.
// Not serialisable — set programmatically in tool setup.
Backend func(*Props) any `json:"-" yaml:"-"`
// DeletionRequestor is an optional factory for a custom GDPR deletion requestor.
// Typed as func(*Props) any to avoid importing pkg/telemetry from pkg/props.
// The returned value must implement telemetry.DeletionRequestor — pkg/cmd/root
// performs the type assertion. If nil, falls back to sending a
// data.deletion_request event through the existing backend.
// Not serialisable — set programmatically in tool setup.
DeletionRequestor func(*Props) any `json:"-" yaml:"-"`
// ForceEnabled bypasses the user consent prompt and enables telemetry
// unconditionally. The telemetry enable/disable/reset commands remain
// functional so users can still inspect status, but collection cannot be
// turned off. Intended for enterprise tools deployed in controlled
// environments where telemetry is a contractual requirement.
// When set, the first-run consent prompt is suppressed.
ForceEnabled bool `json:"force_enabled,omitempty" yaml:"force_enabled,omitempty"`
// ExtendedCollection enables collection of command arguments and error messages
// in telemetry events. Default: false. When false, args and errors are never
// recorded regardless of what callers pass to TrackCommandExtended.
//
// Enable this only in closed enterprise environments where users are
// contractually bound by security policies. In public-facing tools, leave
// this disabled to preserve user privacy.
ExtendedCollection bool `json:"extended_collection,omitempty" yaml:"extended_collection,omitempty"`
// DeliveryMode controls the delivery guarantee. Default: DeliveryAtLeastOnce.
DeliveryMode DeliveryMode `json:"delivery_mode,omitempty" yaml:"delivery_mode,omitempty"`
// Metadata is additional key/value pairs included in every event.
// Useful for custom dimensions like environment name or deployment tier.
Metadata map[string]string `json:"metadata,omitempty" yaml:"metadata,omitempty"`
}
TelemetryConfig holds tool-author telemetry declarations. It is embedded in Tool and specifies where and how to send telemetry. The end-user's opt-in state is stored in the config file under telemetry.enabled and telemetry.local_only — endpoints are not user-configurable.
type Tool ¶
type Tool struct {
Name string `json:"name" yaml:"name"`
Summary string `json:"summary" yaml:"summary"`
Description string `json:"description" yaml:"description"`
Features []Feature `json:"features" yaml:"features"`
Help errorhandling.HelpConfig `json:"-" yaml:"-"`
// Bootstrap groups config-bootstrap lifecycle policy — auto-initialisation
// and per-command relaxation of the missing-config gate. Zero value is
// safe and reproduces historical behaviour. See BootstrapPolicy.
Bootstrap BootstrapPolicy `json:"bootstrap,omitempty" yaml:"bootstrap,omitempty"`
// Config declares the tool's configuration stack and its order. See
// ConfigSpec and ResolveConfigLayers.
Config ConfigSpec `json:"config,omitempty" yaml:"config,omitempty"`
// ConfigLayers declares which layers of the configuration stack this tool
// wires, resolved in the framework's order whatever order it lists.
//
// Deprecated: use Config.Layers, whose order is the precedence (spec 0204
// D13).
ConfigLayers []ConfigLayer `json:"config_layers,omitempty" yaml:"config_layers,omitempty"`
// MCP is the publication mode of the tool's MCP server; the zero value is
// compact. See MCPConfig.
MCP MCPConfig `json:"mcp,omitempty" yaml:"mcp,omitempty"`
// ReleaseSource is the source of truth for the tool's releases; see
// ReleaseSource for the supported types.
ReleaseSource ReleaseSource `json:"release_source" yaml:"release_source"`
// ReleaseProvider, when non-nil, is the release backend the self-update
// subsystem uses, taking precedence over ReleaseSource.Type registry
// lookup. It is a runtime-only dependency-injection seam — for tests
// (see go/forge/test) and custom/embedded providers — and is
// never serialised. Production tools leave it nil and are resolved from the
// release registry by ReleaseSource.Type as before.
ReleaseProvider forge.Provider `json:"-" yaml:"-"`
// UpdatePolicy is the tool author's baseline self-update posture
// (disabled / prompt / enabled). The zero value normalises to
// UpdatePolicyDisabled — the framework default — so a tool does not
// force updates out of the box. Overridable at runtime by the
// `update.policy` config key. See ResolveUpdatePolicy.
UpdatePolicy UpdatePolicy `json:"update_policy,omitempty" yaml:"update_policy,omitempty"`
// UpdateCheckInterval is the tool author's baseline throttle between
// self-update checks. The zero value means "use the framework default"
// (setup.DefaultCheckInterval, 24h) — it is NOT interpreted as
// "check every run". Overridable at runtime by the `update.check_interval`
// config key (where "0"/"0s" does mean every run). See
// setup.ResolveCheckInterval.
UpdateCheckInterval time.Duration `json:"update_check_interval,omitempty" yaml:"update_check_interval,omitempty"`
// Telemetry holds tool-author telemetry configuration.
// Zero-value is safe — tools that don't set it are unaffected.
Telemetry TelemetryConfig `json:"telemetry" yaml:"telemetry"`
// Signing holds tool-author self-update signature configuration.
// Zero-value is safe — verification stays dormant until keys and/or
// require_signature are configured.
Signing SigningConfig `json:"-" yaml:"-"`
// EnvPrefix is the environment variable prefix used by the config package.
// When set, only env vars starting with this prefix (e.g., "GTB_") are
// considered for config overrides. Empty means no prefix (all env vars match).
EnvPrefix string `json:"env_prefix,omitempty" yaml:"env_prefix,omitempty"`
// InstallHint is shown when a feature requires a full release binary
// (e.g. embedded docs) that the running binary lacks. Set it to your
// tool's recommended installation command. When empty, a generic
// message referencing Name is shown — the framework never assumes a
// particular installer.
InstallHint string `json:"install_hint,omitempty" yaml:"install_hint,omitempty"`
}
Tool holds metadata about the CLI tool: its identity, feature flags, release source for self-updates, help channel configuration, and telemetry settings. It is embedded in Props and passed to all commands.
func (Tool) ConfigFilename ¶ added in v0.46.0
ConfigFilename is the base name of the tool's own config file: config plus its format's extension.
func (Tool) GetReleaseSource ¶
GetReleaseSource returns the release source type, owner, and repo.
func (Tool) ResolveConfigLayers ¶ added in v0.36.0
func (t Tool) ResolveConfigLayers() []ConfigLayer
ResolveConfigLayers returns the layers a tool wires, lowest precedence first: Config.Layers as declared, else the deprecated ConfigLayers in the framework's order, else the framework default.
An empty declaration means "unstated", not "none". A tool wanting genuinely no layers has nothing to configure and no reason to build a store, so reading empty as an opt-out would turn an omitted field into a silently broken tool.
func (Tool) ResolvedConfigSpec ¶ added in v0.46.0
func (t Tool) ResolvedConfigSpec() ConfigSpec
ResolvedConfigSpec is Config with its layer list resolved, the declaration the store builds from.
func (Tool) WiresConfigLayer ¶ added in v0.36.0
func (t Tool) WiresConfigLayer(layer ConfigLayer) bool
WiresConfigLayer reports whether the tool wires the named layer.
type ToolMetadataProvider ¶
type ToolMetadataProvider interface {
GetTool() Tool
}
ToolMetadataProvider provides access to tool configuration and metadata.
type UpdatePolicy ¶ added in v0.18.0
type UpdatePolicy string
UpdatePolicy governs how the self-update check behaves when a newer release is found. It is the tool author's baseline (the Tool.UpdatePolicy field), overridable at runtime by the `update.policy` config key. See https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0087-forced-update-feature.
const ( // UpdatePolicyDisabled never auto-updates or prompts: it only logs that an // update is available (and a persistent out-of-date WARN). This is the // framework default (the zero value normalises to it), so a tool built on // GTB does nothing surprising out of the box. UpdatePolicyDisabled UpdatePolicy = "disabled" // UpdatePolicyPrompt asks the user to update when one is available; they may // decline and continue with their command. The GTB CLI itself uses this. UpdatePolicyPrompt UpdatePolicy = "prompt" // UpdatePolicyEnabled blocks every command until the tool is updated when an // update is available (the historical forced-update behaviour, now explicit // and opt-in). UpdatePolicyEnabled UpdatePolicy = "enabled" )
func ResolveUpdatePolicy ¶ added in v0.18.0
func ResolveUpdatePolicy(toolDefault UpdatePolicy, configValue string) UpdatePolicy
ResolveUpdatePolicy computes the effective update policy. A valid `update.policy` config value wins over the tool's baseline; an empty or invalid config value falls back to the baseline; an empty or invalid baseline falls back to the framework default (UpdatePolicyDisabled). Resolution is case-insensitive. `--ci` bypass is handled by the caller and is out of scope here.
func (UpdatePolicy) Normalize ¶ added in v0.18.0
func (p UpdatePolicy) Normalize() UpdatePolicy
Normalize trims, lowercases, and maps the empty policy (the zero value / framework default) to UpdatePolicyDisabled. An unknown non-empty value is returned trimmed/lowercased so UpdatePolicy.Valid can reject it.
func (UpdatePolicy) Valid ¶ added in v0.18.0
func (p UpdatePolicy) Valid() bool
Valid reports whether p is one of the three defined policies. The empty string is NOT valid (it is a sentinel for "unset"); callers wanting the default-applied value use UpdatePolicy.Normalize.