Merge branch 'rig-work'

This commit is contained in:
2026-09-16 14:21:29 -03:00
9 changed files with 79 additions and 650 deletions

View File

@@ -5,7 +5,6 @@
# bash file, and that file holds the variants: # bash file, and that file holds the variants:
# #
# make cluster up -> ctrl/cluster.sh up # make cluster up -> ctrl/cluster.sh up
# make newbox destroy -> ctrl/newbox.sh destroy
# #
# Config layers, weakest first: ctrl/versions.env (pinned toolchain) < # Config layers, weakest first: ctrl/versions.env (pinned toolchain) <
# ctrl/env.d/<profile>.env (cluster shape) < ctrl/.env (local, gitignored) < # ctrl/env.d/<profile>.env (cluster shape) < ctrl/.env (local, gitignored) <
@@ -52,7 +51,7 @@ $(eval $(ARGS):;@:)
endif endif
.PHONY: help setup check selftest mem deps deps-image pins cluster registry addons ports \ .PHONY: help setup check selftest mem deps deps-image pins cluster registry addons ports \
newbox dockerhost docs tilt \ docs tilt \
kind-up kind-down kind-reset tilt-up tilt-down kind-up kind-down kind-reset tilt-up tilt-down
help: ## list targets help: ## list targets
@@ -60,7 +59,7 @@ help: ## list targets
# ── setup ────────────────────────────────────────────────────────────────── # ── setup ──────────────────────────────────────────────────────────────────
setup: ## prepare this machine [core] [--share-docker] [--cluster] setup: ## prepare this machine [core] [--cluster]
bash ctrl/setup.sh $(ARGS) bash ctrl/setup.sh $(ARGS)
check: ## is this machine ready? reports, never fixes check: ## is this machine ready? reports, never fixes
@@ -100,14 +99,6 @@ addons: ## profile addons [install|list] (default
ports: ## this environment's port block [show|persist] ports: ## this environment's port block [show|persist]
bash ctrl/ports.sh $(or $(ARGS),show) bash ctrl/ports.sh $(or $(ARGS),show)
# ── host ───────────────────────────────────────────────────────────────────
newbox: ## throwaway environment [create|status|shell|destroy]
bash ctrl/newbox.sh $(or $(ARGS),status)
dockerhost: ## share Docker between distros [status|share|unshare]
$(if $(filter share unshare,$(ARGS)),sudo ,)bash ctrl/dockerhost.sh $(or $(ARGS),status)
# ── docs + dev loop ──────────────────────────────────────────────────────── # ── docs + dev loop ────────────────────────────────────────────────────────
docs: ## documentation [serve|graphs] (default serve) docs: ## documentation [serve|graphs] (default serve)

View File

