diff --git a/scripts/opentofu-validate.sh b/scripts/opentofu-validate.sh index cf03b4c..67dabe5 100644 --- a/scripts/opentofu-validate.sh +++ b/scripts/opentofu-validate.sh @@ -185,6 +185,54 @@ echo " [WARN] no modules/ dir under $TOFU_DIR -- nothing to validate standalone" fi +# EXTRA ROOTS -- the second blind spot, found 2026-07-20. This gate validated the +# OUTER root plus every module, and NOTHING ELSE. But the tree now holds two more +# roots that are applied against live infrastructure: +# opentofu/vr1-dc0-substrate/ (D-123 Model B inner substrate -- 28 resources) +# opentofu/vr1-dc0-maas/ (MAAS registration) +# Both were shipped and applied while this gate reported ALL GREEN over them, which +# is the same false-confidence failure the module loop above exists to prevent -- +# a gate that is silent about applied code is worse than no gate. +# +# A root is any directory (other than modules/ and the tree root itself) that +# contains a *.tf declaring `terraform {` -- discovered, never hardcoded, so a +# vr1-dc1-* root added later is covered the day it appears rather than the day +# someone remembers to edit this list. +# +# Validated with -backend=false in a TEMP COPY, exactly like the modules: no +# .terraform/ or lock files are written into the repo tree, and no state or +# credentials are touched (validate never contacts a provider's API). +# +# The temp copy is of the WHOLE opentofu/ tree, not the root directory alone: +# these roots reference sibling modules by RELATIVE path (`source = "../modules/..."`), +# so copying just the root yields "Unreadable module directory" -- measured, and the +# reason this loop is shaped differently from the module loop above. +echo "== tofu validate (every EXTRA root -- roots other than the tree root) ==" +extra_roots=0 +treecopy="$(mktemp -d)" +cp -R "$TOFU_DIR/." "$treecopy/" 2>/dev/null +find "$treecopy" -name '.terraform' -type d -prune -exec rm -rf {} + 2>/dev/null +# local *.auto.tfvars are gitignored operator files that may hold secrets; validate +# does not need them, so they never enter the temp tree. +find "$treecopy" -name '*.auto.tfvars' -o -name '*.auto.tfvars.json' | xargs -r rm -f +while IFS= read -r rootdir; do + [ -n "$rootdir" ] || continue + rel="${rootdir#"$TOFU_DIR"/}" + extra_roots=$((extra_roots+1)) + if out="$( cd "$treecopy/$rel" && tofu init -backend=false -input=false -no-color 2>&1 && tofu validate -no-color 2>&1 )"; then + echo " [PASS] $rel" + else + echo " [FAIL] $rel" + printf '%s\n' "$out" | grep -E "^(Error| on | [0-9]+:)" | head -8 | sed 's/^/ /' + fail=1 + fi +done </dev/null | xargs -r -n1 dirname | sort -u) +EOF +rm -rf "$treecopy" +[ "$extra_roots" -gt 0 ] || echo " (none found -- only the tree root)" + if [ "$fail" -eq 0 ]; then echo "PASS: opentofu-validate ($TOFU_DIR)" exit 0