diff --git a/bin/rig b/bin/rig index 690432e..70d2513 100755 --- a/bin/rig +++ b/bin/rig @@ -95,6 +95,12 @@ commands: Shut root SSH on a class=human box once an admin key works. Refuses on class=server — root there is the control plane's automation door — and while no admin holds a key. Run as root. + template-lint ... + Validate tenant-role definitions (the heavy-duty/rig-templates + shape): template.env against the allowlist (data, never sourced), + install.sh present with a shebang, creds.md non-blank. Every refusal + names the failing key. The registry repo's CI runs this on every PR; + no root, no network, no writes. manifest [] Print /etc/rig/manifest — which rig converged this machine and when (bootstrapped_by/_at pin the FIRST convergence forever; converged_by/_at @@ -455,6 +461,10 @@ case "$cmd" in ;; esac ;; + template-lint) + shift + exec "$ROOT/commands/template-lint.sh" "$@" + ;; manifest) shift exec "$ROOT/commands/manifest.sh" "$@" diff --git a/commands/template-lint.sh b/commands/template-lint.sh new file mode 100755 index 0000000..3350130 --- /dev/null +++ b/commands/template-lint.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# rig template-lint ... — is this a valid tenant-role definition? +# +# rig defines what a valid template is (the schema lives in +# lib/templates.sh, beside the mint-time parser that enforces it); the +# heavy-duty/rig-templates repo's CI runs this on every definition on every +# PR, so a broken definition is refused before it can ever reach a mint +# (#110). The two gates are deliberate: CI protects the registry, the +# mint-time parse protects a mint served through RIG_TEMPLATES_REPO/_DIR +# that CI never saw. +# +# Pure read: no root, no network, no writes — lintable anywhere, including +# the registry repo's checkout, where rig's tree is only a fetched tool. +set -euo pipefail + +HERE="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)" +# shellcheck source=SCRIPTDIR/lib/templates.sh +. "$HERE/lib/templates.sh" # template_lint (and the schema it enforces) + +die() { printf 'rig-template-lint: ERROR: %s\n' "$1" >&2; exit "${2:-1}"; } + +usage() { + cat <<'EOF' +usage: rig template-lint ... + +Validate tenant-role definitions (the heavy-duty/rig-templates shape): +each must carry a family-suffixed name (rig#76), a template.env +that parses against the allowlist (KEY="value" only — the file is data, +never sourced), an install.sh with a shebang, and a non-blank creds.md. +Every refusal names the failing key or file. Exits non-zero if any +definition fails; nothing is written. +EOF +} + +case "${1:-}" in + -h|--help) usage; exit 0 ;; + "") usage >&2; die "at least one role directory required" 2 ;; +esac + +fail=0 +for dir in "$@"; do + case "$dir" in + -*) usage >&2; die "unknown flag: $dir" 2 ;; + esac + if template_lint "$dir"; then + printf 'rig-template-lint: OK: %s\n' "$dir" + else + printf 'rig-template-lint: FAIL: %s\n' "$dir" >&2 + fail=1 + fi +done +exit "$fail"