simpler check and deps messages
This commit is contained in:
@@ -1,20 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
# What rig has settled, written down as assertions.
|
||||
#
|
||||
# These are documentation that runs. Each check is ONE decision that has already
|
||||
# been made, with the reason above it — not coverage, and deliberately not an
|
||||
# exhaustive sweep of use cases. rig's own index says a rule without its reason
|
||||
# gets overridden the first time it is inconvenient; a rule nobody can restate
|
||||
# is worse. So the test says what was decided, and failing it should read as
|
||||
# "you are about to undo this" rather than "something broke".
|
||||
#
|
||||
# Scope, on purpose:
|
||||
# - no cluster, no docker, no network. It must be cheap enough to actually run.
|
||||
# - it asserts about RIG. `make check` asserts about the MACHINE and never
|
||||
# fails; this exits 1, the way `make standalone check` does.
|
||||
# - what actually deploys is not testable here. `tilt ci` stays a manual step.
|
||||
#
|
||||
# What rig has settled, written down as assertions: one decision per check.
|
||||
# No cluster, docker or network; exits 1 on failure (unlike `make check`).
|
||||
# Usage: make selftest (or: bash ctrl/selftest.sh)
|
||||
# Notes: docs/notes/selftest.md
|
||||
set -uo pipefail # NOT -e: one failing check must not abort the rest
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
@@ -43,10 +31,7 @@ resolved() {
|
||||
|
||||
|
||||
note "rig needs no profile"
|
||||
# rig assumes no configuration. A profile is an overlay on built-in defaults, so
|
||||
# a rig with no env.d/ at all must resolve, report, and still generate a kit —
|
||||
# and naming a profile that does not exist must still be an error, because a
|
||||
# typo that silently fell back to the defaults would be worse than a failure.
|
||||
# No env.d/ must still resolve and generate a kit; an unknown profile stays an error.
|
||||
NP="$(mktemp -d)"
|
||||
cp -r .. "$NP/rig"; rm -rf "$NP/rig/ctrl/env.d"; sed -i '/^PROFILE=/d' "$NP/rig/ctrl/.env" 2>/dev/null
|
||||
check "no env.d: config resolves" "default" \
|
||||
@@ -63,11 +48,7 @@ rm -rf "$NP"
|
||||
|
||||
|
||||
note "the ports.sh active contract"
|
||||
# ports.sh active is read POSITIONALLY by two other files — the Makefile takes
|
||||
# $(word 2) and $(word 5), the Tiltfile takes _facts[0]..[6]. Insert a field in
|
||||
# the middle and nothing errors: Tilt simply guards on the wrong context or
|
||||
# binds the wrong port. The field count and order are the contract, so they are
|
||||
# pinned here rather than left to whoever edits ports.sh next.
|
||||
# ports.sh active is read positionally by the Makefile and Tiltfile: pin field count and order.
|
||||
FACTS="$(bash ports.sh active)"
|
||||
check "active: exactly 7 fields" "7" "$(printf '%s' "$FACTS" | wc -w)"
|
||||
read -r F_CLUSTER F_CTX F_HTTP F_HTTPS F_TILT F_REG F_MANIFESTS <<< "$FACTS"
|
||||
@@ -83,16 +64,7 @@ check "derive: still 4 fields, not 7" "4" "$(bash ports.sh derive | wc -w)"
|
||||
|
||||
|
||||
note "the caller's env beats the files"
|
||||
# lib/config.sh states one precedence rule: versions.env < env.d/<profile> <
|
||||
# ctrl/.env < the caller's env. It is enforced by CONFIG_OVERRIDABLE, a
|
||||
# hand-maintained list — and a key missing from it loses to the file SILENTLY.
|
||||
# REGISTRY_PORT and MANIFESTS_DIR were both missing on 2026-09-13 and were found
|
||||
# by accident.
|
||||
#
|
||||
# So this loop is generated FROM the list: add a key to CONFIG_OVERRIDABLE and
|
||||
# this test starts asking about it without anyone remembering to come here.
|
||||
# Three keys name something that must exist and are validated at load, so they
|
||||
# get a real alternative rather than a sentinel.
|
||||
# Every key in CONFIG_OVERRIDABLE must lose to the caller's env; the loop follows the list.
|
||||
test_value() {
|
||||
case "$1" in
|
||||
# Picked from what exists, never named: rig must not need any particular
|
||||
@@ -122,16 +94,8 @@ done
|
||||
|
||||
|
||||
note "one derivation, not three"
|
||||
# The Makefile used to compute the cluster name itself and sed TILT_PORT out of
|
||||
# ctrl/.env — a second derivation of values lib/config.sh already owns, which
|
||||
# could disagree with it after `ports.sh persist`. It now reads ports.sh
|
||||
# active. Nothing structurally prevents the sed coming back, so the agreement is
|
||||
# asserted against the real `make -n` output rather than against the source.
|
||||
# --no-print-directory and a grep, not `tail -1`: run from `make selftest` this
|
||||
# is a RECURSIVE make, and the "Entering/Leaving directory" lines go to STDOUT.
|
||||
# tail -1 then reads "make[1]: Leaving directory ..." and both checks below fail
|
||||
# — but only when invoked through make, never when the script is run directly.
|
||||
# A test that passes one way and fails the other is worse than no test.
|
||||
# The Makefile must take context/port from ports.sh active, checked on real `make -n` output.
|
||||
# --no-print-directory + grep, not tail -1: under `make selftest` this is a recursive make.
|
||||
MK="$(cd .. && make --no-print-directory -n tilt 2>/dev/null | grep -m1 'tilt ')"
|
||||
check "Makefile: --context comes from active" "$F_CTX" \
|
||||
"$(printf '%s' "$MK" | sed -n 's/.*--context \([^ ]*\).*/\1/p')"
|
||||
@@ -140,10 +104,7 @@ check "Makefile: --port comes from active" "$F_TILT" \
|
||||
|
||||
|
||||
note "identity follows the folder, safely"
|
||||
# The cluster name is NOT the bare directory name: kind needs a DNS label, so
|
||||
# default_cluster_name lowercases it and replaces everything outside [a-z0-9-].
|
||||
# Re-deriving that anywhere else is how a copy ends up guarding the wrong
|
||||
# context — which is exactly why the Tiltfile asks instead of computing.
|
||||
# The cluster name is the folder name made a DNS label, derived only in lib/config.sh.
|
||||
TMP="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMP"' EXIT
|
||||
mkdir -p "$TMP/My_Proj"
|
||||
@@ -159,52 +120,33 @@ check "a renamed copy gets a DIFFERENT block" "different" \
|
||||
|
||||
|
||||
note "ports are stable across versions"
|
||||
# Not a change-detector. The block is derived, never stored, so if the
|
||||
# derivation shifts then every EXISTING environment's ports move underneath it —
|
||||
# a running cluster keeps its old ports while rig starts reporting new ones, and
|
||||
# `ports.sh show` stops describing reality. Anchored to three known names.
|
||||
# Ports are derived, never stored: a changed derivation moves every existing env's ports.
|
||||
check "derive_port_base rig" "20310" "$(derive_port_base rig)"
|
||||
check "derive_port_base foo" "21690" "$(derive_port_base foo)"
|
||||
check "derive_port_base my-proj" "21030" "$(derive_port_base my-proj)"
|
||||
|
||||
|
||||
note "rig stays standalone"
|
||||
# rig sits inside a host project's tree but must be copyable straight out of it:
|
||||
# no imports, no paths, no assumption the host is there. This grep is the whole
|
||||
# test of that claim, and until now it lived only in prose and in whoever
|
||||
# remembered to run it.
|
||||
#
|
||||
# The pattern is assembled from fragments so this file does not match ITSELF.
|
||||
# Writing it literally would fail forever; excluding this file instead would put
|
||||
# a blind spot in the one check that guards the boundary.
|
||||
# rig must be copyable out of its host project: no references to the host.
|
||||
# The pattern is assembled from fragments so this file does not match itself.
|
||||
HOST_PAT="$(printf '%s' 'sole' 'print' '|\b' 'sp' 'r\b')"
|
||||
check "no host-project references" "0" \
|
||||
"$(cd .. && grep -rIl -iE "$HOST_PAT" . --exclude-dir=def 2>/dev/null | wc -l)"
|
||||
|
||||
|
||||
note "the Tiltfile hardcodes nothing"
|
||||
# Every other Tiltfile on this machine writes its slug in five or six times by
|
||||
# hand, so a copied project deploys into the original's cluster until someone
|
||||
# edits all of them. rig's asks ports.sh. A literal kind-<name> here would mean
|
||||
# that has been undone.
|
||||
# The Tiltfile asks ports.sh for its context; a literal kind-<name> would undo that.
|
||||
check "no literal kind-<name>" "0" "$(grep -cE "['\"]kind-[a-z0-9]" Tiltfile)"
|
||||
check "guards on the variable" "1" "$(grep -c 'allow_k8s_contexts(CTX)' Tiltfile)"
|
||||
check "asks ports.sh for facts" "1" "$(grep -c "local('bash ports.sh active'" Tiltfile)"
|
||||
|
||||
|
||||
note "standalone kits are generated, current, and call only real verbs"
|
||||
# The kits under standalone/<profile>/ are rig flattened into single files, one
|
||||
# per profile. A kit left behind by a change to rig is exactly the drift they
|
||||
# replaced — rigmini.sh once said 2 GB per node long after rig measured 800 MB —
|
||||
# so a stale kit fails here rather than waiting to be noticed on another machine.
|
||||
# A kit left stale by a change to rig fails here, not on another machine.
|
||||
check "every kit matches what rig generates now" "yes" \
|
||||
"$(bash standalone.sh check >/dev/null 2>&1 && echo yes || echo "no — run make standalone")"
|
||||
|
||||
# Each kit's Makefile exists so nothing wrapping these scripts has to GUESS how to
|
||||
# call them. A generated Makefile once did guess: `rigmini.sh on`, not a verb,
|
||||
# and a bare `rigdeps.sh` for "check and report", which installs. So every
|
||||
# target's default verb must be one its script's own dispatch accepts — read
|
||||
# from that dispatch, not from a list here that could drift from it.
|
||||
# Every kit Makefile target must call a verb its script's own dispatch accepts.
|
||||
verbs_of() {
|
||||
sed -n '/^case "\$cmd" in/,/^esac/p' "$1" | grep -oE '^ [a-z]+\)' | tr -d ' )'
|
||||
}
|
||||
@@ -224,12 +166,8 @@ for mk in ../standalone/*/Makefile; do
|
||||
done
|
||||
check "there is a kit for every profile" "$(config_profiles | wc -l)" "$kits"
|
||||
|
||||
# An export is "take the setup I have here somewhere else", so it carries this
|
||||
# machine's CHOICES — profile, ports, manifest dir — and never its credentials:
|
||||
# ctrl/.env can hold registry and mirror logins next to those choices. The
|
||||
# committed per-profile kits carry neither, since they must be the same on any
|
||||
# machine. Proven with sentinel values in a scratch copy, because the real
|
||||
# ctrl/.env may have those keys empty — and an empty value proves nothing.
|
||||
# An export carries this machine's choices but never its credentials; committed kits carry neither.
|
||||
# Proven with sentinel values in a scratch copy, since the real ctrl/.env may leave them empty.
|
||||
SX="$TMP/export-proof"; mkdir -p "$SX"; cp -r .. "$SX/rig"
|
||||
cat >> "$SX/rig/ctrl/.env" <<'EOF'
|
||||
REGISTRY_USER=selftest-sentinel-user
|
||||
@@ -250,10 +188,7 @@ check "export: refuses to write inside the repository" "yes" \
|
||||
|
||||
|
||||
note "optional — needs tilt and this rig's cluster"
|
||||
# Parsing the Tiltfile for real is the only way to know it still evaluates, but
|
||||
# Tilt snapshots a kubectl context before parsing, so it cannot run without a
|
||||
# cluster. Skipped rather than failed when there is none, the same way docgen
|
||||
# skips its graphgen section.
|
||||
# Tilt needs a cluster context to parse the Tiltfile, so this is skipped without one.
|
||||
if ! command -v tilt >/dev/null; then
|
||||
printf ' skip tilt is not installed\n'
|
||||
elif ! kubectl config get-contexts -o name 2>/dev/null | grep -qx "$F_CTX"; then
|
||||
|
||||
Reference in New Issue
Block a user