@@ -1,272 +0,0 @@
#!/usr/bin/env bash
# Share ONE Docker daemon across WSL distros, instead of running one per distro.
#
# Why this exists
# ---------------
# WSL2 distros share a kernel and a network stack. Two dockerd instances then
# contend over docker0 and iptables, which can disturb the daemon you actually
# depend on. Docker Desktop avoids this by running a single daemon in a
# dedicated distro and sharing its socket — this is the same idea, without
# Docker Desktop.
#
# So a throwaway rig box does NOT install Docker. It borrows the daemon from
# whichever distro is the designated host. That also makes the test more honest:
# rig never installs Docker anyway — Docker is its documented prerequisite.
#
# How
# ---
# /mnt/wsl is a tmpfs with `shared` mount propagation, visible to every distro
# in the WSL VM. The owning distro exposes its socket there; guests point
# DOCKER_HOST at it. Two ways, with different costs:
#
# share bind-mount the existing socket onto the shared tmpfs.
# Instant, and dockerd is NEVER restarted. Lasts until the
# next WSL shutdown.
# share --persist additionally install a systemd drop-in so dockerd listens
# there itself. Survives restarts, but requires one Docker
# restart now — which stops every container that has no
# restart policy, since live-restore is off by default.
#
# The bind mount is the default precisely because the persistent version's cost
# is paid on a machine that is already working.
#
# Reversibility is the whole design
# ---------------------------------
# `unshare` removes the bind mount (no restart) and, if present, the drop-in.
# The original systemd unit is never edited — only an additive drop-in file is
# ever created — so undoing is deletion, not repair. `status` always states
# which of the three roles a distro is in, in those words.
#
# Nothing here runs automatically. It does nothing until invoked.
#
# Usage:
# dockerhost.sh status # which distro owns Docker; what this one uses
# dockerhost.sh share # share it (bind mount, no daemon restart)
# dockerhost.sh share --persist # ...and survive WSL restarts (restarts Docker)
# dockerhost.sh unshare # undo it; this distro owns its Docker again
# dockerhost.sh use [--persist] # point THIS distro at the shared socket
set -euo pipefail
SHARED_DIR=/mnt/wsl/shared-docker
SHARED_SOCK="$SHARED_DIR/docker.sock"
OWNER_FILE="$SHARED_DIR/OWNER"
DROPIN=/etc/systemd/system/docker.service.d/10-rig-shared-socket.conf
PROFILE_D=/etc/profile.d/rig-docker-host.sh
distro_name() { echo "${WSL_DISTRO_NAME:-$(hostname)}"; }
require_wsl() {
grep -qi microsoft /proc/version 2>/dev/null && return 0
echo "dockerhost is WSL-only: it relies on /mnt/wsl being shared between distros." >&2
exit 1
}
# ── status ─────────────────────────────────────────────────────────────────
status() {
require_wsl
echo "distro $(distro_name)"
if [ -f "$DROPIN" ] || mountpoint -q "$SHARED_SOCK" 2>/dev/null; then
echo "role SHARING — this distro's Docker is offered to other distros"
elif [ -n "${DOCKER_HOST:-}" ] && [ "${DOCKER_HOST}" = "unix://$SHARED_SOCK" ]; then
echo "role BORROWING — using another distro's Docker"
else
echo "role standalone — this WSL installation has the main host Docker"
fi
echo
if [ -S "$SHARED_SOCK" ]; then
echo "shared sock $SHARED_SOCK (present)"
[ -f "$OWNER_FILE" ] && sed 's/^/ /' "$OWNER_FILE"
else
echo "shared sock none — no distro is sharing right now"
fi
echo
echo "DOCKER_HOST ${DOCKER_HOST:-(unset — using /var/run/docker.sock)}"
if command -v docker >/dev/null 2>&1; then
echo "docker $(docker version --format '{{.Server.Version}}' 2>/dev/null || echo unreachable)"
else
echo "docker cli not installed"
fi
}
# ── share / unshare (run on the host distro) ───────────────────────────────
# Default: expose the EXISTING socket by bind-mounting it onto the shared tmpfs.
# /mnt/wsl has `shared` propagation, so the mount is visible in other distros.
#
# The point of doing it this way is that dockerd is never restarted. Restarting
# it stops every container that has no restart policy (live-restore is off by
# default), which on a working machine means quietly killing whatever you had
# running. Not a trade worth making just to expose a socket.
#
# Cost: a bind mount does not survive a WSL VM shutdown. `--persist` adds the
# systemd drop-in as well, which does survive but needs that one restart.
share_bind() {
mkdir -p "$SHARED_DIR"
chmod 0755 "$SHARED_DIR"
if mountpoint -q "$SHARED_SOCK" 2>/dev/null; then
echo "already bind-mounted at $SHARED_SOCK"
else
[ -S /var/run/docker.sock ] || { echo "no /var/run/docker.sock here" >&2; exit 1; }
# The target must exist as a file for a bind mount onto it.
[ -e "$SHARED_SOCK" ] || : > "$SHARED_SOCK"
mount --bind /var/run/docker.sock "$SHARED_SOCK"
echo "bind-mounted /var/run/docker.sock -> $SHARED_SOCK (no daemon restart)"
fi
cat > "$OWNER_FILE" <<EOF
owner distro: $(distro_name)
docker gid: $(getent group docker | cut -d: -f3)
socket: $SHARED_SOCK
method: bind-mount (until the next WSL shutdown)
EOF
}
share() {
require_wsl
[ "$(id -u)" -eq 0 ] || { echo "run with sudo: sudo bash ctrl/dockerhost.sh share" >&2; exit 1; }
share_bind
if [ "${1:-}" != "--persist" ]; then
echo
echo "This lasts until the next WSL shutdown. To make it survive, re-run with"
echo "--persist — but note that adds a systemd drop-in and RESTARTS Docker,"
echo "which stops any container that has no restart policy."
return 0
fi
if [ -f "$DROPIN" ]; then
echo "drop-in already present — sharing persists across restarts."
return 0
fi
echo
echo "--persist: installing a systemd drop-in and restarting Docker."
echo "Containers without a restart policy will stop and will NOT come back."
docker ps --format ' {{.Names}} restart={{.HostConfig.RestartPolicy.Name}}' 2>/dev/null \
|| docker ps --format ' {{.Names}}' 2>/dev/null || true
echo
local exec_line
exec_line=$(systemctl cat docker.service | grep -m1 '^ExecStart=')
if [ -z "$exec_line" ]; then
echo "could not read docker.service ExecStart — refusing to guess" >&2
exit 1
fi
mkdir -p "$(dirname "$DROPIN")" "$SHARED_DIR"
# Additive only: blank the inherited ExecStart, then restate it verbatim
# with one extra -H. Nothing about the original unit is edited.
cat > "$DROPIN" <<EOF
# Added by rig (ctrl/dockerhost.sh share).
#
# Adds a SECOND listening socket on the WSL-shared tmpfs so other distros can
# use this daemon instead of running their own. The original socket is
# untouched, so this distro behaves exactly as before.
#
# To undo: sudo bash ctrl/dockerhost.sh unshare
[Service]
ExecStartPre=-/bin/mkdir -p $SHARED_DIR
ExecStartPre=-/bin/chmod 0755 $SHARED_DIR
ExecStart=
${exec_line} -H unix://$SHARED_SOCK
EOF
systemctl daemon-reload
systemctl restart docker
# Guests need a group with a MATCHING GID to use the socket; GIDs are not
# consistent across distros, so record ours rather than assume.
cat > "$OWNER_FILE" <<EOF
owner distro: $(distro_name)
docker gid: $(getent group docker | cut -d: -f3)
socket: $SHARED_SOCK
EOF
echo "sharing from '$(distro_name)'"
echo " guests: export DOCKER_HOST=unix://$SHARED_SOCK"
echo " undo: sudo bash ctrl/dockerhost.sh unshare"
echo
echo "NOTE: /mnt/wsl is tmpfs and is cleared when the WSL VM shuts down."
echo " The drop-in recreates the directory on the next Docker start."
}
unshare_() {
require_wsl
[ "$(id -u)" -eq 0 ] || { echo "run with sudo: sudo bash ctrl/dockerhost.sh unshare" >&2; exit 1; }
local did=0
# The bind mount first: undoing it needs no restart, so a plain `share`
# is fully reversible without disturbing anything.
if mountpoint -q "$SHARED_SOCK" 2>/dev/null; then
umount "$SHARED_SOCK"
rm -f "$SHARED_SOCK"
echo " removed the bind mount (no restart needed)"
did=1
fi
rm -f "$OWNER_FILE"
rmdir "$SHARED_DIR" 2>/dev/null || true
if [ -f "$DROPIN" ]; then
rm -f "$DROPIN"
rmdir "$(dirname "$DROPIN")" 2>/dev/null || true
systemctl daemon-reload
systemctl restart docker
echo " removed the systemd drop-in and restarted Docker"
did=1
fi
if [ "$did" -eq 0 ]; then
echo "not sharing — this WSL installation already has the main host Docker."
return 0
fi
echo "restored: this WSL installation has the main host Docker again."
echo " (nothing else was changed; the original unit was never edited)"
}
# ── use (run on a guest distro) ────────────────────────────────────────────
use() {
require_wsl
if [ ! -S "$SHARED_SOCK" ]; then
echo "no shared socket at $SHARED_SOCK" >&2
echo "Run 'sudo bash ctrl/dockerhost.sh share' in the distro that owns Docker." >&2
exit 1
fi
# Align the local docker group GID with the owner's, or the socket is
# unreadable here even though it is visible.
if [ -f "$OWNER_FILE" ] && [ "$(id -u)" -eq 0 ]; then
local gid; gid=$(awk '/docker gid:/ {print $3}' "$OWNER_FILE")
if [ -n "$gid" ]; then
if getent group docker >/dev/null; then
[ "$(getent group docker | cut -d: -f3)" = "$gid" ] || groupmod -g "$gid" docker
else
groupadd -g "$gid" docker
fi
fi
fi
if [ "${1:-}" = "--persist" ]; then
[ "$(id -u)" -eq 0 ] || { echo "--persist needs root" >&2; exit 1; }
echo "export DOCKER_HOST=unix://$SHARED_SOCK" > "$PROFILE_D"
echo "persisted in $PROFILE_D"
fi
echo "export DOCKER_HOST=unix://$SHARED_SOCK"
}
case "${1:-status}" in
status) status ;;
share) shift; share "${1:-}" ;;
unshare) unshare_ ;;
use) shift; use "${1:-}" ;;
*) echo "usage: $0 [status|share|unshare|use [--persist]]" >&2; exit 1 ;;
esac

