Documentation
¶
Overview ¶
Package template handles external templates: detect (spin.toml + _base/), load (clone or read pin), and render via text/template.
Index ¶
- func HasHooks(t *Template) bool
- func RunPostHook(ctx context.Context, t *Template, values map[string]any, dir string, ...) error
- func RunPreHook(ctx context.Context, t *Template, values map[string]any, dir string, ...) error
- func UnwrapValue(v params.Value) any
- type Author
- type DestAction
- type HookOptions
- type HookView
- type IncludeRule
- type Loader
- type PostStep
- type PreStep
- type SpinToml
- type Template
- func (t *Template) BuildForm(values map[string]any) (*huh.Form, error)
- func (t *Template) Hints() []string
- func (t *Template) Render(values map[string]any) (map[string][]byte, error)
- func (t *Template) RenderTo(ctx context.Context, dest string, values map[string]any) error
- func (t *Template) RenderToWithPost(ctx context.Context, dest string, values map[string]any, opts HookOptions) error
- func (t *Template) ResolveForm(values map[string]any, interactive bool) (map[string]any, error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func HasHooks ¶
HasHooks reports whether the template would run any shell commands: [[pre]]/[[post]] steps, or non-hidden files in _pre/ or _post/.
func RunPostHook ¶
func RunPostHook(ctx context.Context, t *Template, values map[string]any, dir string, opts HookOptions) error
RunPostHook executes the template's [[post]] steps (if any) after the files have been written to disk. Each step's `run` is rendered against the resolved param + flag values (so `{{.project_name}}` interpolates correctly), then run via `sh -c` in dir. Steps run in order; the hook stops on the first failure and returns that error (with the failing command and its combined output).
An empty or missing post section is a no-op.
The post-hook runs AFTER files are written, BEFORE the spin.toml is removed from the output directory. This ordering lets the hook observe the full scaffolded state (including any spin.toml that might have been included in _base/) but ensures the project that the user sees has spin.toml deleted by the time the scaffolder returns.
func RunPreHook ¶
func RunPreHook(ctx context.Context, t *Template, values map[string]any, dir string, opts HookOptions) error
RunPreHook executes the template's [[pre]] steps (if any) after params are resolved but before files are rendered. Each step's `run` is rendered against the resolved param + flag values, then run via `sh -c` in dir. Steps run in order; the hook stops on the first failure and returns that error.
An empty or missing pre section is a no-op.
func UnwrapValue ¶
UnwrapValue returns the underlying primitive held by a params.Value. The text/template engine wants raw Go types (string, int, bool, []string), not the multi-field struct. Exported because post_hook.go also needs it.
Types ¶
type Author ¶
type Author struct {
Name string `toml:"name"`
Email string `toml:"email"`
URL string `toml:"url"`
}
Author identifies the template creator. All fields are optional; templates only need to fill what they want to publish.
type DestAction ¶
type DestAction int
DestAction is the user's choice when a pre-existing clone is found at the destination.
const ( DestReuse DestAction = iota // use the existing clone as-is DestPin // reuse and persist the source for offline use DestWipe // remove the clone and re-clone DestCancel // abort without changes )
type HookOptions ¶
type HookOptions struct {
// NoHooks skips execution entirely. Commands are still printed if
// PrintCommands is true.
NoHooks bool
// PrintCommands prints each rendered command before running it.
PrintCommands bool
// Verbose streams hook output to the caller. When false, output is
// captured and only returned on failure.
Verbose bool
// Output, when set, receives the echoed command lines and, when
// Verbose is true, the live command output of each step. It is used
// by the interactive TUI to stream hook execution into a viewport.
// When Output is nil, PrintCommands falls back to the package logger.
Output io.Writer
// StepStart, when set, is called before each step runs so the caller
// can print a styled header. Only used when PrintCommands is true;
// falls back to a plain log line otherwise.
StepStart func(kind, cmd string)
}
HookOptions controls how hooks are reported and whether they run.
type HookView ¶
type HookView struct {
// Phase is "pre" or "post".
Phase string
// Run is the inline shell command for [[pre]]/[[post]] steps.
Run string
// File is the absolute path to the script for _pre/_post hooks.
File string
// IsFile reports whether this entry refers to a script file.
IsFile bool
}
HookView is one reviewable hook entry surfaced by the interactive TUI. A hook is either an inline [[pre]]/[[post]] step (Run set, File empty) or a script file discovered in _pre/ or _post/ (File set, IsFile true).
func CollectHooks ¶
CollectHooks returns every hook the template would run, in execution order: inline [[pre]] steps, then _pre/ scripts, then inline [[post]] steps, then _post/ scripts. Missing _pre/_post directories are skipped.
type IncludeRule ¶
IncludeRule gates files or directories on a param-driven condition. Path is a glob relative to _base/. If is non-empty it is rendered as a Go template against the resolved values; the file/directory is included only when the result is truthy. An empty If always includes.
type Loader ¶
type Loader struct {
CacheDir string // where to store cloned templates; defaults to ~/.config/spin/templates
// PromptInvalidPinned is called when a template exists on disk but
// fails validation. It returns true to keep the clone, false to
// remove it, or a non-nil error to surface directly. A nil hook
// keeps the clone (used by non-interactive runs and tests).
PromptInvalidPinned func(name, localPath string, detectErr error) (bool, error)
// PromptExistingDest is called when cloneGit finds dest already
// exists. A nil hook wipes and re-clones, which suits scripts/CI.
PromptExistingDest func(name, localPath string) (DestAction, error)
}
Loader fetches a template from a local path, a git URL, or a name in ~/.config/spin/pinned.json, and returns it ready to render.
func (*Loader) Clear ¶
Clear removes the cached clone of the given ref (the sanitised directory name produced by SanitiseRepoName). Used by tests to keep the cache clean between runs. No-op if the ref is not cached.
func (*Loader) Lister ¶
Lister returns the basenames of all top-level entries in the loader's cache directory. Used by tests to assert cache behaviour without exposing the cache dir directly.
func (*Loader) Load ¶
Load fetches a template by source spec using a background context. Prefer LoadContext when a cancellable context is available.
func (*Loader) LoadContext ¶
LoadContext fetches a template by source spec. The spec can be a local path, a git URL, a `<alias>/<id>` registry shorthand, or a pinned name from ~/.config/spin/pinned.json. ctx bounds any git clone the loader performs.
type PostStep ¶
type PostStep struct {
Run string `toml:"run"`
}
PostStep is one command in the post-scaffold hook. The shell command is templated against the resolved param + flag values, then run via `sh -c` in the project root. Steps execute in order; the hook stops on the first failure.
This is intentionally a list, not a single string -- it matches the shape npm scripts, Taskfile.yml, and Just converged on, and gives a clean path to per-step metadata (env, cwd, on_error) without a breaking schema change.
type PreStep ¶
type PreStep struct {
Run string `toml:"run"`
}
PreStep is one command in the pre-scaffold hook. It runs after params are resolved but before files are rendered, via sh -c in the project root. Steps execute in order; the hook stops on the first failure.
type SpinToml ¶
type SpinToml struct {
Name string `toml:"name"`
Description string `toml:"description"`
Type string `toml:"type"` // "tui" | "cli" | "lib" | ...
Language string `toml:"language"` // "go" | "rust" | "ts" | ...
Author Author `toml:"author"`
License string `toml:"license"`
Repository string `toml:"repository"`
MinSpinVersion string `toml:"min_spin_version"`
Exclude []string `toml:"exclude"`
Include []IncludeRule `toml:"include"`
Params map[string]params.Spec `toml:"params"`
Pre []PreStep `toml:"pre"`
Post []PostStep `toml:"post"`
Tags []string `toml:"tags"`
}
SpinToml is the parsed manifest at the root of an external template.
Example:
name = "rust-cli"
description = "Minimal Rust CLI"
type = "cli"
language = "rust"
license = "MIT"
repository = "https://github.com/me/rust-cli-template"
min_spin_version = "0.2.0"
[author]
name = "Sam"
email = "sam@example.com"
url = "https://sam.example.com"
[params]
project_name = { type = "text", prompt = "Project name" }
edition = { type = "select", options = ["2021", "2024"], default = "2021" }
[[post]]
run = "cargo init --name {{.project_name}}"
[[post]]
run = "git init && git add -A"
func ParseSpinToml ¶
ParseSpinToml reads and parses a spin.toml file from disk.
func ParseSpinTomlBytes ¶
type Template ¶
type Template struct {
Name string // dir name, e.g. "rust-cli"
Source string // local path on disk (post-clone)
Repo string // git URL, if any
Spec string // original spec the user typed (may differ from Repo/Source when resolved via a registry shorthand)
SpinToml *SpinToml // parsed spin.toml
BaseDir string // _base/ inside Source
PreHookDir string // _pre/ inside Source (optional)
PostHookDir string // _post/ inside Source (optional)
}
Template is a loaded external template, ready to render.
func (*Template) BuildForm ¶
BuildForm constructs a huh.Form from the template's spin.toml params. The user fills the form; the resolved values are written back into the supplied map.
func (*Template) Hints ¶
Hints returns a one-line-per-param summary, used by `spin new <template> --print-params` and the template README.
func (*Template) Render ¶
Render walks the template's _base/ tree, rendering each .tmpl file against the supplied values. The output is a rel-path → bytes map.
values is the resolved param + flag map. Keys ending in `.Name` are also available as `.Name` for backwards compat with the existing scaffold package.
Files whose path (relative to _base/, with the .tmpl extension stripped) matches any glob in t.SpinToml.Exclude are skipped - they never reach the output tree. This is how templates opt out of files (e.g. a CI badge, a contributor list) that should stay out of the generated project.
If t.SpinToml.Include rules exist, only files matching at least one true rule are included. A rule with an empty If always includes.
func (*Template) RenderTo ¶
RenderTo writes the rendered files to dest. Same path-traversal guard as scaffold.emit.
func (*Template) RenderToWithPost ¶
func (t *Template) RenderToWithPost(ctx context.Context, dest string, values map[string]any, opts HookOptions) error
RenderToWithPost is the full v2.0 template pipeline:
- Render the template to an in-memory file map.
- Write the files to dest (path-traversal-safe via writeFiles).
- Run the post-hook (if any) in dest.
- Walk dest and delete every spin.toml file found (TPL-16: "spin.toml is deleted from the output after a successful render").
Returns the first non-nil error encountered. The post-hook and the spin.toml deletion are best-effort cleanup operations: if the post-hook fails, the spin.toml deletion still runs.
func (*Template) ResolveForm ¶
ResolveForm runs the form (or applies defaults in non-interactive mode) and returns the resolved values ready for Render().
Returned values are unwrapped to raw Go primitives (string, int, bool, []string) so text/template rendering produces sensible output (e.g. `{{.project_name}}` interpolates as the name, not the params.Value struct dump).
Order of operations is significant: defaults are applied first, THEN any caller-supplied values are layered on top. This ensures explicit values from the CLI or pre-apply map win over the template's own defaults.