feat: rig platform — what is this machine, computed not stored #74
5 changed files with 440 additions and 0 deletions
39
CHANGELOG.md
39
CHANGELOG.md
|
|
@ -6,6 +6,45 @@ on the way to cutting its first release, and this file starts there.
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- **`rig platform` — what is this machine, calculated at run time, stored
|
||||
nowhere** (#64) — rig read no hardware at all; the single exception was
|
||||
`uname -m` in `runner-install.sh`, used to pick a tarball and then
|
||||
discarded. So "is this the 32GB one, or the M900?" was answered by logging
|
||||
in and running `free -h`, `nproc`, `df -h` and `uname -r` by hand, four
|
||||
commands deep, on a machine you were already unsure about. `rig platform`
|
||||
prints hostname, OS, kernel, CPU, memory, disk and virtualization, then a
|
||||
provenance block (which rig, when, and the role marker's traits). It
|
||||
**computes rather than stores**: specs change without rig doing anything —
|
||||
RAM added, root disk resized, the unattended-upgrades bootstrap itself
|
||||
enables patching the kernel — so a stored spec is stale the moment the
|
||||
machine changes, and refreshing one per run would collide with bootstrap's
|
||||
"a second run changes nothing" contract. Nothing is written, so nothing can
|
||||
go stale. The corollary is deliberate: reading only `/proc`, `uname`,
|
||||
`/etc/os-release`, `df` and `systemd-detect-virt` means it needs no root,
|
||||
makes no network call, and **runs on a pristine Debian box rig has never
|
||||
bootstrapped** — useful for deciding what to converge a machine into, not
|
||||
only for auditing it afterwards. That also makes it the rare rig command
|
||||
the harness can RUN for real instead of grepping: the tests assert the
|
||||
actual answer describes the actual test machine. Provenance is read, never
|
||||
written, and degrades per-file — `/etc/rig/manifest` is #61 and does not
|
||||
exist yet, so those lines read `not bootstrapped` on every machine today and
|
||||
nothing else depends on it. The reader is keyed to #61's documented schema
|
||||
(`schema`, `bootstrapped_by`/`_at`, `converged_by`/`_at`) and fixtures pin
|
||||
that exact spelling, so the integration cannot land silently broken; birth
|
||||
and latest stay separate rather than one being inferred from the other. A
|
||||
fresh machine writes both pairs equal, so two identical lines read as
|
||||
"never re-converged"; a manifest missing the pair is partial rather than
|
||||
fresh, and says so instead of backfilling from birth. Named `platform` and not `status` on purpose:
|
||||
`users status` and `runner status` cross-check recorded against live state
|
||||
and print `DRIFT`, and a command that records nothing cannot drift — which
|
||||
also leaves `rig status` free for the machine-wide roll-up. Known
|
||||
limitation, stated rather than guessed at: `CPU`/`MEMORY` are read from
|
||||
`/proc` with no cgroup awareness, and whether an `lxc` guest sees its own
|
||||
limits or the host's totals depends on whether `lxcfs` is in play — it is
|
||||
unverified, so those two lines are unreliable there.
|
||||
|
||||
### Changed
|
||||
|
||||
- **BREAKING: `--class human|server` is now `--root-door closed|open`** (#77) —
|
||||
|
|
|
|||
96
README.md
96
README.md
|
|
@ -719,6 +719,102 @@ default, dumps it, restores into a second container whose superuser differs, and
|
|||
asserts the rows and an ordered checksum survived — the same proof, done against
|
||||
throwaway containers on every push.
|
||||
|
||||
### `rig platform`
|
||||
|
||||
```sh
|
||||
rig platform
|
||||
```
|
||||
|
||||
What is this machine — computed at run time, **stored nowhere**:
|
||||
|
||||
```
|
||||
PLATFORM
|
||||
HOSTNAME hetzner-cp-1
|
||||
OS Debian GNU/Linux 13 (trixie)
|
||||
KERNEL 6.12.95+deb13-amd64 (x86_64)
|
||||
CPU AMD Ryzen 7 3700X 8-Core Processor (16 cores)
|
||||
MEMORY 31Gi total, 24Gi available
|
||||
DISK 456Gi total, 201Gi free on /
|
||||
VIRT kvm
|
||||
|
||||
PROVENANCE
|
||||
CONVERGED 0.6.0, 2026-08-02T09:11:03Z
|
||||
BOOTSTRAP 0.4.0, 2026-07-19T14:24:51Z
|
||||
ROLE dev-server (root-door=closed host=yes join=authkey)
|
||||
```
|
||||
|
||||
"Is this the 32GB one, or the M900?" was previously a question you answered by
|
||||
logging in and running `free -h`, `nproc`, `df -h` and `uname -r` by hand —
|
||||
four commands deep, on a machine you were already unsure about.
|
||||
|
||||
**Why this computes instead of storing.** It would be easy to write the specs
|
||||
into a file at bootstrap. That is the wrong shape: specs change without rig
|
||||
doing anything — someone adds RAM, resizes the root disk, or the
|
||||
unattended-upgrades that bootstrap itself enables patches the kernel. A stored
|
||||
spec is stale the moment the machine changes, and refreshing it on every run
|
||||
would collide with bootstrap's contract that a second run changes nothing.
|
||||
Computing at run time removes the problem instead of managing it: the answer
|
||||
is correct by construction because there is nothing to go stale.
|
||||
|
||||
The corollary is deliberate: **`rig platform` works on a machine rig has never
|
||||
converged.** It reads only `/proc`, `uname`, `/etc/os-release`, `df` and
|
||||
`systemd-detect-virt`, so it runs on bare Debian before bootstrap — useful for
|
||||
deciding *what to converge this into*, not just for auditing afterwards. It
|
||||
needs no root, makes no network call, and writes nothing, ever.
|
||||
|
||||
The `PROVENANCE` block is the complementary half — which rig, and when, which
|
||||
is *decided* rather than observed, so it is stored. It is **read, never
|
||||
written**: `CONVERGED`/`BOOTSTRAP` come from `/etc/rig/manifest` and `ROLE`
|
||||
from `/etc/rig/role`. Neither file is required — a machine missing one reads
|
||||
`not bootstrapped` for that line, which is itself the useful answer. The
|
||||
manifest is #61 and is not implemented yet, so today those lines read `not
|
||||
bootstrapped` on every machine; nothing else in the command depends on it.
|
||||
|
||||
**The two dates are deliberately separate**, matching #61's schema: `BOOTSTRAP`
|
||||
is birth (`bootstrapped_by`/`bootstrapped_at`, first-write-wins, pinned
|
||||
forever) and `CONVERGED` is latest (`converged_by`/`converged_at`, updated only
|
||||
when the converging version actually differs). That distinction is what answers
|
||||
"is this machine still converged by a rig that predates the fix?".
|
||||
|
||||
A **fresh machine writes both pairs with equal values**, so two identical lines
|
||||
mean "bootstrapped and never re-converged since" — not a missing record. A
|
||||
later re-converge by a different rig moves `CONVERGED` and leaves `BOOTSTRAP`
|
||||
untouched, which is the whole point of keeping them apart.
|
||||
|
||||
`CONVERGED not recorded` therefore does **not** describe a freshly
|
||||
bootstrapped box; no writer produces a manifest without the pair. It means the
|
||||
file is partial or hand-edited, and the value is deliberately not backfilled
|
||||
from `BOOTSTRAP` — inferring a convergence that never happened would be worse
|
||||
than saying so. Likewise a manifest whose `schema=` this rig does not know is
|
||||
named as such instead of being half-read in silence.
|
||||
|
||||
**Known limitation — `CPU` and `MEMORY` inside a container-style guest are
|
||||
unverified.** `CPU` and `MEMORY` are read straight from `/proc/cpuinfo` and
|
||||
`/proc/meminfo`, with no cgroup awareness. Inside a box-minted guest (`VIRT`
|
||||
says `lxc`) it is **not currently established** whether those files report the
|
||||
instance's configured limits or the host's totals: neither file is namespaced
|
||||
by the kernel, but `lxcfs` — when the guest has it — overmounts both with
|
||||
limit-aware versions, so the answer depends on the guest's setup rather than
|
||||
on anything rig controls. Until someone confirms it against a real guest,
|
||||
treat those two lines as unreliable on `lxc` machines and check the instance
|
||||
config if the number matters. Everything else (OS, kernel, disk, virt,
|
||||
provenance) is the guest's own either way.
|
||||
|
||||
Deliberately not guessed at: cgroup-aware limit detection would be the fix if
|
||||
the numbers do turn out to be the host's, but writing it against a *reasoned*
|
||||
answer rather than an *observed* one risks correcting a bug that isn't there
|
||||
and papering over one that is.
|
||||
|
||||
Deliberately **not** here: NIC names, MAC addresses, PCI inventory, mount
|
||||
tables, sensors — this is a cheatsheet, not `inxi`, and the bar is "what would
|
||||
I want to know before I SSH in". Nor any health judgement ("disk nearly
|
||||
full"): that needs thresholds this command has no business owning. It is
|
||||
called `platform` and not `status` on purpose — `rig users status` and `rig
|
||||
runner status` cross-check recorded state against live state and print
|
||||
`DRIFT`, and this command records nothing, so it cannot drift and must not
|
||||
borrow a promise it structurally cannot make. That leaves `rig status` free
|
||||
for the machine-wide roll-up it will eventually want to be.
|
||||
|
||||
### `rig runner install --repo <owner/repo>`
|
||||
|
||||
Runner box only, run after `rig bootstrap runner-server` (the same two-step rhythm
|
||||
|
|
|
|||
13
bin/rig
13
bin/rig
|
|
@ -54,6 +54,15 @@ commands:
|
|||
writes a gzipped SQL artifact (--no-owner --no-acl, so it restores
|
||||
onto a different instance); `restore` loads one back, connecting as
|
||||
the container's own superuser, behind a confirm gate. Run as root.
|
||||
platform
|
||||
What is this machine: hostname, OS, kernel, CPU, memory, disk and
|
||||
virtualization, then rig's own provenance (which rig, when, and the
|
||||
role marker bootstrap wrote). Computed at run time from /proc, uname,
|
||||
/etc/os-release, df and systemd-detect-virt and stored NOWHERE — a
|
||||
spec goes stale the moment someone adds RAM, so there is nothing to
|
||||
go stale here. Writes nothing, needs no root, makes no network call,
|
||||
and therefore also runs on a pristine Debian box rig has never
|
||||
bootstrapped, where the provenance block reads 'not bootstrapped'.
|
||||
runner install --repo <owner/repo> [options]
|
||||
GitHub Actions runner as a systemd service under an unprivileged
|
||||
user — outbound-only, no Docker. Prompts for the short-lived
|
||||
|
|
@ -383,6 +392,10 @@ case "$cmd" in
|
|||
;;
|
||||
esac
|
||||
;;
|
||||
platform)
|
||||
shift
|
||||
exec "$ROOT/commands/platform.sh" "$@"
|
||||
;;
|
||||
runner)
|
||||
shift
|
||||
sub="${1:-}"
|
||||
|
|
|
|||
205
commands/platform.sh
Executable file
205
commands/platform.sh
Executable file
|
|
@ -0,0 +1,205 @@
|
|||
#!/usr/bin/env bash
|
||||
# rig platform — what is this machine? Calculated at run time, stored nowhere.
|
||||
#
|
||||
# Read-only in the strongest sense rig has: it reads /proc, uname,
|
||||
# /etc/os-release, df and systemd-detect-virt, and writes NOTHING, ever. That
|
||||
# is the design, not an implementation detail — specs change without rig doing
|
||||
# anything (RAM added, root disk resized, unattended-upgrades patching the
|
||||
# kernel), so a stored spec is stale the moment the machine changes, and
|
||||
# refreshing one on every run would collide with bootstrap's convergence
|
||||
# contract ("safe to re-run; a second run changes nothing").
|
||||
#
|
||||
# The corollary is worth having deliberately: this runs on a machine rig has
|
||||
# never converged, and needs no root. It answers "what should I converge this
|
||||
# into?", not only "what did I converge this into?".
|
||||
set -euo pipefail
|
||||
|
||||
HERE="$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)"
|
||||
# shellcheck source=SCRIPTDIR/lib/users-config.sh
|
||||
. "$HERE/lib/users-config.sh" # read_role_marker — one reader of /etc/rig/role
|
||||
|
||||
die() { printf 'rig-platform: ERROR: %s\n' "$1" >&2; exit "${2:-1}"; }
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
usage: rig platform
|
||||
|
||||
Describes the machine you are on: hostname, OS, kernel, CPU, memory, disk
|
||||
and virtualization, then rig's own provenance (which rig, when, and the role
|
||||
marker bootstrap wrote).
|
||||
|
||||
Computed at run time from /proc, uname, /etc/os-release, df and
|
||||
systemd-detect-virt. Writes nothing, needs no root, makes no network call —
|
||||
so it also works on a pristine Debian box rig has never bootstrapped, where
|
||||
the provenance block reads 'not bootstrapped'.
|
||||
EOF
|
||||
}
|
||||
|
||||
# --- args ------------------------------------------------------------------
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown flag: $1" 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# One aligned column for every line, so the output diffs cleanly across a
|
||||
# fleet and reads as one table rather than a log.
|
||||
field() { printf '%-10s %s\n' "$1" "$2"; }
|
||||
|
||||
# --- hostname ---------------------------------------------------------------
|
||||
# uname -n is the coreutils fallback: `hostname` lives in its own package and a
|
||||
# minimal image may not carry it, and this command's whole point is running
|
||||
# before anything has been installed.
|
||||
HOSTNAME_V="$(hostname 2>/dev/null || uname -n)"
|
||||
|
||||
# --- OS ---------------------------------------------------------------------
|
||||
# THE os-release TRAP: /etc/os-release defines VERSION, NAME and ID, so
|
||||
# sourcing it in the MAIN shell silently clobbers same-named script variables.
|
||||
# Every site in this tree sources it in a SUBSHELL instead (bootstrap.sh:305,
|
||||
# bootstrap-tenant.sh:126, runner-install.sh:88, db.sh:52,
|
||||
# coolify-backup-install.sh:88), and test/cli.sh greps commands/ to keep it
|
||||
# that way. Follow the form verbatim.
|
||||
if [ -r /etc/os-release ]; then
|
||||
OS="$(. /etc/os-release && printf '%s %s' "${NAME:-}" "${VERSION:-${VERSION_ID:-}}")"
|
||||
else
|
||||
OS=""
|
||||
fi
|
||||
|
||||
# --- kernel -----------------------------------------------------------------
|
||||
KERNEL="$(uname -r) ($(uname -m))"
|
||||
|
||||
# --- CPU --------------------------------------------------------------------
|
||||
# 'model name' is x86's spelling; arm64 /proc/cpuinfo has no such field, so an
|
||||
# unnamed CPU still reports its core count rather than nothing at all.
|
||||
CPU_MODEL="$(awk -F': ' '/^model name/ {print $2; exit}' /proc/cpuinfo 2>/dev/null || true)"
|
||||
CORES="$(nproc 2>/dev/null || true)"
|
||||
|
||||
# --- memory -----------------------------------------------------------------
|
||||
# /proc/meminfo is in kB. MemAvailable is the kernel's own estimate of what a
|
||||
# new workload could claim (MemFree undercounts badly, reclaimable cache being
|
||||
# most of a busy box's RAM); it predates every kernel rig targets, but degrade
|
||||
# rather than print a wrong number if it is missing.
|
||||
mem_kb() { awk -v k="$1" '$1 == k":" {print $2; exit}' /proc/meminfo 2>/dev/null || true; }
|
||||
MEM_TOTAL_KB="$(mem_kb MemTotal)"
|
||||
MEM_AVAIL_KB="$(mem_kb MemAvailable)"
|
||||
human_kb() { # kB -> IEC, matching df's units below
|
||||
[ -n "${1:-}" ] || { printf 'unknown'; return 0; }
|
||||
numfmt --to=iec-i "$(( $1 * 1024 ))" 2>/dev/null || printf '%s kB' "$1"
|
||||
}
|
||||
|
||||
# --- disk -------------------------------------------------------------------
|
||||
# -P is the one-line-per-filesystem guarantee (a long device name otherwise
|
||||
# wraps and breaks field positions); -B1 gives bytes, so numfmt renders the
|
||||
# same IEC units as memory above instead of df's own bare 'G'.
|
||||
DISK_TOTAL="" DISK_FREE=""
|
||||
if DF="$(df -PB1 / 2>/dev/null)"; then
|
||||
DISK_TOTAL="$(printf '%s\n' "$DF" | awk 'NR==2 {print $2}')"
|
||||
DISK_FREE="$(printf '%s\n' "$DF" | awk 'NR==2 {print $4}')"
|
||||
fi
|
||||
human_b() { # bytes -> IEC; falls back like human_kb rather than to 'unknown'
|
||||
[ -n "${1:-}" ] || { printf 'unknown'; return 0; }
|
||||
numfmt --to=iec-i "$1" 2>/dev/null || printf '%s B' "$1"
|
||||
}
|
||||
|
||||
# --- virtualization ---------------------------------------------------------
|
||||
# THE set -e TRAP: systemd-detect-virt exits NON-ZERO on bare metal while
|
||||
# printing 'none'. That is a normal, correct answer — without the `|| true` a
|
||||
# bare-metal machine would turn this whole command into a failed run. The
|
||||
# substitution also swallows the binary being absent entirely (a non-systemd
|
||||
# box), which lands as 'unknown'.
|
||||
VIRT="$(systemd-detect-virt 2>/dev/null || true)"
|
||||
|
||||
printf '%s\n' "PLATFORM"
|
||||
field HOSTNAME "$HOSTNAME_V"
|
||||
field OS "${OS:-unknown}"
|
||||
field KERNEL "$KERNEL"
|
||||
field CPU "${CPU_MODEL:-unknown}${CORES:+ ($CORES cores)}"
|
||||
field MEMORY "$(human_kb "$MEM_TOTAL_KB") total, $(human_kb "$MEM_AVAIL_KB") available"
|
||||
field DISK "$(human_b "$DISK_TOTAL") total, $(human_b "$DISK_FREE") free on /"
|
||||
field VIRT "${VIRT:-unknown}"
|
||||
echo
|
||||
|
||||
# --- provenance: READ, never written ----------------------------------------
|
||||
# The complementary half of the answer — which rig, and when — is decided
|
||||
# rather than observed, so unlike everything above it IS stored. rig writes it
|
||||
# during bootstrap; this command only ever reads it.
|
||||
#
|
||||
# /etc/rig/manifest is #61 and is NOT implemented yet, so on every machine in
|
||||
# existence today this block reads 'not bootstrapped'. That degradation is the
|
||||
# point: the two features are independent and neither blocks the other. The
|
||||
# parse is the flat key=value shape the manifest is specified to use — the
|
||||
# same jq-free shape /etc/rig/users and /etc/rig/role already use, parseable
|
||||
# with `read` on a box that has no YAML parser.
|
||||
#
|
||||
# RIG_MANIFEST / RIG_ROLE_MARKER override the paths so the harness can drive
|
||||
# both the present and the absent case against fixtures, non-root, without a
|
||||
# real marker on the machine running the tests (repo precedent: the
|
||||
# RIG_ROLE_MARKER gate in bin/rig, install.sh and users-close-root.sh).
|
||||
MANIFEST="${RIG_MANIFEST:-/etc/rig/manifest}"
|
||||
MARKER="${RIG_ROLE_MARKER:-/etc/rig/role}"
|
||||
|
||||
manifest_field() { # $1 = key — empty when absent, unreadable or unset
|
||||
local k v
|
||||
[ -r "$MANIFEST" ] || return 0
|
||||
# `|| [ -n "$k" ]` so a manifest whose last line lacks a trailing newline
|
||||
# still yields that line: read returns 1 at EOF even having filled k/v.
|
||||
# Same guard parse_users_file uses (lib/users-config.sh:47) — #61's writer
|
||||
# should not have to know whether this reader tolerates a missing \n.
|
||||
while IFS='=' read -r k v || [ -n "$k" ]; do
|
||||
[ "$k" = "$1" ] || continue
|
||||
printf '%s\n' "$v"
|
||||
return 0
|
||||
done < "$MANIFEST"
|
||||
return 0
|
||||
}
|
||||
|
||||
printf '%s\n' "PROVENANCE"
|
||||
# Keys are #61's documented schema verbatim — schema/bootstrapped_by/
|
||||
# bootstrapped_at/converged_by/converged_at — NOT invented ones. #61 keeps
|
||||
# birth and latest deliberately separate ("is this machine converged by a rig
|
||||
# that predates the fix?"), so both are reported and neither is inferred from
|
||||
# the other: CONVERGED answers currency, BOOTSTRAPPED answers provenance.
|
||||
# Every field degrades independently, so a partial manifest from a future
|
||||
# schema still renders what it does carry.
|
||||
if [ -r "$MANIFEST" ]; then
|
||||
M_SCHEMA="$(manifest_field schema)"
|
||||
B_BY="$(manifest_field bootstrapped_by)"; B_AT="$(manifest_field bootstrapped_at)"
|
||||
C_BY="$(manifest_field converged_by)"; C_AT="$(manifest_field converged_at)"
|
||||
# A manifest with no schema= line is pre-#61; say so rather than render blanks.
|
||||
if [ -z "$M_SCHEMA$B_BY$B_AT$C_BY$C_AT" ]; then
|
||||
field RIG "manifest present but carries no recognised fields ($MANIFEST)"
|
||||
else
|
||||
# 'not recorded' rather than 'unknown', and it does NOT describe a fresh
|
||||
# box: #61's writer records both pairs equally at bootstrap, so no writer
|
||||
# produces a manifest missing converged_* — its absence means the file is
|
||||
# partial or hand-edited. Deliberately not backfilled from bootstrapped_*,
|
||||
# because inferring a convergence that never happened is worse than saying
|
||||
# the record is not there. README and the fixtures pin exactly this.
|
||||
field CONVERGED "${C_BY:-not recorded}${C_AT:+, $C_AT}"
|
||||
field BOOTSTRAP "${B_BY:-not recorded}${B_AT:+, $B_AT}"
|
||||
[ -n "$M_SCHEMA" ] && [ "$M_SCHEMA" != "1" ] && \
|
||||
field NOTE "manifest schema=$M_SCHEMA is newer than this rig reads (expects 1)"
|
||||
fi
|
||||
else
|
||||
field RIG "not bootstrapped (no $MANIFEST)"
|
||||
fi
|
||||
|
||||
# The role marker is bootstrap's own line — 'role=dev-server
|
||||
# root-door=closed host=yes join=authkey' — printed as the role plus its
|
||||
# traits. The example tracks the CURRENT vocabulary (#76's -server/-box role
|
||||
# suffixes, #77's root-door trait); this command renders whatever fields the
|
||||
# marker carries, so a pre-rename box still prints its own class= line as-is.
|
||||
MARKER_LINE="$(read_role_marker "$MARKER")"
|
||||
if [ -n "$MARKER_LINE" ]; then
|
||||
ROLE_NAME="" ROLE_TRAITS=""
|
||||
for kv in $MARKER_LINE; do
|
||||
case "$kv" in
|
||||
role=*) ROLE_NAME="${kv#role=}" ;;
|
||||
*) ROLE_TRAITS="${ROLE_TRAITS:+$ROLE_TRAITS }$kv" ;;
|
||||
esac
|
||||
done
|
||||
field ROLE "${ROLE_NAME:-unknown}${ROLE_TRAITS:+ ($ROLE_TRAITS)}"
|
||||
else
|
||||
field ROLE "not bootstrapped (no $MARKER)"
|
||||
fi
|
||||
87
test/cli.sh
87
test/cli.sh
|
|
@ -977,6 +977,93 @@ else
|
|||
echo "skip: runner status/remove/repoint non-root refusals (running as root)"
|
||||
fi
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# rig platform (#64). Unusually testable for this repo: it needs no root, no
|
||||
# network and no fixtures, and it WRITES NOTHING — so unlike every other
|
||||
# command here the harness can RUN it for real on the machine running the
|
||||
# tests and assert on the actual answer, instead of proving arg-parse
|
||||
# refusals and grepping the rest.
|
||||
# ---------------------------------------------------------------------------
|
||||
check "platform: --help exits 0" 0 "usage:" "$ROOT/commands/platform.sh" --help
|
||||
check "platform: unknown flag exits 2" 2 "unknown flag" "$ROOT/commands/platform.sh" --nope
|
||||
check "platform: dispatches through bin/rig" 0 "PLATFORM" "$ROOT/bin/rig" platform
|
||||
|
||||
# The real run: exit 0 and every field present, as the running user.
|
||||
check "platform: runs as this user, exit 0" 0 "PLATFORM" "$ROOT/bin/rig" platform
|
||||
for f in HOSTNAME OS KERNEL CPU MEMORY DISK VIRT; do
|
||||
check "platform: reports $f" 0 "$f" "$ROOT/bin/rig" platform
|
||||
done
|
||||
# Not just the labels — the VALUES have to describe THIS machine. uname -r and
|
||||
# the hostname are the two the harness can independently compute and compare,
|
||||
# which is what separates "it printed a table" from "it read the machine".
|
||||
check "platform: KERNEL is this kernel" 0 "$(uname -r)" "$ROOT/bin/rig" platform
|
||||
check "platform: HOSTNAME is this host" 0 "$(uname -n)" "$ROOT/bin/rig" platform
|
||||
# MemAvailable/df rendered, not left as the 'unknown' fallback: a numfmt or
|
||||
# /proc parse that silently broke would still print the labels above.
|
||||
check "platform: MEMORY carries real numbers" 0 "total," "$ROOT/bin/rig" platform
|
||||
|
||||
# Provenance degrades on a machine rig never converged — #61's manifest does
|
||||
# not exist yet, so 'not bootstrapped' is the state of the world today and the
|
||||
# command must ship complete without it. Both paths driven against fixtures.
|
||||
PLATWORK="$(mktemp -d)"
|
||||
# THE INTEGRATION CONTRACT (#61, found in #74 review): these fixtures carry
|
||||
# #61's documented schema VERBATIM — schema/bootstrapped_by/bootstrapped_at/
|
||||
# converged_by/converged_at. An earlier draft of this reader invented `version`
|
||||
# and `bootstrapped`, which no writer would ever have produced: the command
|
||||
# would have rendered 'unknown' forever the day #61 landed, and nothing here
|
||||
# would have said so. Keep these keys in step with #61; that is the point.
|
||||
printf 'schema=1\nbootstrapped_by=0.4.0\nbootstrapped_at=2026-07-19T14:24:51Z\nconverged_by=0.6.0\nconverged_at=2026-08-02T09:11:03Z\n' > "$PLATWORK/manifest"
|
||||
check "platform: no manifest reads 'not bootstrapped'" 0 "RIG not bootstrapped" \
|
||||
env RIG_MANIFEST="$PLATWORK/absent" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
check "platform: no role marker reads 'not bootstrapped'" 0 "ROLE not bootstrapped" \
|
||||
env RIG_MANIFEST="$PLATWORK/absent" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
# A manifest that DOES exist is read, never written — the forward-compatible
|
||||
# half, so #61 landing needs no change here.
|
||||
check "platform: reads #61's converged_by/at" 0 "CONVERGED 0.6.0, 2026-08-02T09:11:03Z" \
|
||||
env RIG_MANIFEST="$PLATWORK/manifest" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
check "platform: reads #61's bootstrapped_by/at" 0 "BOOTSTRAP 0.4.0, 2026-07-19T14:24:51Z" \
|
||||
env RIG_MANIFEST="$PLATWORK/manifest" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
# A FRESH bootstrap carries both pairs with EQUAL values — #61 is explicit
|
||||
# ("On a fresh machine both pairs are written with equal values"); rule 2 only
|
||||
# suppresses converged_* churn on a later same-version re-run. So equal dates
|
||||
# are the never-re-converged case and must render as themselves, not be
|
||||
# special-cased into looking unset.
|
||||
printf 'schema=1\nbootstrapped_by=0.4.0\nbootstrapped_at=2026-07-19T14:24:51Z\nconverged_by=0.4.0\nconverged_at=2026-07-19T14:24:51Z\n' > "$PLATWORK/manifest-fresh"
|
||||
check "platform: a fresh bootstrap shows both pairs equal (#61)" 0 "CONVERGED 0.4.0, 2026-07-19T14:24:51Z" \
|
||||
env RIG_MANIFEST="$PLATWORK/manifest-fresh" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
# A manifest missing converged_* is therefore NOT a fresh box — no writer
|
||||
# produces that — so it is partial or hand-edited. Degrade loudly rather than
|
||||
# backfilling from birth, which would invent a convergence that never happened.
|
||||
printf 'schema=1\nbootstrapped_by=0.4.0\nbootstrapped_at=2026-07-19T14:24:51Z\n' > "$PLATWORK/manifest-partial"
|
||||
check "platform: a partial manifest says so, never infers from birth" 0 "CONVERGED not recorded" \
|
||||
env RIG_MANIFEST="$PLATWORK/manifest-partial" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
# A newer schema renders what it recognises and says the rest is unreadable,
|
||||
# rather than pretending a partial read is the whole truth.
|
||||
printf 'schema=2\nbootstrapped_by=9.9.9\nbootstrapped_at=2027-01-01T00:00:00Z\n' > "$PLATWORK/manifest-v2"
|
||||
check "platform: a newer schema is named, not silently half-read" 0 "schema=2 is newer" \
|
||||
env RIG_MANIFEST="$PLATWORK/manifest-v2" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
# A manifest carrying none of #61's keys is reported as such — the pre-#61 or
|
||||
# corrupt case, distinct from both 'absent' and 'read fine'.
|
||||
printf 'somethingelse=1\n' > "$PLATWORK/manifest-alien"
|
||||
check "platform: an unrecognised manifest is not read as empty" 0 "no recognised fields" \
|
||||
env RIG_MANIFEST="$PLATWORK/manifest-alien" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
# ...including one whose last line has no trailing newline: `read` returns 1 at
|
||||
# EOF even having filled the variables, so an unguarded loop drops that line
|
||||
# silently — the timestamp would vanish while the version still rendered. #61's
|
||||
# writer must not have to know this reader's tolerances (found in #74 review).
|
||||
printf 'schema=1\nbootstrapped_by=0.4.0\nbootstrapped_at=2026-07-19T14:24:51Z' > "$PLATWORK/manifest-nonl"
|
||||
check "platform: reads a manifest with no trailing newline" 0 "BOOTSTRAP 0.4.0, 2026-07-19T14:24:51Z" \
|
||||
env RIG_MANIFEST="$PLATWORK/manifest-nonl" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform
|
||||
printf 'role=dev class=human host=yes join=authkey\n' > "$PLATWORK/role"
|
||||
check "platform: renders the role marker's traits" 0 "dev (class=human host=yes join=authkey)" \
|
||||
env RIG_MANIFEST="$PLATWORK/absent" RIG_ROLE_MARKER="$PLATWORK/role" "$ROOT/bin/rig" platform
|
||||
# The defining property: it writes NOTHING. Not the manifest it just reported
|
||||
# missing, not the marker, not anything else in the fixture directory — the
|
||||
# whole design rests on this, so assert it rather than trust it.
|
||||
env RIG_MANIFEST="$PLATWORK/absent" RIG_ROLE_MARKER="$PLATWORK/absent" "$ROOT/bin/rig" platform >/dev/null 2>&1
|
||||
check "platform: writes nothing (no manifest created)" 1 "" test -e "$PLATWORK/absent"
|
||||
rm -rf "$PLATWORK"
|
||||
|
||||
check "bare users shows usage, exit 2" 2 "usage:" "$ROOT/bin/rig" users
|
||||
check "users: bad subcommand exits 2" 2 "usage:" "$ROOT/bin/rig" users frobnicate
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue