diff --git a/AGENTS.md b/AGENTS.md index 440bada..44d8897 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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) diff --git a/hk.pkl b/hk.pkl index 223f5dd..77bd872 100644 --- a/hk.pkl +++ b/hk.pkl @@ -28,6 +28,11 @@ local allSteps = new Mapping { 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) diff --git a/mise.lock b/mise.lock index f29f4b0..8836e2c 100644 --- a/mise.lock +++ b/mise.lock @@ -29,6 +29,30 @@ checksum = "sha256:6e7241b51e6817ea6a047693d8e6fed13b31819c9a0dd6c5a726e1592d22f url = "https://github.com/rhysd/actionlint/releases/download/v1.7.12/actionlint_1.7.12_windows_amd64.zip" provenance = "github-attestations" +[[tools.chezmoi]] +version = "2.70.5" +backend = "aqua:twpayne/chezmoi" + +[tools.chezmoi."platforms.linux-arm64"] +checksum = "sha256:4f4f31d0a10ed3b955e814a5ae20075426e27c9d3a09f536bfa4a6c8718353f2" +url = "https://github.com/twpayne/chezmoi/releases/download/v2.70.5/chezmoi_2.70.5_linux_arm64.tar.gz" + +[tools.chezmoi."platforms.linux-x64"] +checksum = "sha256:6a76a0ac3718f0d45b34b4b57067f9556f8f6042e3da710a3c496838362aca14" +url = "https://github.com/twpayne/chezmoi/releases/download/v2.70.5/chezmoi_2.70.5_linux_amd64.tar.gz" + +[tools.chezmoi."platforms.macos-arm64"] +checksum = "sha256:c3531896769175089b9b2e9cf99b15a2357427f776289b66583a2432d69cd3cc" +url = "https://github.com/twpayne/chezmoi/releases/download/v2.70.5/chezmoi_2.70.5_darwin_arm64.tar.gz" + +[tools.chezmoi."platforms.macos-x64"] +checksum = "sha256:6676e06f094e5b0860bd3642d203b71869b5cbeb8089155886bc54c1096b9d3c" +url = "https://github.com/twpayne/chezmoi/releases/download/v2.70.5/chezmoi_2.70.5_darwin_amd64.tar.gz" + +[tools.chezmoi."platforms.windows-x64"] +checksum = "sha256:ebc29455dc9106e8d2d980708b45bd3120e44c7d5e6a06787ff307faaa0681a8" +url = "https://github.com/twpayne/chezmoi/releases/download/v2.70.5/chezmoi_2.70.5_windows_amd64.zip" + [[tools.hk]] version = "1.48.0" backend = "aqua:jdx/hk" diff --git a/mise.toml b/mise.toml index 16330b4..7fbf0db 100644 --- a/mise.toml +++ b/mise.toml @@ -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" diff --git a/scripts/lint-templates.sh b/scripts/lint-templates.sh new file mode 100644 index 0000000..3757140 --- /dev/null +++ b/scripts/lint-templates.sh @@ -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..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"