templates

package
v0.41.1 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

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). Template data types like CommandData carry the resolved configuration for each generated command.

Index

Constants

View Source
const SkeletonConfig = `` /* 625-byte string literal not displayed */
View Source
const SkeletonGoMod = `` /* 286-byte string literal not displayed */

Variables

View Source
var ExternalArgTokens = []string{"logger", "props", "config", "fs", "version"}

ExternalArgTokens is the closed injection vocabulary for the declarative external-command channel. Each token names a well-known dependency the generator can derive from the *props.Props value (p) when rendering an external constructor call onto the generated root.

The set is deliberately CLOSED (see https://gitlab.com/phpboyscout/go-tool-base/-/wikis/specs/0182-external-command-attachment): keeping it a fixed vocabulary — rather than a free-form Go expression — keeps the generated root type-safe and reviewable and prevents the manifest from becoming an arbitrary-code injection vector. Anything the vocabulary cannot express uses the adapter channel instead.

It is the SINGLE source of truth for the valid token set: the manifest validator (generator.validateExternalAttach) checks membership against it, and ExternalArgExpr renders each token, so the two sides cannot drift.

View Source
var FeatureCatalogue = []FeatureDescriptor{
	{props.UpdateCmd, props.PackagePath, "UpdateCmd", true},
	{props.InitCmd, props.PackagePath, "InitCmd", true},
	{props.McpCmd, props.PackagePath, "McpCmd", true},
	{props.DocsCmd, props.PackagePath, "DocsCmd", true},
	{props.DoctorCmd, props.PackagePath, "DoctorCmd", true},
	{props.ChangelogCmd, props.PackagePath, "ChangelogCmd", true},
	{props.AiCmd, props.PackagePath, "AiCmd", false},
	{props.ConfigCmd, props.PackagePath, "ConfigCmd", false},
	{props.TelemetryCmd, props.PackagePath, "TelemetryCmd", false},
	{props.ManCmd, props.PackagePath, "ManCmd", false},
	{forge.GithubFeature, forge.PackagePath, "GithubFeature", false},
	{forge.GitlabFeature, forge.PackagePath, "GitlabFeature", false},
	{forge.GiteaFeature, forge.PackagePath, "GiteaFeature", false},
	{forge.CodebergFeature, forge.PackagePath, "CodebergFeature", false},
	{forge.BitbucketFeature, forge.PackagePath, "BitbucketFeature", false},
}

FeatureCatalogue is the ordered, canonical name<->constant<->default table for every scaffoldable props.FeatureID feature. It is generator-internal tooling (not public API): the SetFeatures renderer and the manifest scanner both derive from it, so the mapping has a single origin and the two sides cannot drift. The historical bug it fixes: the scanner froze at the original four features while the set grew. A test guards this list against the props registry so a new framework feature cannot silently omit its generator handling.

The table is written out rather than derived from the props registry, and it duplicates data props.FeatureDescriptor already carries — the guard test asserts the two agree field by field. That is a known cost, not a claim the duplication is free. Deriving it needs three things this package does not do yet:

  • Lazy evaluation. props.FeatureDescriptors seals the registry on read, so computing this at package init would make any later RegisterFeature panic with ErrRegistrySealed.
  • A Kind filter. The registry answers what this binary linked; the generator needs the complete set a scaffolded tool may select, and a downstream tool's own registrations are not GTB's to scaffold.
  • Somewhere for keychain, which has no FeatureID to derive from.

Note the ordering argument does NOT hold: FeatureDescriptors guarantees a stable total order derived from data, and names this generator's golden files as the reason it does. Whether the duplication is worth collapsing is being settled separately — see go-tool-base#11, which asks whether feature management belongs in a module of its own rather than split between props and the generator.

keychain is intentionally absent: it has no FeatureID and is a build-time blank-import decision, recovered from its artefact rather than SetFeatures.

Functions

func CommandConfigValidation

func CommandConfigValidation(data CommandData) string

CommandConfigValidation generates the content for a config.go file containing a Config struct stub and a ValidateConfig function for per-package schema validation.

func CommandExecution

func CommandExecution(data CommandData) string

func CommandInitializer

func CommandInitializer(data CommandData) *jen.File

func CommandRegistration

func CommandRegistration(data CommandData) *jen.File

func ConfigLayerConstName

func ConfigLayerConstName(name string) string

ConfigLayerConstName maps a manifest layer name to its exported props constant. Exported so the mapping can be asserted directly rather than only through rendered output.

func ExternalArgExpr

func ExternalArgExpr(token string) jen.Code

ExternalArgExpr renders one injection token to the Go expression, derived from the props value p, that is passed to an external constructor. It is the render counterpart of ExternalArgTokens; an unknown token (which the manifest validator rejects before render) falls back to p so the render can never panic. The token set here and in ExternalArgTokens must stay in lockstep — guarded by a test.

func IsExternalArgToken

func IsExternalArgToken(tok string) bool

IsExternalArgToken reports whether tok is a member of the closed vocabulary.

func SkeletonExternalAdapter

func SkeletonExternalAdapter() string

SkeletonExternalAdapter returns the source of the external-command adapter escape hatch (pkg/cmd/external/attach.go). Unlike most generated files this is author-owned: gtb scaffolds it once and never overwrites it (it is preserved across regenerate), so the author can attach external command trees of any shape the declarative vocabulary cannot express. The generated root spreads external.Commands(p) into NewCmdRoot, so returned commands still pick up the framework middleware pipeline.

func SkeletonInternalVersion

func SkeletonInternalVersion(modulePath string) *jen.File

SkeletonInternalVersion generates the internal/version/version.go file for scaffolded projects, mirroring gtb's own internal/version pattern.

func SkeletonKeychain

func SkeletonKeychain() *jen.File

SkeletonKeychain generates cmd/<name>/keychain.go for scaffolded projects, blank-importing pkg/credentials/keychain so the tool activates OS keychain support by default. Deleting this single file produces a regulated-build variant with no IPC-to-keychain code linked — verifiable via SBOM against the linked artefact.

Skipped when the "keychain" feature is opted out during `gtb generate` (--features=...without keychain, or the interactive multi-select).

func SkeletonMain

func SkeletonMain(modulePath string) *jen.File

SkeletonMain generates the cmd/<name>/main.go for scaffolded projects. It uses the internal/version package to retrieve version info and passes it to the root command constructor. Execution is delegated to gtbRoot.Execute, which runs the command tree with a signal-aware context: SIGINT/SIGTERM cancel cmd.Context() for graceful shutdown, a second signal force-exits, and a signal-terminated run exits 128+signum.

func SkeletonRoot

func SkeletonRoot(data SkeletonRootData) *jen.File

func SkeletonSigning

func SkeletonSigning(data SkeletonSigningData) *jen.File

SkeletonSigning generates pkg/cmd/root/signing.go for scaffolded projects with signing enabled. It mirrors go-tool-base's own internal/cmd/root/signing.go split: an init() that sets the pkg/setup enforcement defaults, kept in a sibling file to the templated root command. The values come from the manifest signing block rather than being hand-written, so the author tunes posture via `gtb enable signing` flags, not by editing generated code.

Only non-default values are emitted, so a freshly enabled project (no email, require_signature off, default key source) produces an init() that does nothing until the author configures a posture.

func SkeletonTrustKeys

func SkeletonTrustKeys() *jen.File

SkeletonTrustKeys generates internal/trustkeys/trustkeys.go for scaffolded projects with signing enabled. It mirrors go-tool-base's own internal/trustkeys/trustkeys.go: a //go:embed of the keys directory plus a Keys() [][]byte accessor that walks keys/*.asc.

The package is generated and regeneration-protected; the author adds their minted release public key(s) as files under keys/, never by editing this .go. With no keys present Keys returns nil, so signature verification stays dormant until a real key is dropped in.

Types

type CommandData

type CommandData struct {
	Package                  string
	PascalName               string
	Name                     string
	Aliases                  []string
	Args                     string
	Short                    string
	Long                     string
	Hidden                   bool
	WithAssets               bool
	Flags                    []CommandFlag
	PersistentFlags          []CommandFlag
	AncestralPersistentFlags []CommandFlag
	MutuallyExclusive        [][]string
	RequiredTogether         [][]string
	Logic                    string
	Imports                  []string
	FullFileContent          string
	TestCode                 string
	Recommendations          []string
	PersistentPreRun         bool
	PreRun                   bool
	HasSubcommands           bool
	// PureGroup marks a command that only groups its subcommands and so has no
	// run logic of its own — see Generator.pureGroup for how it is decided.
	// Such a command gets no Run<Name>, and its RunE is the framework's.
	PureGroup            bool
	WithInitializer      bool
	WithConfigValidation bool
	Hashes               map[string]string
	// MCPExposure controls whether the generated command stamps a
	// setup.ExcludeFromMCP / setup.IncludeInMCP marker. The zero value
	// (Inherit) emits nothing.
	MCPExposure setup.MCPExposure
}

type CommandFlag

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

type FeatureDescriptor

type FeatureDescriptor struct {
	// Cmd is the feature's props.FeatureID; its string value is the
	// config/manifest name (e.g. "ai").
	Cmd props.FeatureID
	// ConstPackage is the import path of the package declaring ConstName.
	// Built-ins live in props; forge features live in pkg/setup/forge. The
	// emitter qualifies against this rather than assuming props, which is what
	// previously made forge features unemittable.
	ConstPackage string
	// ConstName is the exported Go identifier of the constant as it appears in
	// generated source, e.g. "AiCmd". It cannot be derived reliably from the
	// value (mcp -> McpCmd), so it is recorded explicitly.
	ConstName string
	// Default is the framework default-enabled state, mirroring
	// props.DefaultFeatures. Forge features are never default-enabled: a blank
	// import changes what is available, never what is on.
	Default bool
}

FeatureDescriptor carries the four facts the generator needs to round-trip a feature between its config/manifest name, the Go source token that names its props FeatureID constant, the package that declares that constant, and its default-enabled state.

type SkeletonExternalCommand

type SkeletonExternalCommand struct {
	ImportPath  string   // e.g. "gitlab.com/phpboyscout/go/signing-cli"
	PkgAlias    string   // e.g. "signingcli"
	Constructor string   // e.g. "NewCmdSign"
	Args        []string // injection tokens, e.g. ["logger"]
	Wrap        bool     // wrap in setup.Wrap("", …)
}

SkeletonExternalCommand describes one external constructor (the declarative attachment channel) to render as an argument to NewCmdRoot. Args are injection tokens from the closed vocabulary (ExternalArgTokens); the template resolves each via ExternalArgExpr. When Wrap is true the constructor returns *cobra.Command and is wrapped in setup.Wrap("", …); otherwise it returns *setup.Command and is attached directly. Declarative attachments are un-gated, so the wrap label is always the empty string.

type SkeletonRootData

type SkeletonRootData struct {
	Name                  string
	Description           string
	ReleaseProvider       string
	Host                  string
	Org                   string
	RepoName              string
	Private               bool
	DisabledFeatures      []string
	EnabledFeatures       []string
	HelpType              string // "slack", "teams", or ""
	SlackChannel          string
	SlackTeam             string
	TeamsChannel          string
	TeamsTeam             string
	TelemetryEndpoint     string
	TelemetryOTelEndpoint string
	EnvPrefix             string
	// ConfigLayers wires props.Tool.ConfigLayers in the generated root. Empty
	// means the project states nothing and inherits the framework default, so
	// no field is emitted — keeping generated output byte-identical for every
	// project that does not care.
	ConfigLayers []string
	// UpdatePolicy wires props.Tool.UpdatePolicy in the generated root. Empty
	// (or "disabled") leaves the field off so the framework default applies;
	// "prompt"/"enabled" emit the matching props.UpdatePolicy* constant.
	UpdatePolicy string
	// UpdateCheckInterval wires props.Tool.UpdateCheckInterval as a Go duration
	// string (e.g. "24h"). Empty, unparseable, or non-positive values leave the
	// field off so the framework default (24h) applies.
	UpdateCheckInterval string
	// SigningEnabled gates the Signing: props.SigningConfig{...} block.
	// When true the generated tool wires trustkeys.Keys() as its embedded
	// trust anchor; ModulePath supplies the import path for that package.
	SigningEnabled bool
	ModulePath     string
	// AutoInitialise wires props.Tool.Bootstrap.AutoInitialise. When true the
	// generated tool auto-runs a non-interactive init if config is missing.
	AutoInitialise bool
	// SkipConfigCheck wires props.Tool.Bootstrap.SkipConfigCheck — commands
	// (by Name() or full CommandPath()) whose missing-config gate is relaxed.
	SkipConfigCheck []string
	Subcommands     []SkeletonSubcommand
	// ExternalCommands are declarative external-module attachments rendered as
	// additional NewCmdRoot arguments (see the external-command-attachment spec).
	ExternalCommands []SkeletonExternalCommand
	// ExternalAdapter, when true, spreads external.Commands(p) — the user-owned
	// adapter escape hatch at <ModulePath>/pkg/cmd/external — into NewCmdRoot.
	ExternalAdapter bool
}

type SkeletonSigningData

type SkeletonSigningData struct {
	ExternalKeyEmail          string
	RequireSignature          bool
	KeySource                 string // "embedded" | "external" | "both"; empty → framework default
	RequireExternalCrosscheck bool
}

SkeletonSigningData carries the manifest signing posture into the generated signing.go enforcement defaults.

type SkeletonSubcommand

type SkeletonSubcommand struct {
	ImportPath  string // e.g. "github.com/org/repo/pkg/cmd/serve"
	PkgAlias    string // e.g. "serve"
	Constructor string // e.g. "NewCmdServe"
}

SkeletonSubcommand describes a top-level command that must be registered in the generated NewCmdRoot function.

Jump to

Keyboard shortcuts

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