generator

package
v0.18.0 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 52 Imported by: 0

Documentation

Overview

Package generator implements the code generation engine that powers project scaffolding, command generation, and regeneration from manifest definitions.

The core Generator type orchestrates a shared pipeline: asset generation, command registration, child re-registration, manifest updates, and documentation output. It operates on a manifest-first architecture where .gtb/manifest.yaml is the single source of truth for project structure.

Key entry points are Generator.GenerateSkeleton for new projects, [Generator.GenerateCommand] for adding commands, and Generator.RegenerateProject for rebuilding registration files from an existing manifest.

Index

Constants

View Source
const (
	DefaultFileMode = 0o644
	DefaultDirMode  = 0o755
)
View Source
const (
	// DefaultCICDComponentSource is the default include base for the
	// phpboyscout/cicd components. It is overridable via the manifest
	// `ci.component_source` value (O1) so a mirrored/self-hosted downstream
	// can repoint the include base; the default works out-of-the-box.
	DefaultCICDComponentSource = "gitlab.com/phpboyscout/cicd"

	// CICDComponentVersion is the phpboyscout/cicd component version the
	// scaffold pins (go-lint, go-test, go-security, goreleaser,
	// zensical-pages, renovate-self). Mirror the framework's own pin.
	CICDComponentVersion = "v0.10.5"

	// ReleaserPleaserComponentVersion is the apricote/releaser-pleaser/run
	// component version the scaffold pins. The component does not support
	// floating tags, so this is always a full version (O7).
	ReleaserPleaserComponentVersion = "v0.8.0"
)

CICD component pins for the scaffolded GitLab pipeline.

These are kept in LOCKSTEP with the framework's own root .gitlab-ci.yml (resolved O2 of the 2026-06-15 generator-gitlab-ci-refresh spec): when the framework upgrades its phpboyscout/cicd component pins, bump these in the same change. The scaffolded renovate config (extending the cicd preset) then keeps the pins current for the downstream tool.

The releaser-pleaser component is pinned separately because it lives under apricote/, not phpboyscout/cicd, and is referenced $CI_SERVER_FQDN-relative (instance-local) per O7.

Variables

View Source
var (
	// ErrNotGoToolBaseProject is the placeholder-free sentinel for "no
	// .gtb/manifest.yaml here". Call sites attach the offending path via
	// errors.Wrapf so errors.Is keeps matching.
	ErrNotGoToolBaseProject      = errors.New("the current project is not a gtb project (.gtb/manifest.yaml not found)")
	ErrParentPathNotFound        = errors.New("parent path not found in manifest")
	ErrModuleNotFound            = errors.New("could not find module name in go.mod")
	ErrFuncNotFound              = errors.New("target function not found")
	ErrParentCommandFileNotFound = errors.New("parent command file not found")
)
View Source
var BreakingChanges = map[string]string{
	"v2.10.0": "Breaking changes to Assets interface and command signatures. Please refer to the migration guide.",
}

BreakingChanges is a map of version strings to descriptions of breaking changes introduced in that version. The keys should be valid semantic version strings (e.g., "v2.10.0"). The values are messages displayed to the user when they upgrade across these versions.

View Source
var ErrCommandProtected = errors.New("command is protected")
View Source
var ErrInvalidInput = errors.New("invalid generator input")

ErrInvalidInput is the sentinel wrapped by every Validate* failure. Discriminate with errors.Is in callers that need to distinguish validation failures from other error shapes.

View Source
var ErrInvalidPackageName = errors.Newf("invalid package name")
View Source
var ToggleableFeatures = []string{
	"init", "update", "mcp", "docs", "doctor", "changelog", "ai", "config", "telemetry",
}

ToggleableFeatures is the set of built-in features that `gtb enable <feature>` and `gtb disable <feature>` can flip in a generated project's manifest. It is the nine props.FeatureCmd values the generator already understands via `generate project --features`.

keychain is intentionally excluded: it is a build-time blank-import decision (the scaffolded cmd/<name>/keychain.go), not a runtime feature flag, so it is changed by adding/removing that file, not by a manifest toggle.

Functions

func EncodeManifestFile added in v0.17.0

func EncodeManifestFile(fs afero.Fs, manifestPath string, m *Manifest) error

EncodeManifestFile serialises m to the given manifest.yaml path on fs using the streaming yaml encoder with the canonical two-space indent. It is the single encode helper for the encoder-based write sites (writeManifest, persistProjectHashes, the skeleton hash refresh), so the Create+SetIndent+Encode boilerplate cannot drift.

func ManifestPathFor added in v0.17.0

func ManifestPathFor(projectPath string) string

ManifestPathFor returns the canonical .gtb/manifest.yaml path under the given project root.

func MarshalManifestFile added in v0.17.0

func MarshalManifestFile(fs afero.Fs, manifestPath string, m *Manifest, mode os.FileMode) error

MarshalManifestFile serialises m with yaml.Marshal and writes it to the given manifest.yaml path on fs with the supplied file mode. This is the buffered (whole-document) write flavour used by the update/scan/query/flag sites; it differs from EncodeManifestFile only in that it produces the yaml.Marshal default indentation, preserving each site's existing on-disk output.

func PascalCase

func PascalCase(s string) string

func ValidateCIComponentSource added in v0.18.0

func ValidateCIComponentSource(source string) error

ValidateCIComponentSource accepts an empty string (meaning "use the framework default") and otherwise requires a bare host/path component source such as `gitlab.com/phpboyscout/cicd`. The value is interpolated verbatim into an unquoted YAML include path in the scaffolded .gitlab-ci.yml, so it is restricted to a strict character class (alphanumerics, `.`, `-`, `_`, `/`) and rejected if it carries a URL scheme, whitespace, control characters, or template delimiters.

