Files
soleprint/rig/docs/notes/selftest.md

5.9 KiB

ctrl/selftest.sh

Purpose

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.

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. 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.

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.

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 the loop is generated FROM the list: add a key to CONFIG_OVERRIDABLE and the 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.

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 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.

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.

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.

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 it was added it lived only in prose and in whoever remembered to run it.

The pattern is assembled from fragments so the file does not match ITSELF. Writing it literally would fail forever; excluding the file instead would put a blind spot in the one check that guards the boundary.

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> in it would mean that has been undone.

standalone kits are generated and current

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.

kit Makefiles call only real verbs

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 that could drift from it.

export carries choices, not credentials

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.

optional: Tiltfile evaluates

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.