Hooks
[[pre]]runs after params are resolved but before files are rendered or written.[[post]]runs after files are written but before the outputspin.tomlis removed.
Trust and safety
Every hook command is shown to you before it runs. When you open the hooks review screen (the second step of spin new), you see a scrollable list of every [[pre]] and [[post]] command the template will execute, including hook scripts from _pre/ and _post/. You can inspect them all and choose between:
- Run — execute all hooks and scaffold the project
- Skip — scaffold the project but skip all hooks
- Cancel — do nothing, dismiss the dialog
No shell command runs without your review. This is intentional: spin treats every template as potentially untrusted and gives you full visibility before any code executes.
Spin also rejects any rendered file path that resolves outside the destination directory, protecting you from path traversal attacks in malicious templates.
Quick example
[[pre]]
run = "mkdir -p cmd"
[[post]]
run = "go mod init {{ .project_name }}"
[[post]]
run = "git init && git add -A && git commit -m 'initial'"
Commands run in order. The first failure stops the hook chain.
Pre-hooks
Use pre-hooks to prepare the destination directory before files land. They can create directories, initialise package managers that need a project root, or write files that later .tmpl files can reference.
[[pre]]
run = "go mod init {{ .project_name }}"
If a pre-hook fails, no files are written and no post-hooks run.
Post-hooks
Use post-hooks to finish the project after files are written.
[[post]]
run = "go mod init {{ .project_name }}"
[[post]]
run = "git init && git add -A && git commit -m 'initial'"
If a post-hook fails, the rendered files remain on disk so you can inspect or fix the state.
Templating
Hook commands are rendered through text/template with the same param values used for file rendering, but only field access is available:
[[post]]
run = "echo {{ .project_name }}"
Custom functions like upper, quote, and snake_case are only available in _base/*.tmpl file templates, not in hook commands. If you need a transformed value in a hook, use shell tools:
[[post]]
run = "echo {{ .project_name }} | tr '[:lower:]' '[:upper:]'"
Hook scripts
For non-trivial hooks you can drop scripts in _pre/ and _post/ instead of inlining everything in spin.toml:
my-template/
spin.toml
_base/
_pre/
01-mkdir.sh
02-init.sh
_post/
setup.sh
cleanup.sh
spin runs every file in _pre/ and _post/ automatically as the last pre-hook and post-hook steps, sorted alphabetically. Inline [[pre]]/[[post]] commands and hook scripts can be used together.
- Executable files are run as
./_pre/<file>or./_post/<file>. - Non-executable files are run with
sh. - Hidden files and subdirectories are ignored.
Skipping hooks
Pass --no-hooks to spin new to skip both [[pre]] and [[post]] hooks and hook scripts. Pass --verbose to print hook output.
Failure behavior
If a hook fails, spin reports the command and its combined output. Pre-hook failures stop before any files are written; post-hook failures leave the rendered files in place.