func ValidateCommandName added in v0.17.0

func ValidateCommandName(name string) error

ValidateCommandName enforces the naming rule for generated commands — kebab-case plus underscore, a lowercase letter first, at most 64 characters: ^[a-z][a-z0-9_-]{0,63}$. Path separators and dots are rejected explicitly (before the character-class check) because the name flows into filepath.Join(path, "pkg", "cmd", name) and FS.RemoveAll — this rule is the gate that forecloses path traversal from CLI flags and tampered manifests alike.

func ValidateDescription

func ValidateDescription(desc string) error

ValidateDescription enforces a bounded-length, control-character-free description that is safe to interpolate into YAML/TOML string values and Markdown prose. The rule explicitly forbids `{{` / `}}` as a belt-and-braces guard: text/template does not re-parse interpolated data, so this is not exploitable today, but matching the pattern lets a future change (e.g. switching to html/template with its `{{`-as-action reparsing) remain safe.

func ValidateEnvPrefix

func ValidateEnvPrefix(prefix string) error

ValidateEnvPrefix accepts an empty string (meaning "no prefix") and otherwise requires an upper-snake-case prefix matching `^[A-Z][A-Z0-9_]{0,31}$`. Shell metacharacters are excluded by the class; length is bounded so the rendered env-var name stays below POSIX limits.

func ValidateFeatureName added in v0.18.0

func ValidateFeatureName(name string) error

ValidateFeatureName rejects any name that is not one of the toggleable built-in features (see ToggleableFeatures). Used by `gtb enable <feature>` / `gtb disable <feature>` so an unknown name fails fast with the valid set listed, rather than silently writing a junk manifest entry.

func ValidateFlagName added in v0.17.0

func ValidateFlagName(name string) error

ValidateFlagName enforces the naming rule for a generated command's flag: kebab-case, a lowercase letter first, at most 64 characters (^[a-z][a-z0-9-]{0,63}$). The name becomes a Go identifier in the generated options struct and a cobra flag registration, so the same constraint as command names applies.

func ValidateFlagType added in v0.17.0

func ValidateFlagType(flagType string) error

ValidateFlagType rejects a flag type the command generator can't render. The empty string and "string" are accepted (both map to a string flag); every other value must be one the generator's flagFuncMap knows. Without this gate an unknown type silently degrades to a string flag in the generated code.

func ValidateHost

func ValidateHost(host string) error

ValidateHost enforces an RFC 1123 hostname (optionally with `:port`). Punycode labels (`xn--...`) are accepted; raw Unicode labels are rejected — callers that need an internationalised host must supply the punycode form explicitly so homoglyph attacks fail visibly at input time rather than in a rendered URL.

func ValidateManifest

func ValidateManifest(m *Manifest) error

ValidateManifest runs every user-influenced field of a loaded Manifest through the validators above. Used by regenerate and manifest-update paths so a tampered manifest fails fast before driving file writes.

Only [Manifest.Properties.Name] is unconditionally required — a manifest missing the tool name is structurally broken. Other fields are optional in the YAML schema and are validated only when populated; empty fields short-circuit to nil, matching the forgiving behaviour of the fine-grained validators above.

func ValidateName

func ValidateName(name string) error

ValidateName enforces the naming rule for the scaffolded tool — lowercase alphanumeric with optional hyphens, a letter first, and at most 64 characters. This tight rule simultaneously forecloses path traversal, Unicode spoofing, and YAML/TOML/Markdown/shell injection because none of the dangerous characters are in the class.

func ValidateOrg

func ValidateOrg(org, releaseProvider string) error

ValidateOrg enforces GitHub-org syntax for the `github` release provider and GitLab-namespace syntax for `gitlab`, including `/`-separated subgroups up to a reasonable depth. CODEOWNERS silently drops invalid `@`-mentions, so catching bad input early prevents the scaffolded project from shipping broken ownership rules.

func ValidateParentPath added in v0.17.0

func ValidateParentPath(parent string) error

ValidateParentPath validates a `/`-separated parent command path as supplied via --parent. The literal "root" (and the empty string) mean the root command and are accepted as-is; every other segment must be a valid command name.

func ValidateRepo

func ValidateRepo(repo string) error

ValidateRepo enforces Go module path rules: a domain-style first component followed by one or more `[a-zA-Z0-9._~-]+` path segments, no leading/trailing `/`, and no `..` segments. `go mod tidy` would also reject invalid paths, but failing early surfaces a useful error at generation time rather than at first build.

func ValidateSigningBackend added in v0.17.0

func ValidateSigningBackend(backend string) error

ValidateSigningBackend accepts an empty string (meaning "default") and otherwise enforces the registered-backend-name character class.

func ValidateSigningKMSRegion added in v0.17.0

func ValidateSigningKMSRegion(region string) error

ValidateSigningKMSRegion accepts an empty string and otherwise enforces the AWS region character class.

func ValidateSigningKeyID added in v0.17.0

func ValidateSigningKeyID(keyID string) error

