From c9c8ad9ba9b63b7d57f69a732ed432c71715ff98 Mon Sep 17 00:00:00 2001 From: claude-bot-andresmgsl Date: Fri, 24 Jul 2026 22:54:34 +0000 Subject: [PATCH] =?UTF-8?q?feat(templates):=20rig=20template-lint=20?= =?UTF-8?q?=E2=80=94=20the=20registry=20repo's=20CI=20gate,=20dispatched?= =?UTF-8?q?=20from=20bin/rig=20(#110)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bin/rig | 10 ++++++++ commands/template-lint.sh | 52 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100755 commands/template-lint.sh 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"