Author and apply custom template overlays¶
The generator renders a fixed embedded skeleton. Custom template overlays let you extend it with your own files β an organisation SECURITY.md, a house-style CODEOWNERS, an .editorconfig, a Dockerfile, or a wholesale-replacement CI pipeline β without forking GTB.
A template source is a directory tree (a local folder or a git repo). The generator walks every file and renders each one through the same text/template engine to the identical relative path in your project:
- A path that does not exist in the skeleton adds a file.
- A path that also exists in the skeleton overwrites it β your deviation wins.
Two reserved root meta files are never rendered: README.md (a human explainer of the set) and gtb-template.yaml (the descriptor).
Apply a source at generate time¶
gtb generate project -n mytool -r acme/mytool \
--template ./house-templates # local folder
# repeatable; layered in order:
# --template acme/[email protected] # forge repo, pinned to a ref
<src>@<ref> β <src> is a local path or a forge repo (org/repo, nested GitLab groups, or a full https:///ssh:// URL); @<ref> is optional and defaults to the source's default branch. A remote source prompts a one-time trust confirmation before it is fetched (skipped under --ci).
Manage sources on an existing project¶
gtb template add ./house-templates --name house # add, pin, regenerate
gtb template list # show sources, refs, resolved commits
gtb template update house # re-resolve a git ref to a new commit
gtb template remove house # drop the source (restoring any scaffold it replaced)
All four resolve to the same manifest representation β hand-editing the properties.templates: block and running gtb regenerate is an equivalent, supported path.
Replace a whole embedded CI scaffold¶
An overlay can only add or overwrite, never delete. To fully replace an embedded forge-CI scaffold (where your pipeline has fewer/differently-named files than the embedded tree), declare it in a root gtb-template.yaml:
# gtb-template.yaml at the source root β NOT rendered
contract: 1 # the data-contract version this set targets
description: "ACME house CI + extra scaffolding"
replaces:
- gitlab-ci # suppress the whole embedded .gitlab/ + .gitlab-ci.yml + renovate.json5
# - github-ci # suppress the whole embedded .github/
replaces: suppresses the named embedded scaffold before the overlay renders, so your source supplies the complete CI with no stranded embedded files. The two named sets are gitlab-ci and github-ci. Removing the source later restores the embedded scaffold.
The data contract¶
Overlay templates render against a stable, metadata-only, secret-free subset of the generator's data β never any token, credential, env var, or absolute path:
{{ .Name }} {{ .Description }} {{ .Repo }} {{ .Host }} {{ .Org }} {{ .RepoName }} {{ .ModulePath }} {{ .ReleaseProvider }} {{ .GoVersion }} {{ .GoToolBaseVersion }} {{ .EnvPrefix }} {{ .EnabledFeatures }} {{ .DisabledFeatures }} {{ .Private }} {{ .SigningEnabled }} {{ .HelpType }}
The restricted template FuncMap exposes the escape helpers (escapeYAML, escapeMarkdown, β¦) plus pure string helpers (lower, upper, join, replace, β¦). There is no file/exec/env/network function. Declare the contract: version in your descriptor; GTB rejects an unknown version.
Reproducibility and offline behaviour¶
- A git source is pinned:
ref(what you asked for) plusresolved(the commit SHA it resolved to).gtb regeneratereads the SHA, so output is byte-stable across time even asmainmoves. Sources are cached at$XDG_CACHE_HOME/gtb/templates/<host>/<owner>/<repo>@<sha>; a warm cache makes regenerate fully offline. Onlygtb template updateadvances the pin. - A local source has no SHA; GTB records a content fingerprint and
gtb regeneratewarns if the on-disk source has drifted. - An offline cold cache for a
replaces:/overwriting git source is a clear error β GTB never silently restores the suppressed embedded scaffold.
Security: custom templates run as trusted input¶
Custom overlays execute text/template content GTB did not author. The posture is trusted-source with bounded blast radius, not a sandbox. GTB enforces:
- Write-path containment under the project root β a source rendering to
../escapeor an absolute path is refused. - A protected-path denylist β an overlay may never write
.gtb/**,internal/trustkeys/**, orgo.mod/go.sum, even when it otherwise overwrites freely. - A restricted FuncMap and a metadata-only data contract (no secrets reachable).
- Inert fetch (no hooks, filters, or submodule recursion) and a per-file size bound.
A custom template's output correctness is the author's responsibility β GTB guarantees where output lands and what data a template sees, not the bytes it emits. See Template Security for the full threat model.