ValidateSigningKeyID accepts an empty string and otherwise enforces the KMS key-id / ARN / alias character class (which also admits the local backend's PEM paths). The value is rendered into the generated .goreleaser.yaml, so quotes, whitespace, and control characters are all outside the class. A literal `..` substring is rejected as defence-in-depth: legitimate KMS ids, ARNs, aliases, and the local backend's relative PEM paths never contain it, mirroring how ValidateCommandName forecloses traversal.

func ValidateSigningPublicKey added in v0.17.0

func ValidateSigningPublicKey(publicKey string) error

ValidateSigningPublicKey accepts an empty string and otherwise requires a clean, slash-separated path relative to the project root (the manifest documents the field as the path to the armored public-key file, e.g. internal/trustkeys/keys/signing-key-v1.asc). A single leading `./` is normalised away as a friendliness affordance, so `./key.asc` is treated as `key.asc`. Absolute paths, `..` escapes, backslashes, and any other unclean forms are still rejected — the path must resolve inside the project root.

func ValidateSlackChannel

func ValidateSlackChannel(channel string) error

ValidateSlackChannel accepts an empty string and otherwise enforces Slack's own channel-name rules — lowercase, alphanumeric, hyphens, 1–80 characters.

func ValidateSlackTeam

func ValidateSlackTeam(team string) error

ValidateSlackTeam accepts an empty string and otherwise enforces Slack's workspace-name rules.

func ValidateTeamsChannel

func ValidateTeamsChannel(channel string) error

ValidateTeamsChannel accepts an empty string and otherwise enforces generic "safe for YAML and Markdown" rules: bounded length, no control characters, no template-brace sequences.

func ValidateTeamsTeam

func ValidateTeamsTeam(team string) error

ValidateTeamsTeam mirrors ValidateTeamsChannel.

func ValidateTelemetryEndpoint

func ValidateTelemetryEndpoint(endpoint string) error

ValidateTelemetryEndpoint accepts an empty string (meaning "no endpoint") and otherwise requires a syntactically valid HTTP or HTTPS URL, bounded in length and free of control characters.

func ValidateTemplateSource added in v0.18.0

func ValidateTemplateSource(ts *TemplateSource) error

ValidateTemplateSource validates a single manifest templates entry: the type discriminator, the location character class (no traversal), the ref/SHA shape, and the optional name. It is the gate that forecloses a tampered manifest driving a fetch or write outside the rules.

func ValidateUpdateCheckInterval added in v0.18.0

func ValidateUpdateCheckInterval(interval string) error

ValidateUpdateCheckInterval accepts an empty string (meaning "use the framework default of 24h") and otherwise requires a valid, non-negative Go duration (as understood by time.ParseDuration, e.g. "24h", "168h", "30m"). The value is rendered into the generated tool's props.Tool.UpdateCheckInterval as a time.Duration expression, so a malformed or negative value is rejected rather than silently falling back. A length cap bounds the parse input.

func ValidateUpdatePolicy added in v0.18.0

func ValidateUpdatePolicy(policy string) error

ValidateUpdatePolicy accepts an empty string (meaning "use the framework default of disabled") and otherwise requires one of the three known posture values: disabled, prompt, or enabled. The value selects a typed props.UpdatePolicy constant rendered into the generated tool, so an unknown value is rejected rather than silently treated as disabled.

Types

type CommandContext

type CommandContext struct {
	// Identity
	Name       string
	ParentPath []string // empty = direct child of root

	// Display
	Short string
	Long  string

	// Routing / feature options
	Aliases                       []string
	Args                          string
	WithAssets                    bool
	WithInitializer               bool
	WithConfigValidation          bool
	WrapSubcommandsWithMiddleware bool
	PersistentPreRun              bool
	PreRun                        bool
	Protected                     *bool
	Hidden                        bool

	// Project-level settings (carried from the originating generator)
	ProjectPath string
	DryRun      bool
	Force       bool
	UpdateDocs  bool
}

CommandContext holds the fully resolved configuration for a single command generation or regeneration pass. It is a value type so recursive invocations cannot accidentally share or mutate each other's state.

func (CommandContext) ToConfig

func (c CommandContext) ToConfig() *Config

ToConfig converts the CommandContext into a *Config suitable for constructing a Generator scoped to this specific command.

type CommandFlag

type CommandFlag struct {
	Default       string
	DefaultIsCode bool
	Description   string
	Hidden        bool
	Name          string
	Persistent    bool
	Required      bool
	Shorthand     string
	Type          string
}

type CommandPipeline

type CommandPipeline struct {
	// contains filtered or unexported fields
}

CommandPipeline owns the ordered post-generation steps that are shared by both generate command and regenerate project. Constructing a pipeline and calling Run centralises all registration, hash, manifest, and documentation logic so a fix in one place applies to both entrypoints.

func (*CommandPipeline) Run

Run executes the pipeline steps in order for the given command data and directory. Fatal steps (asset generation) return an error immediately. Advisory steps (registration, manifest) log a warning and accumulate into PipelineResult so callers can inspect partial failures.

type Config

type Config struct {
	Agentless  bool
	AIModel    string
	AIProvider string
	// MaxSteps bounds the autonomous repair agent's ReAct loop. Zero uses the
	// framework default (chat.DefaultMaxSteps). Raise it for complex commands
	// the agent cannot finish within the default budget.
	MaxSteps int
	// NonInteractive withholds the repair agent's query_user tool so a
	// generation never blocks for input (CI / --non-interactive).
	NonInteractive bool
	Aliases        []string
	Args           string
	DryRun         bool
	// GitInit controls the post-generation git step on `generate project`
	// (init + stage + initial commit). It is opt-out: the CLI sets it true by
	// default and false under --no-git. Only honoured by GenerateSkeleton.
	GitInit bool
	// GitPush, when true (--push), adds the derived remote as origin and pushes
	// the initial commit. Opt-in; requires GitInit. Push failures are non-fatal.
	GitPush bool
	// GitBranch is the default branch the initial commit lands on (default
	// "main", overridable via --git-branch).
	GitBranch                     string
	Flags                         []string
	Force                         bool
	Hidden                        bool
	Overwrite                     string // allow, deny, or ask (default ask)
	Long                          string
	Name                          string
	Parent                        string
	Path                          string
	PersistentPreRun              bool
	PreRun                        bool
	Prompt                        string
	Protected                     *bool
	ScriptPath                    string
	Short                         string
	UpdateDocs                    bool
	WithAssets                    bool
	WithConfigValidation          bool
	WithInitializer               bool
	WrapSubcommandsWithMiddleware *bool
}

type DryRunResult

type DryRunResult struct {
	Created  []FilePreview `json:"created,omitempty"`
	Modified []FilePreview `json:"modified,omitempty"`
	// Actions describes non-file side effects the run would perform (e.g. the
	// post-generation git init/commit/push), surfaced as plain text lines.
	Actions []string `json:"actions,omitempty"`
}

DryRunResult contains the preview of planned file operations.

func (*DryRunResult) Print

func (r *DryRunResult) Print(w io.Writer)

Print writes a human-readable preview of the dry-run result to w.

type FilePreview

type FilePreview struct {
	Path    string `json:"path"`
	Content []byte `json:"content,omitempty"`
	Diff    string `json:"diff,omitempty"`
}

FilePreview represents a single file operation in a dry run.

type Generator

type Generator struct {
	// contains filtered or unexported fields
}

func New

func New(p *props.Props, cfg *Config) *Generator

func (*Generator) AddTemplateSource added in v0.18.0

func (g *Generator) AddTemplateSource(ctx context.Context, ts TemplateSource) error

AddTemplateSource appends a source to the manifest (rejecting a duplicate name) and regenerates so the overlay is applied and the pin/hashes are recorded. The supplied source is validated first.

func (*Generator) ApplyFeatures added in v0.18.0

func (g *Generator) ApplyFeatures(ctx context.Context, desired map[string]bool) ([]string, error)

ApplyFeatures flips the requested built-in features in the manifest's properties.features block and re-renders the generated root command so its props.SetFeatures(...) wiring matches. desired maps a toggleable feature name to its requested enabled state (the same method serves the single-name CLI path and the multi-select wizard).

The manifest is normalised against the framework defaults: an entry is kept only when it differs from the default, so returning a feature to its default state removes the entry and the rendered root drops the now-redundant toggle (and the whole SetFeatures call when nothing differs). Unknown names are a hard error. It returns the feature names that actually changed — an empty result means every requested feature was already in the requested state, and nothing is written.

func (*Generator) CurrentFeatures added in v0.18.0

func (g *Generator) CurrentFeatures() ([]ManifestFeature, error)

CurrentFeatures returns the project's current manifest feature entries so a caller (e.g. the enable/disable wizard) can present the current state. A project that has never toggled a feature returns an empty slice (every feature is at its framework default).

func (*Generator) CurrentSigning added in v0.15.1

func (g *Generator) CurrentSigning() (ManifestSigning, error)

CurrentSigning returns the project's current manifest signing block, so a caller (e.g. `gtb enable signing`) can merge flag overrides onto the existing posture rather than replacing it. A project that has never enabled signing returns the zero ManifestSigning.

func (*Generator) DisableSigning added in v0.14.0

func (g *Generator) DisableSigning(ctx context.Context) error

DisableSigning sets signing.enabled = false in the manifest and selectively regenerates: it drops the Signing: field from the root command and removes the generated signing.go. It never deletes internal/trustkeys or any author-added *.asc.

func (*Generator) EnableRealTemplateClone added in v0.18.0

func (g *Generator) EnableRealTemplateClone() *Generator

EnableRealTemplateClone wires the production provider-aware git clone for custom template sources. Call this on a Generator before resolving git sources in production paths; tests inject a fake via WithTemplateClone instead. Returns the Generator for chaining.

func (*Generator) EnableSigning added in v0.14.0

func (g *Generator) EnableSigning(ctx context.Context, signing ManifestSigning) error

EnableSigning sets the manifest signing posture, then selectively regenerates only the signing-affected files: it writes the trustkeys package, keys/.gitkeep and signing.go, and re-renders the root command (pkg/cmd/root/cmd.go) to add the Signing: field. It does not run a full regenerate — unrelated files are left untouched.

func (*Generator) FeatureEnabled added in v0.18.0

func (g *Generator) FeatureEnabled(name string) (bool, error)

FeatureEnabled reports whether the named feature is currently enabled in the project, honouring a manifest override and otherwise falling back to the framework default.

func (*Generator) FindCommandParentPath

func (g *Generator) FindCommandParentPath(name string) ([]string, error)

func (*Generator) Generate

func (g *Generator) Generate(ctx context.Context) error

func (*Generator) GenerateCommandFile

func (g *Generator) GenerateCommandFile(ctx context.Context, cmdDir string, data *templates.CommandData) error

func (*Generator) GenerateDocs

func (g *Generator) GenerateDocs(ctx context.Context, target string, isPackage bool) error

GenerateDocs generates documentation for the command or package.

func (*Generator) GenerateDryRun

func (g *Generator) GenerateDryRun(ctx context.Context) (*DryRunResult, error)

GenerateDryRun previews what Generate would do without writing to disk.

func (*Generator) GenerateSkeleton

func (g *Generator) GenerateSkeleton(ctx context.Context, config SkeletonConfig) error

func (*Generator) GenerateSkeletonDryRun

func (g *Generator) GenerateSkeletonDryRun(ctx context.Context, config SkeletonConfig) (*DryRunResult, error)

GenerateSkeletonDryRun previews what GenerateSkeleton would do without writing to disk.

func (*Generator) ListTemplateSources added in v0.18.0

func (g *Generator) ListTemplateSources() ([]TemplateSource, error)

ListTemplateSources returns the template sources recorded in the project manifest, in layer order.

func (*Generator) RegenerateCommand added in v0.17.0

func (g *Generator) RegenerateCommand(ctx context.Context, cmd ManifestCommand, parentPath []string) error

RegenerateCommand regenerates a single command's cmd.go from its full ManifestCommand record and persists the refreshed cmd.go hash back to the manifest. It reuses the exact mapping the `regenerate project` path uses (prepareRegenerationData → performGeneration → postGenerate), so a command's aliases, persistent/required/shorthand flags, and pre-run hooks survive the regeneration rather than being dropped by a hand-built partial CommandData.

Unlike regenerateCommandRecursive it does NOT recurse into subcommands: it is the entrypoint for the targeted `generate add-flag` regeneration, which must only rewrite the command whose flag set changed. Child registrations in the regenerated cmd.go are preserved by the pipeline's reRegisterChildCommands step.

func (*Generator) RegenerateManifest

func (g *Generator) RegenerateManifest(ctx context.Context) error

func (*Generator) RegenerateProject

func (g *Generator) RegenerateProject(ctx context.Context) error

func (*Generator) RegenerateProjectDryRun

func (g *Generator) RegenerateProjectDryRun(ctx context.Context) (*DryRunResult, error)

RegenerateProjectDryRun previews what RegenerateProject would do without writing to disk.

func (*Generator) Remove

func (g *Generator) Remove(ctx context.Context) error

func (*Generator) RemoveTemplateSource added in v0.18.0

func (g *Generator) RemoveTemplateSource(ctx context.Context, name string) error

RemoveTemplateSource drops a named source from the manifest and regenerates. Removing the source restores any embedded scaffold a `replaces:` had suppressed (the embedded files re-enter the walk) and drops the source's tracked overlay output from the manifest. The on-disk overlay files themselves are left in place unless a regenerated embedded file overwrites them — a clean, reversible swap consistent with the rest of regenerate.

func (*Generator) SetProtection

func (g *Generator) SetProtection(ctx context.Context, commandName string, protected bool) error

func (*Generator) UpdateTemplateSource added in v0.18.0

func (g *Generator) UpdateTemplateSource(ctx context.Context, name string) error

UpdateTemplateSource re-resolves a named git source's ref to a new SHA (clearing the old pin so the clone re-resolves) and regenerates. This is the only pin-advancing path (D7). A local source has no pin to advance; updating it simply regenerates against the current on-disk tree.

func (*Generator) WithTemplateClone added in v0.18.0

func (g *Generator) WithTemplateClone(fn templateCloneFunc) *Generator

WithTemplateClone injects the git template-source clone implementation. Primarily for tests (a local bare remote or a fake); production code calls EnableRealTemplateClone to wire the provider-aware pkg/vcs/repo clone.

type IgnoreRules

type IgnoreRules struct {
	// contains filtered or unexported fields
}

IgnoreRules holds compiled ignore patterns from a .gtb/ignore file. Patterns are evaluated top-to-bottom; later patterns override earlier ones. Negation (!) re-includes a previously excluded file.

func LoadIgnoreRules

func LoadIgnoreRules(fs afero.Fs, projectPath string) *IgnoreRules

LoadIgnoreRules reads the .gtb/ignore file from the project directory. Returns empty rules (nothing ignored) if the file doesn't exist.

func (*IgnoreRules) IsIgnored

func (r *IgnoreRules) IsIgnored(relPath string) bool

IsIgnored evaluates all rules top-to-bottom and returns whether the given relative path should be ignored. Negation patterns (!) can re-include files excluded by earlier patterns.

type Manifest

type Manifest struct {
	Properties    ManifestProperties    `yaml:"properties"`
	ReleaseSource ManifestReleaseSource `yaml:"release_source"`
	Version       ManifestVersion       `yaml:"version"`
	Hashes        map[string]string     `yaml:"hashes,omitempty"` // project-level file hashes (relative path → SHA256)
	Commands      []ManifestCommand     `yaml:"commands,omitempty"`
}

func DecodeManifestFile added in v0.17.0

func DecodeManifestFile(fs afero.Fs, manifestPath string) (*Manifest, error)

DecodeManifestFile reads and unmarshals the manifest at the given manifest.yaml path from fs. It is the single read/decode helper routed through by every manifest-loading site (including the generate-flag command in internal/cmd), so the read+unmarshal boilerplate — and its error wording — lives in one place.

func (*Manifest) GetReleaseSource

func (m *Manifest) GetReleaseSource() (sourceType, owner, repo string)

GetReleaseSource returns the release source type, owner, and repo.

type ManifestCI added in v0.18.0

type ManifestCI struct {
	// ComponentSource is the include base for the phpboyscout/cicd
	// components in the scaffolded .gitlab-ci.yml. Empty means "use the
	// framework default" (DefaultCICDComponentSource,
	// gitlab.com/phpboyscout/cicd); a mirrored/self-hosted downstream sets
	// this to repoint the include base. Defaulted on render so a manifest
	// with no ci block still produces a complete pipeline.
	ComponentSource string `yaml:"component_source,omitempty"`
}

ManifestCI holds CI-pipeline configuration for generated tools. It is the manifest representation of the scaffolded GitLab pipeline's configurable inputs. Currently it carries only the phpboyscout/cicd component source so a mirrored or self-hosted downstream can repoint the include base; the component versions are pinned by a generator constant kept in lockstep with the framework (not manifest-driven). See docs/development/specs/2026-06-15-generator-gitlab-ci-refresh.md.

type ManifestCommand

type ManifestCommand struct {
	Name                          string            `yaml:"name"`
	Description                   MultilineString   `yaml:"description"`
	LongDescription               MultilineString   `yaml:"long_description,omitempty"`
	Aliases                       []string          `yaml:"aliases,omitempty"`
	Hidden                        bool              `yaml:"hidden,omitempty"`
	Args                          string            `yaml:"args,omitempty"`
	Hash                          string            `yaml:"hash,omitempty"` // Deprecated: use Hashes
	Hashes                        map[string]string `yaml:"hashes,omitempty"`
	WithAssets                    bool              `yaml:"with_assets,omitempty"`
	WithInitializer               bool              `yaml:"with_initializer,omitempty"`
	WithConfigValidation          bool              `yaml:"with_config_validation,omitempty"`
	WrapSubcommandsWithMiddleware bool              `yaml:"wrap_subcommands_with_middleware,omitempty"`
	Protected                     *bool             `yaml:"protected,omitempty"`

	PersistentPreRun  bool              `yaml:"persistent_pre_run,omitempty"`
	PreRun            bool              `yaml:"pre_run,omitempty"`
	MutuallyExclusive [][]string        `yaml:"mutually_exclusive,omitempty"`
	RequiredTogether  [][]string        `yaml:"required_together,omitempty"`
	Flags             []ManifestFlag    `yaml:"flags,omitempty"`
	Commands          []ManifestCommand `yaml:"commands,omitempty"`
	Warning           string            `yaml:"-"` // Used for comments
}

func (ManifestCommand) MarshalYAML

func (c ManifestCommand) MarshalYAML() (any, error)

type ManifestCommandUpdate

type ManifestCommandUpdate struct {
	Name                          string
	Description                   string
	LongDescription               string
	Aliases                       []string
	Args                          string
	Hashes                        map[string]string
	Flags                         []ManifestFlag
	WithAssets                    bool
	WithInitializer               bool
	WithConfigValidation          bool
	WrapSubcommandsWithMiddleware *bool
	PersistentPreRun              bool
	PreRun                        bool
	Protected                     *bool
	Hidden                        bool
}

ManifestCommandUpdate carries all fields that updateCommandRecursive writes to a ManifestCommand entry. Adding a new manifest field means adding it here rather than extending the function signature.

type ManifestFeature

type ManifestFeature struct {
	Name    string `yaml:"name"`
	Enabled bool   `yaml:"enabled"`
}

type ManifestFlag

type ManifestFlag struct {
	Name          string          `yaml:"name"`
	Type          string          `yaml:"type"`
	Description   MultilineString `yaml:"description"`
	Persistent    bool            `yaml:"persistent,omitempty"`
	Shorthand     string          `yaml:"shorthand,omitempty"`
	Default       string          `yaml:"default,omitempty"`
	DefaultIsCode bool            `yaml:"default_is_code,omitempty"`
	Required      bool            `yaml:"required,omitempty"`
	Hidden        bool            `yaml:"hidden,omitempty"`
	Warning       string          `yaml:"-"` // Used for comments
}

func (ManifestFlag) MarshalYAML

func (f ManifestFlag) MarshalYAML() (any, error)

type ManifestHelp

type ManifestHelp struct {
	Type         string `yaml:"type,omitempty"`
	SlackChannel string `yaml:"slack_channel,omitempty"`
	SlackTeam    string `yaml:"slack_team,omitempty"`
	TeamsChannel string `yaml:"teams_channel,omitempty"`
	TeamsTeam    string `yaml:"teams_team,omitempty"`
}

type ManifestProperties

type ManifestProperties struct {
	Name        string            `yaml:"name"`
	Description MultilineString   `yaml:"description"`
	Features    []ManifestFeature `yaml:"features"`
	EnvPrefix   string            `yaml:"env_prefix,omitempty"`
	// UpdatePolicy is the generated tool's self-update posture baseline
	// (disabled / prompt / enabled). Empty = framework default (disabled).
	UpdatePolicy string `yaml:"update_policy,omitempty"`
	// UpdateCheckInterval is the generated tool's baseline self-update-check
	// throttle as a Go duration string (e.g. "24h"). Empty = framework
	// default (24h).
	UpdateCheckInterval string            `yaml:"update_check_interval,omitempty"`
	Help                ManifestHelp      `yaml:"help,omitempty"`
	Telemetry           ManifestTelemetry `yaml:"telemetry,omitempty"`
	Signing             ManifestSigning   `yaml:"signing,omitempty"`
	CI                  ManifestCI        `yaml:"ci,omitempty"`
	// Templates records the custom template-overlay sources applied to the
	// project, in render (layer) order: embedded base → templates[0] →
	// templates[1] → … (last writer wins for a shared path). Each entry is
	// provenance + pinning only; suppression behaviour lives in the source's
	// own gtb-template.yaml descriptor. See
	// docs/development/specs/2026-06-15-generator-custom-partial-templates.md.
	Templates []TemplateSource `yaml:"templates,omitempty"`
}

type ManifestReleaseSource

type ManifestReleaseSource struct {
	Type    string `yaml:"type"`
	Host    string `yaml:"host"`
	Owner   string `yaml:"owner"`
	Repo    string `yaml:"repo"`
	Private bool   `yaml:"private,omitempty"`
}

type ManifestSigning added in v0.14.0

type ManifestSigning struct {
	// Enabled gates all signing scaffolding. Defaults to false; set true
	// by `gtb enable signing` or `gtb generate project --signing`.
	Enabled bool `yaml:"enabled,omitempty"`
	// ExternalKeyEmail derives the WKD URL and enables the external
	// (WKD) trust-anchor leg. Empty leaves verification embedded-only.
	ExternalKeyEmail string `yaml:"external_key_email,omitempty"`
	// RequireSignature fails an update closed when no valid signature is
	// present. Stays false until a signed release has shipped (the N+1
	// rollout); only ever flipped via `gtb enable signing --require-signature`.
	RequireSignature bool `yaml:"require_signature,omitempty"`
	// KeySource selects the trust-anchor source: "embedded", "external"
	// or "both" (the framework default when empty).
	KeySource string `yaml:"key_source,omitempty"`
	// RequireExternalCrosscheck fails closed when the external (WKD)
	// resolver is unreachable, rather than degrading to embedded-only.
	RequireExternalCrosscheck bool `yaml:"require_external_crosscheck,omitempty"`
	// Backend selects the `gtb sign` backend the generated release
	// pipeline signs with (e.g. "aws-kms", "local"). Defaults to
	// "aws-kms" when a key id is recorded. Drives the GoReleaser signs
	// block; backend-specific args (e.g. kms_region) are emitted only for
	// the backends that take them.
	Backend string `yaml:"backend,omitempty"`
	// KeyID is the backend-specific signing key identifier passed to
	// `gtb sign --key-id` (a KMS id/ARN/alias, or a PEM path for the
	// local backend). Recording it is what turns the GoReleaser signs
	// block on; empty leaves the release pipeline untouched.
	KeyID string `yaml:"key_id,omitempty"`
	// KMSRegion is the AWS region for the aws-kms backend
	// (`gtb sign --kms-region`). Defaults to "eu-west-2". Ignored by
	// backends that don't take a region.
	KMSRegion string `yaml:"kms_region,omitempty"`
	// PublicKey is the path to the armored public-key file the signature
	// identifies (`gtb sign --public-key`), relative to the project root.
	// Defaults to the embedded-key convention
	// internal/trustkeys/keys/signing-key-v1.asc.
	PublicKey string `yaml:"public_key,omitempty"`
}

ManifestSigning holds self-update signature-verification configuration for generated tools. It is the manifest representation of the generated internal/trustkeys package, the props.Tool.Signing wiring, and the generated signing.go enforcement defaults. Signing is disabled by default — a project with no signing block scaffolds nothing signing-related. See docs/development/specs/2026-06-10-signing-generator-feature.md.

func ApplySigningDefaults added in v0.15.0

func ApplySigningDefaults(s ManifestSigning) ManifestSigning

ApplySigningDefaults fills the release-pipeline defaults when a signing key id has been recorded, so the generated GoReleaser signs block is always complete. With no key id the release pipeline is left untouched. Exported so the generate command can default at its boundary, keeping the persisted manifest and the rendered .goreleaser.yaml consistent — the same defaulting EnableSigning applies.

type ManifestTelemetry

type ManifestTelemetry struct {
	Endpoint     string `yaml:"endpoint,omitempty"`
	OTelEndpoint string `yaml:"otel_endpoint,omitempty"`
}

ManifestTelemetry holds telemetry configuration for generated tools.

type ManifestVersion

type ManifestVersion struct {
	GoToolBase string `yaml:"gtb"`
}

type MultilineString

type MultilineString string

func (MultilineString) MarshalYAML

func (s MultilineString) MarshalYAML() (any, error)

type PipelineOptions

type PipelineOptions struct {
	SkipAssets        bool // do not generate asset files
	SkipDocumentation bool // do not run documentation generation
	SkipRegistration  bool // do not modify the parent cmd.go
}

PipelineOptions controls which steps CommandPipeline executes. The zero value enables all steps.

type PipelineResult

type PipelineResult struct {
	Warnings []StepWarning
}

PipelineResult is returned by CommandPipeline.Run and carries any advisory warnings accumulated during execution. A non-empty Warnings slice does not indicate overall failure — the pipeline continued past those steps.

type SkeletonConfig

type SkeletonConfig struct {
	Name                  string
	Repo                  string
	Host                  string
	Description           string
	Path                  string
	GoVersion             string // overrides autodetected version when set
	Features              []ManifestFeature
	Private               bool   // true if the repository requires authentication to access
	HelpType              string // "slack", "teams", or ""
	SlackChannel          string
	SlackTeam             string
	TeamsChannel          string
	TeamsTeam             string
	TelemetryEndpoint     string          // populated from manifest telemetry.endpoint
	TelemetryOTelEndpoint string          // populated from manifest telemetry.otel_endpoint
	EnvPrefix             string          // environment variable prefix for config overrides
	Signing               ManifestSigning // self-update signature-verification posture (disabled by default)
	// UpdatePolicy is the generated tool's self-update posture baseline
	// (disabled / prompt / enabled). Empty leaves it unset so the framework
	// default (disabled) applies; wired into the generated root command's
	// props.Tool.UpdatePolicy. See docs/development/specs/2026-06-16-forced-update-feature.md.
	UpdatePolicy string
	// UpdateCheckInterval is the generated tool's baseline self-update-check
	// throttle as a Go duration string (e.g. "24h"). Empty leaves it unset so
	// the framework default (24h) applies; wired into the generated root
	// command's props.Tool.UpdateCheckInterval.
	UpdateCheckInterval string
	// CIComponentSource overrides the phpboyscout/cicd include base in the
	// scaffolded GitLab pipeline. Empty falls back to
	// DefaultCICDComponentSource. GitLab-only; ignored for GitHub projects.
	CIComponentSource string
	// Templates carries the custom template-overlay sources (in layer order)
	// through generation. The wizard/flags populate it and
	// writeSkeletonManifest persists it.
	Templates []TemplateSource
}

type StepWarning

type StepWarning struct {
	Step string
	Err  error
}

StepWarning records a non-fatal failure within a pipeline step.

type TemplateContractData added in v0.18.0

type TemplateContractData struct {
	Name              string
	Description       string
	Repo              string
	Host              string
	Org               string
	RepoName          string
	ModulePath        string
	ReleaseProvider   string
	GoVersion         string
	GoToolBaseVersion string
	EnvPrefix         string
	EnabledFeatures   []string
	DisabledFeatures  []string
	Private           bool
	// SigningEnabled / HelpType expose only presence/shape, never secrets.
	SigningEnabled bool
	HelpType       string
}

TemplateContractData is the documented, stable, metadata-only and secret-free subset of skeletonTemplateData exposed to overlay templates. It deliberately excludes any resolved credential, env var, absolute host path, or forge token — the information-disclosure threat. Adding a field is a safe additive change; removing one is a contract-version bump.

func (TemplateContractData) GetReleaseProvider added in v0.18.0

func (d TemplateContractData) GetReleaseProvider() string

GetReleaseProvider satisfies releaseProviderAccessor.

type TemplateDescriptor added in v0.18.0

type TemplateDescriptor struct {
	// Contract is the data-contract version the set targets. Zero is treated
	// as version 1 (the only version) for descriptors that omit it; any
	// value above supportedContractVersion is rejected.
	Contract int `yaml:"contract"`
	// Description is a human-facing summary of the template set.
	Description string `yaml:"description"`
	// Replaces names the embedded scaffolds this set supersedes. Each entry
	// must be a known alias in suppressibleAliases.
	Replaces []string `yaml:"replaces"`
}

TemplateDescriptor is the parsed gtb-template.yaml at a source root. It declares the data-contract version the set targets and the embedded scaffolds it suppresses before the overlay renders. Absent descriptor / empty Replaces ⇒ pure overlay (nothing suppressed).

type TemplateSource added in v0.18.0

type TemplateSource struct {
	// Name is an optional operator-assigned handle used by
	// `gtb template update/remove <name>`. Defaults to the repo/dir base
	// name when unset.
	Name string `yaml:"name,omitempty"`
	// Type is "git" or "local".
	Type TemplateSourceType `yaml:"type"`
	// Location is the forge repo path (org/repo, nested GitLab groups
	// supported) or a full clone URL for git sources, or a filesystem path
	// for local sources.
	Location string `yaml:"location"`
	// Ref is the branch/tag/commit the operator specified, recorded verbatim
	// (provenance). Empty/"" defaults to the source's default branch.
	Ref string `yaml:"ref,omitempty"`
	// Resolved is the commit SHA Ref resolved to at generate time — the pin
	// regenerate reproduces from. Empty for local sources.
	Resolved string `yaml:"resolved,omitempty"`
	// Fingerprint is a content fingerprint of a local source's tree at
	// generate time, so regenerate can warn when the on-disk source drifts.
	// Empty for git sources (the resolved SHA is the pin).
	Fingerprint string `yaml:"fingerprint,omitempty"`
	// Hashes records each rendered overlay file's SHA256 keyed by output
	// relative path, so a source's footprint is self-contained and can be
	// removed cleanly (D5).
	Hashes map[string]string `yaml:"hashes,omitempty"`
}

TemplateSource is the minimal consumer-manifest record for one custom template-overlay source: provenance (where it came from, what ref the operator asked for) plus the pin (the resolved commit SHA for git, a content fingerprint for local) and the per-source rendered-output hashes.

The *behaviour* of a source (which embedded scaffolds it replaces, which data-contract version it targets) lives with the template set in its gtb-template.yaml descriptor, never here — the consumer manifest stays provenance-only so consuming projects do not repeat the author's intent.

func ParseTemplateSpec added in v0.18.0

func ParseTemplateSpec(fs afero.Fs, spec, name string) (TemplateSource, error)

ParseTemplateSpec parses a `<src>@<ref>` spec into a TemplateSource. The `@<ref>` suffix is optional (defaults to the source's default branch for git). The source type is inferred: a value that exists as a directory on fs, or that looks like a filesystem path (starts with ./ ../ / or ~), is a local source; otherwise it is a git source. The parsed entry is validated before return so a malformed spec fails at the CLI boundary.

An explicit name (e.g. from `--name`) overrides the inferred default; pass "" to take the default (the repo/dir base name).

type TemplateSourceType added in v0.18.0

type TemplateSourceType string

TemplateSourceType discriminates the two custom-template source backends.

const (
	// TemplateSourceLocal reads a template overlay directly from a
	// filesystem path. No network, no cache, no SHA pin — a content
	// fingerprint is recorded so regenerate can warn on drift.
	TemplateSourceLocal TemplateSourceType = "local"
	// TemplateSourceGit clones a template overlay from a forge repo (public
	// over https/go-git, private via the configured forge auth) and pins the
	// resolved commit SHA for byte-stable regeneration.
	TemplateSourceGit TemplateSourceType = "git"
)

Directories

Path Synopsis
Package templates provides Go template definitions and data structures used by the generator to produce CLI command scaffolding, registration code (cmd.go), and implementation stubs (main.go).
Package templates provides Go template definitions and data structures used by the generator to produce CLI command scaffolding, registration code (cmd.go), and implementation stubs (main.go).
Package verifier provides post-generation verification strategies that validate generated projects compile and pass tests.
Package verifier provides post-generation verification strategies that validate generated projects compile and pass tests.

Jump to

Keyboard shortcuts

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