View File

@@ -1,15 +0,0 @@
# /etc/hosts block for this environment. Rendered by newbox.sh; ${CLUSTER} and
# ${HTTP_PORT} are substituted.
#
# Hostnames are a convenience, not a requirement — every service is reachable at
# localhost:<port> without any of this, which is why DNS is not touched by
# default. Add entries here as the model grows.
#
# On Windows the same block has to go in
# C:\Windows\System32\drivers\etc\hosts for a browser to resolve these. That
# file does NOT support wildcards, so every name must be listed explicitly.
# newbox.sh prints the block for you to paste rather than editing it.
127.0.0.1 ${CLUSTER}.local
127.0.0.1 api.${CLUSTER}.local
127.0.0.1 docs.${CLUSTER}.local

View File

@@ -1,316 +0,0 @@
#!/usr/bin/env bash
# Create a disposable Linux environment to validate the installer from a
# genuinely clean slate — one that can be thrown away without touching the
# environment you actually work in.
#
# This is the ONLY host-aware file in the tree. Everything else needs just a
# Linux with Docker, which is what keeps other host types a later addition
# rather than a rewrite.
#
# On WSL it creates a second distro. There is no .bat and no PowerShell script:
# wsl.exe is callable from inside WSL, and wslpath converts the paths it wants.
# A machine with no WSL at all needs `wsl --install` run once by hand first —
# scripting a reboot-requiring Windows feature install is not worth it.
#
# Docker: borrowed by default, never installed twice
# --------------------------------------------------
# WSL2 distros share one kernel and one network stack, so two dockerd instances
# contend over docker0 and iptables and can disturb the daemon you depend on.
# (That is why Docker Desktop runs one daemon in a dedicated distro and shares
# its socket rather than installing one per distro.)
#
# REUSE_DOCKER=1 (default) borrow the host distro's daemon over /mnt/wsl.
# Nothing is installed; nothing can conflict.
# Requires `ctrl/dockerhost.sh share` once on the
# distro that owns Docker.
# REUSE_DOCKER=0 install a second daemon in the new distro. Only
# if you specifically want to test a from-scratch
# Docker install, and not on a machine you need.
#
# Borrowing is also the more honest test: rig never installs Docker anyway — it
# is the documented prerequisite — so a clean box does not need its own to
# exercise everything rig actually does.
#
# Usage: newbox.sh create | destroy [--purge] | status | shell
set -euo pipefail
cd "$(dirname "$0")"
source ./lib/config.sh
load_config
REPO="$(cd .. && pwd)"
# The distro is named after this environment, and that derived name is the ONLY
# thing this script will ever destroy. See guard_name().
BOX="${BOX:-${CLUSTER}box}"
BOX_USER="${BOX_USER:-dev}"
# Borrow the host distro's Docker rather than installing a second daemon.
REUSE_DOCKER="${REUSE_DOCKER:-1}"
SHARED_SOCK=/mnt/wsl/shared-docker/docker.sock
WSL_EXE=/mnt/c/Windows/System32/wsl.exe
# ── host detection ─────────────────────────────────────────────────────────
require_wsl() {
if ! grep -qi microsoft /proc/version 2>/dev/null; then
cat >&2 <<'EOF'
newbox is WSL-only for now.
If WSL is not installed, run `wsl --install` from an elevated Windows prompt
first — see "Starting from plain Windows" in README.md.
On native Linux you do not need it: rig already isolates environments by
directory (own cluster, context, images and port block), so a second copy in a
second directory is the clean slate. To validate the installer itself against a
bare system, run ctrl/deps.sh against a stock Debian container instead.
EOF
exit 1
fi
if [ ! -x "$WSL_EXE" ]; then
echo "wsl.exe not found at $WSL_EXE" >&2
exit 1
fi
}
wsl_list() { "$WSL_EXE" -l -q 2>/dev/null | tr -d '\0\r'; }
box_exists() { wsl_list | grep -qx "$BOX"; }
# `wsl --unregister` permanently deletes a distro's filesystem. The whole safety
# story is this function: only the name derived from this directory can ever be
# a target, so a typo or a stray argument cannot destroy the distro you work in.
guard_name() {
local derived="${CLUSTER}box"
if [ "$BOX" != "$derived" ]; then
echo "refusing: BOX='$BOX' is not the name derived from this directory ('$derived')." >&2
echo "That guard exists because --unregister is irreversible." >&2
exit 1
fi
if [ -z "$CLUSTER" ] || [ "$BOX" = "box" ]; then
echo "refusing: empty environment name" >&2
exit 1
fi
}
# ── create ─────────────────────────────────────────────────────────────────
rootfs_path() {
local win_home; win_home=$(wslpath "$("$WSL_EXE" -d "$(wsl_list | head -1)" -e printf '%s' "$USERPROFILE" 2>/dev/null || true)" 2>/dev/null || true)
# Simpler and reliable: use the current user's Windows home via /mnt/c.
ls -d /mnt/c/Users/*/ 2>/dev/null | grep -viE '/(All Users|Default|Default User|Public)/$' | head -1
}
build_rootfs() {
local tar="$1"
if [ -f "$tar" ]; then
echo " rootfs cached: $(basename "$tar")"
return
fi
echo " exporting a stock Debian rootfs (cached for next time)"
local cid; cid=$(docker create debian:trixie-slim)
docker export "$cid" > "$tar"
docker rm -f "$cid" >/dev/null
}
provision() {
echo " provisioning (root)"
local hosts_block
hosts_block=$(CLUSTER="$CLUSTER" HTTP_PORT="$HTTP_PORT" \
envsubst < ./hosts.tmpl 2>/dev/null || sed "s/\${CLUSTER}/$CLUSTER/g" ./hosts.tmpl)
# Piped as stdin rather than a second script file, the same shape as any
# remote provisioning heredoc. Everything here is idempotent so a failed run
# can simply be repeated.
"$WSL_EXE" -d "$BOX" -u root -- bash -s <<PROVISION
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl gnupg sudo >/dev/null
if [ "$REUSE_DOCKER" = "1" ]; then
# Borrow the host distro's daemon: CLI only, no dockerd, nothing to
# conflict with. The GID must match the owner's or the shared socket is
# unreadable here even though it is visible.
install -m 0755 -d /etc/apt/keyrings
if [ ! -f /etc/apt/keyrings/docker.asc ]; then
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
fi
echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \$(. /etc/os-release && echo \$VERSION_CODENAME) stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq docker-ce-cli >/dev/null
echo "export DOCKER_HOST=unix://$SHARED_SOCK" > /etc/profile.d/rig-docker-host.sh
if [ -f /mnt/wsl/shared-docker/OWNER ]; then
gid=\$(awk '/docker gid:/ {print \$3}' /mnt/wsl/shared-docker/OWNER)
if [ -n "\$gid" ]; then
getent group docker >/dev/null && groupmod -g "\$gid" docker || groupadd -g "\$gid" docker
fi
fi
else
# A second daemon. Only when deliberately testing a from-scratch install.
install -m 0755 -d /etc/apt/keyrings
if [ ! -f /etc/apt/keyrings/docker.asc ]; then
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
fi
echo "deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \$(. /etc/os-release && echo \$VERSION_CODENAME) stable" \
> /etc/apt/sources.list.d/docker.list
apt-get update -qq
apt-get install -y -qq docker-ce docker-ce-cli containerd.io >/dev/null
fi
id -u "$BOX_USER" >/dev/null 2>&1 || useradd -m -s /bin/bash "$BOX_USER"
usermod -aG sudo,docker "$BOX_USER"
echo "$BOX_USER ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-$BOX_USER
chmod 0440 /etc/sudoers.d/90-$BOX_USER
# systemd is off by default in WSL, and Docker needs it. Takes effect on the
# next start of this distro, which is why create() terminates it below.
cat > /etc/wsl.conf <<WSLCONF
[boot]
systemd=true
[user]
default=$BOX_USER
WSLCONF
# The default inotify limits are low enough that file watching silently stops
# working — no error, changes just stop being noticed. Fix it before it bites.
cat > /etc/sysctl.d/99-rig.conf <<SYSCTL
fs.inotify.max_user_watches=524288
fs.inotify.max_user_instances=512
SYSCTL
if ! grep -q 'rig environment' /etc/hosts 2>/dev/null; then
{ echo ""; echo "# rig environment"; cat <<'HOSTS'
$hosts_block
HOSTS
} >> /etc/hosts
fi
touch /etc/rig-provisioned
PROVISION
}
create() {
require_wsl
guard_name
local winhome; winhome=$(rootfs_path)
[ -n "$winhome" ] || { echo "could not locate the Windows user directory" >&2; exit 1; }
local tar="${winhome}rig-rootfs.tar"
local installdir="${winhome}WSL/${BOX}"
echo "creating '$BOX'"
if [ "$REUSE_DOCKER" = "1" ]; then
echo " docker: borrowing the host distro's daemon (nothing installed)"
if [ ! -S "$SHARED_SOCK" ]; then
echo
echo " No shared socket yet. In the distro that owns Docker, run once:"
echo " sudo bash ctrl/dockerhost.sh share"
echo " That adds one systemd drop-in and nothing else; undo with 'unshare'."
echo " Continuing — the box will be created, but Docker won't work in it"
echo " until you do that."
fi
else
echo
echo " REUSE_DOCKER=0: installing a SECOND Docker daemon."
echo " WSL distros share a network stack, so this can disturb Docker in"
echo " the distro you work in. Ctrl-C now if that is a bad trade today."
echo
sleep 4
fi
echo
if box_exists; then
echo " distro already registered"
else
build_rootfs "$tar"
mkdir -p "$installdir"
"$WSL_EXE" --import "$BOX" "$(wslpath -w "$installdir")" "$(wslpath -w "$tar")" --version 2
fi
# Resumable: a partially-created box is finished rather than restarted.
if "$WSL_EXE" -d "$BOX" -u root -- test -f /etc/rig-provisioned 2>/dev/null; then
echo " already provisioned"
else
provision
echo " restarting the distro so systemd and group membership apply"
"$WSL_EXE" --terminate "$BOX" # ONLY this distro; never --shutdown
fi
echo " copying rig in"
tar c -C "$REPO" --exclude=def --exclude=.git --exclude=ctrl/.env . \
| "$WSL_EXE" -d "$BOX" -u "$BOX_USER" -- bash -lc "mkdir -p ~/rig && tar x -C ~/rig"
echo
echo " docker: $("$WSL_EXE" -d "$BOX" -u "$BOX_USER" -- bash -lc 'systemctl is-active docker 2>/dev/null || echo inactive')"
echo
echo "next:"
echo " make newbox shell # a shell inside it"
echo " then: cd ~/rig && make check && make deps && make cluster up"
echo
echo "For a browser on Windows to resolve the hostnames, paste this into"
echo "C:\\Windows\\System32\\drivers\\etc\\hosts (it has no wildcard support):"
CLUSTER="$CLUSTER" envsubst < ./hosts.tmpl 2>/dev/null | grep -v '^#' | grep -v '^$' | sed 's/^/ /'
}
# ── the rest ───────────────────────────────────────────────────────────────
destroy() {
require_wsl
guard_name
if ! box_exists; then
echo "no distro '$BOX' to remove"
else
echo "about to PERMANENTLY delete the distro '$BOX' and its filesystem."
"$WSL_EXE" --terminate "$BOX" 2>/dev/null || true
"$WSL_EXE" --unregister "$BOX"
echo " unregistered"
fi
local winhome; winhome=$(rootfs_path)
rm -rf "${winhome}WSL/${BOX}" 2>/dev/null || true
if [ "${1:-}" = "--purge" ]; then
rm -f "${winhome}rig-rootfs.tar"
echo " cached rootfs removed"
fi
}
status() {
require_wsl
echo "environment $CLUSTER"
echo "distro $BOX"
if box_exists; then
echo "registered yes"
echo "provisioned $("$WSL_EXE" -d "$BOX" -u root -- test -f /etc/rig-provisioned 2>/dev/null && echo yes || echo no)"
echo "docker $("$WSL_EXE" -d "$BOX" -u root -- bash -lc 'systemctl is-active docker 2>/dev/null' || echo unknown)"
echo "rig copied $("$WSL_EXE" -d "$BOX" -u "$BOX_USER" -- bash -lc 'test -f ~/rig/Makefile && echo yes || echo no' 2>/dev/null)"
else
echo "registered no"
fi
echo
echo "all distros (this one is never touched unless it is '$BOX'):"
wsl_list | sed 's/^/ /'
}
shell() {
require_wsl
box_exists || { echo "no distro '$BOX' — run 'make newbox' first" >&2; exit 1; }
"$WSL_EXE" -d "$BOX" -u "$BOX_USER" --cd '~'
}
case "${1:-status}" in
create) create ;;
destroy) shift; destroy "${1:-}" ;;
status) status ;;
shell) shell ;;
*) echo "usage: $0 [create|destroy [--purge]|status|shell]" >&2; exit 1 ;;
esac

View File

@@ -168,6 +168,26 @@ check "guards on the variable" "1" "$(grep -c 'allow_k8s_contexts(CTX)' Tiltfil
check "asks ports.sh for facts" "1" "$(grep -c "local('bash ports.sh active'" Tiltfile)" check "asks ports.sh for facts" "1" "$(grep -c "local('bash ports.sh active'" Tiltfile)"
note "the standalone Makefile only calls real verbs"
# That file exists so nothing wrapping these scripts has to GUESS how to call
# them. A generated Makefile once did guess: `rigmini.sh on`, which is not a
# verb, and a bare `rigdeps.sh` for "check and report", which installs. So each
# target's default verb must be one the script's own dispatch accepts — read
# from the script's `case`, not from a list here that could drift from it.
verbs_of() {
sed -n '/^case "\$cmd" in/,/^esac/p' "$1" | grep -oE '^ [a-z]+\)' | tr -d ' )'
}
for pair in deps:rigdeps.sh mem:rigmini.sh; do
target=${pair%%:*}; script=${pair##*:}
verb="$(make --no-print-directory -s -n -f ../standalone/Makefile "$target" 2>/dev/null \
| awk -v s="$script" 'index($0, s) { print $NF; exit }')"
check "make $target -> $script ${verb:-?}, a verb it accepts" "yes" \
"$(verbs_of "../standalone/$script" | grep -qx "$verb" && echo yes || echo "no: '$verb'")"
done
check "no \`mini\` target, which already means minimal footprint" "0" \
"$(grep -cE '^mini:' ../standalone/Makefile)"
note "optional — needs tilt and this rig's cluster" note "optional — needs tilt and this rig's cluster"
# Parsing the Tiltfile for real is the only way to know it still evaluates, but # 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 # Tilt snapshots a kubectl context before parsing, so it cannot run without a

View File

@@ -11,13 +11,9 @@
# an unfamiliar machine, the full picture is the whole point. Failures are # an unfamiliar machine, the full picture is the whole point. Failures are
# collected and reported together, and the exit code reflects the worst outcome. # collected and reported together, and the exit code reflects the worst outcome.
# #
# The same script runs inside a fresh throwaway distro (newbox), so the
# provisioning path and the everyday path cannot drift apart.
#
# Usage: # Usage:
# setup.sh # host checks + the dev toolchain # setup.sh # host checks + the dev toolchain
# setup.sh core # kubectl and jq only — no cluster tooling # setup.sh core # kubectl and jq only — no cluster tooling
# setup.sh --share-docker # ...and offer this distro's Docker to others
# setup.sh --cluster # ...and bring the cluster up # setup.sh --cluster # ...and bring the cluster up
set -euo pipefail set -euo pipefail
cd "$(dirname "$0")" cd "$(dirname "$0")"
@@ -25,7 +21,6 @@ cd "$(dirname "$0")"
source ./lib/config.sh source ./lib/config.sh
load_config load_config
WITH_SHARE=0
WITH_CLUSTER=0 WITH_CLUSTER=0
# Cluster tooling is not wanted everywhere: a managed or corporate-issued # Cluster tooling is not wanted everywhere: a managed or corporate-issued
# machine may legitimately want kubectl and nothing that builds clusters. # machine may legitimately want kubectl and nothing that builds clusters.
@@ -33,7 +28,6 @@ TIER=dev
for a in "$@"; do for a in "$@"; do
case "$a" in case "$a" in
core|dev) TIER="$a" ;; core|dev) TIER="$a" ;;
--share-docker) WITH_SHARE=1 ;;
--cluster) WITH_CLUSTER=1 ;; --cluster) WITH_CLUSTER=1 ;;
*) echo "unknown option: $a" >&2; exit 1 ;; *) echo "unknown option: $a" >&2; exit 1 ;;
esac esac
@@ -143,33 +137,6 @@ step_docker() {
fi fi
} }
step_share_docker() {
if [ "$WITH_SHARE" -ne 1 ]; then
record docker-share skip "not requested (--share-docker)"
return
fi
if ! grep -qi microsoft /proc/version 2>/dev/null; then
record docker-share skip "not WSL — sharing only applies between WSL distros"
return
fi
if [ -f /etc/systemd/system/docker.service.d/10-rig-shared-socket.conf ]; then
record docker-share already "this distro is offering its Docker to others"
return
fi
# Needs root, and asking mid-script is worse than telling the user the
# single command to run.
if [ "$(id -u)" -ne 0 ] && ! sudo -n true 2>/dev/null; then
record docker-share manual "run: sudo bash ctrl/dockerhost.sh share"
return
fi
if sudo bash ./dockerhost.sh share >/tmp/rig-share.$$ 2>&1; then
record docker-share done "this distro now owns the shared Docker"
rm -f "/tmp/rig-share.$$"
else
record docker-share fail "see /tmp/rig-share.$$"
fi
}
step_ports() { step_ports() {
local busy="" local busy=""
for entry in "HTTP:$HTTP_PORT" "HTTPS:$HTTPS_PORT" "TILT:$TILT_PORT" "REGISTRY:$REGISTRY_PORT"; do for entry in "HTTP:$HTTP_PORT" "HTTPS:$HTTPS_PORT" "TILT:$TILT_PORT" "REGISTRY:$REGISTRY_PORT"; do
@@ -215,7 +182,6 @@ step_host
step_toolchain step_toolchain
step_path step_path
step_docker step_docker
step_share_docker
step_ports step_ports
step_cluster step_cluster

View File

@@ -298,7 +298,6 @@ make cluster up <span class="c"># build the cluster for the active profile</spa
unfamiliar machine the complete list is the point. The tail of the output is unfamiliar machine the complete list is the point. The tail of the output is
a to-do list of only what is outstanding.</p> a to-do list of only what is outstanding.</p>
<pre><code>make setup <span class="c"># host + toolchain</span> <pre><code>make setup <span class="c"># host + toolchain</span>
make setup --share-docker <span class="c"># ...and offer this machine's Docker to other distros</span>
</code></pre> </code></pre>
<h3>3 &middot; make cluster up</h3> <h3>3 &middot; make cluster up</h3>
@@ -325,7 +324,6 @@ make cluster reset <span class="c"># destroy and rebuild — the on
<dt>make cluster list</dt><dd>Every cluster on the machine, its memory cost and its port block. The usual reason a new one will not start is an old one you forgot about; <code>make cluster free</code> frees them without deleting.</dd> <dt>make cluster list</dt><dd>Every cluster on the machine, its memory cost and its port block. The usual reason a new one will not start is an old one you forgot about; <code>make cluster free</code> frees them without deleting.</dd>
<dt>make ports</dt><dd>This environment's port block, and whether each is derived or overridden.</dd> <dt>make ports</dt><dd>This environment's port block, and whether each is derived or overridden.</dd>
<dt>make registry</dt><dd>Which of the four registry modes is active, and where it points.</dd> <dt>make registry</dt><dd>Which of the four registry modes is active, and where it points.</dd>
<dt>make dockerhost</dt><dd>Which WSL distro owns Docker and what this one is using.</dd>
</dl> </dl>
<h3>Running more than one</h3> <h3>Running more than one</h3>

43
rig/standalone/Makefile Normal file
View File

@@ -0,0 +1,43 @@
# Optional shorthand for the two standalone scripts. They run without it:
# `bash rigdeps.sh detect` and `bash rigmini.sh status` are the whole interface,
# and this file only spells those out so nobody has to guess them.
#
# Guessing is what went wrong before. A Makefile generated around these scripts
# invented `make mini` -> `rigmini.sh on`, a verb that does not exist, and a bare
# `rigdeps.sh` for "check and report", which actually installs. So every target
# here calls only a verb the script accepts, and ctrl/selftest.sh checks that.
#
# Works wherever the three files sit together — the scripts are found beside
# this Makefile, not in the current directory, so `make -f path/Makefile` works.
#
# Usage:
# make deps report the host and toolchain; changes nothing
# make deps install [dev] download, verify and install into ~/.local/bin
# make mem advertised memory and what caps it; safe
# make mem push allocates until it stops — not on a box you need
HERE := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
# Words after the target become the script's verb and arguments.
ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
ifneq ($(ARGS),)
$(eval $(ARGS):;@:)
.PHONY: $(ARGS)
endif
.DEFAULT_GOAL := help
.PHONY: help deps mem
help: ## list targets
@grep -hE '^[a-z][a-z-]*:.*?##' $(MAKEFILE_LIST) | sed 's/:.*##/\t/' | expand -t16
# Defaults to `detect`, not `install`: on a machine you are still evaluating,
# the bare command should report. Installing is a word you type.
deps: ## toolchain [detect|list|verify|install|fetch] (default detect)
bash $(HERE)rigdeps.sh $(or $(ARGS),detect)
# `mem`, matching the full rig's `make mem`, and deliberately NOT `mini`: that
# word already means "minimal footprint" in the projects that use this, and one
# name pointing at two jobs is how the invented target happened.
mem: ## memory and its caps [status|push|all] (default status)
bash $(HERE)rigmini.sh $(or $(ARGS),status)

View File

@@ -39,6 +39,20 @@ bash rigmini.sh status # advertised memory and caps; safe
bash rigmini.sh push # allocates until it stops — not on a machine you need bash rigmini.sh push # allocates until it stops — not on a machine you need
``` ```
The `Makefile` beside them is **optional shorthand** for exactly those calls —
copy it along or don't; the scripts do not need it:
```bash
make deps # = rigdeps.sh detect (reports; installing is `make deps install`)
make mem # = rigmini.sh status
```
If you wrap these scripts in a Makefile of your own, copy the calls from that
file rather than guessing them. `rigmini.sh` measures memory; it has no `on` or
`off`, and toggling a heavy service off for a smaller footprint is a job for the
project's own manifests, not for this script. `make selftest` in the full rig
fails if this Makefile ever calls a verb its script does not accept.
If an earlier setup already put these tools in some other directory on PATH, If an earlier setup already put these tools in some other directory on PATH,
remove that directory and the line that added it — do not rely on `install` to remove that directory and the line that added it — do not rely on `install` to
notice. It only reports shadowing once `~/.local/bin` is itself on PATH, which on notice. It only reports shadowing once `~/.local/bin` is itself on PATH, which on