Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Key variables available in `.tmpl` files:
```text
package.json # Dev-tooling devDependencies; Renovate-managed
pnpm-workspace.yaml # pnpm catalogs: dev toolchain + globals (pnpm add -g)
scripts/ # Repo-local dev/lint scripts wired into hk (not deployed)
home/
├── .chezmoi.yaml.tmpl # Chezmoi config; prompts for hosttype on first run
├── .chezmoiexternal.yaml.tmpl # External resources fetched during apply (skills, vim-plug, ~/Code dev repos)
Expand Down
5 changes: 5 additions & 0 deletions hk.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ local allSteps = new Mapping<String, Step> {
glob = List("mise.toml", "mise.tasks.toml")
check = "gtb verify mise"
}

["render-templates"] {
glob = List("**/*.tmpl")
check = "sh scripts/lint-templates.sh {{files}}"
}
}

hooks = Defaults.hooksFor(allSteps)
24 changes: 24 additions & 0 deletions mise.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions mise.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ includes = ["mise.tasks.toml"]

[tools]
actionlint = "1.7.12"
# Backs the hk render-templates step; also lets CI render templates without a
# separate chezmoi install.
chezmoi = "2.70.5"
hk = "1.48.0"
node = "24.18.0"
"npm:@gtbuchanan/cli" = "0.2.0"
Expand Down
72 changes: 72 additions & 0 deletions scripts/lint-templates.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/sh
# Prove every source template renders. chezmoi's funcMap (include, sprig,
# .chezmoi.*, .chezmoidata) means a bare text/template parse is insufficient, so
# each template is rendered through `chezmoi execute-template`, which fails on
# syntax errors and undefined refs. This validates that templates render; it is
# not a lint of the rendered output.
#
# `chezmoi init` normally writes a config whose data section (hosttype and the
# values derived from it) most templates read; that config doesn't exist in CI.
# Regenerate it from the config template into a throwaway file and render every
# content template against it, so the check behaves the same locally and in CI.
#
# The config template answers a hosttype prompt via promptChoiceOnce, whose
# --promptChoice simulation is keyed by the prompt text (not the data key), so
# the text is derived from the template below rather than hardcoded, to avoid
# drift. On a machine that already ran `chezmoi init`, the cached answer wins
# and this is ignored; CI has no cache, so this default applies. Only one
# representative hosttype/OS variation is exercised — conditional branches for
# other hosttypes/OSes are not covered.
set -u

work=$(mktemp -d)
trap 'rm -rf "$work"' EXIT

# chezmoi source root. hk runs from the repo root, but a CI checkout isn't at
# chezmoi's default source dir, so pass --source explicitly.
src_root=$PWD

config_tmpl=home/.chezmoi.yaml.tmpl
hosttype=${LINT_HOSTTYPE:-personal}
hosttype_prompt=$(sed -n 's/.*promptChoiceOnce \. "hosttype" "\([^"]*\)".*/\1/p' "$config_tmpl")
if [ -z "$hosttype_prompt" ]; then
printf 'could not find the hosttype prompt in %s\n' "$config_tmpl" >&2
exit 1
fi

chezmoi_config=$work/chezmoi-config.yaml
if ! err=$(chezmoi execute-template --init --no-tty --promptChoice "$hosttype_prompt=$hosttype" \
--source "$src_root" <"$config_tmpl" 2>&1 >"$chezmoi_config"); then
printf 'config generation failed (%s):\n%s\n' "$config_tmpl" "$err" >&2
exit 1
fi

rc=0

# Render one template, reporting a chezmoi failure. Args after the source path
# are passed to chezmoi execute-template.
render() {
src=$1
shift
if ! err=$(chezmoi execute-template "$@" --source "$src_root" <"$src" 2>&1 >/dev/null); then
printf 'render failed: %s\n%s\n' "$src" "$err" >&2
rc=1
fi
}

for src in "$@"; do
# The config template (.chezmoi.<format>.tmpl) uses init-only prompt functions
# and renders without .chezmoidata/.chezmoitemplates, so it renders in --init
# mode with the hosttype above. Every other template renders against the
# generated config.
case "$src" in
*/.chezmoi.*.tmpl | .chezmoi.*.tmpl)
render "$src" --init --no-tty --promptChoice "$hosttype_prompt=$hosttype"
;;
*)
render "$src" --config "$chezmoi_config"
;;
esac
done

exit "$rc"