A machine's role is now the interview's first question and the one answer Panama records. Servers get the same shell minus the screen: core packages, nvm, Bun, Claude Code and Codex (desktops get Codex too), linger, rootless ports from 80, firewalld, the nginx-bridge network, and a nightly image updater that replaced watchtower for cause. server/containers/ carries junior's 23 compose services -- secrets moved to per-machine .env files that never enter this public repo, every transformed compose proven to render byte-identical to what is live. 'panama server' enables, disables and relinks them; nothing here restarts a running service. 'boot --server' walks a fresh VPS from its root login to a normal install. Five new contracts pin the secrets rule, the catalog's shape, panama-server's behavior, the role plumbing, and the dotfile classification. Claude-Session: https://claude.ai/code/session_01NU5JGiN3JfzqrLQB6wmJ1E
57 lines
2.0 KiB
Bash
Executable File
57 lines
2.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Every directory under config/dot is claimed by exactly one of
|
|
# link-dotfiles' three lists -- universal (both roles), desktop, or handled
|
|
# (linked or consumed some other way in that script). The failure this
|
|
# prevents is quiet: a new dot directory added to the repo but classified
|
|
# nowhere links on no server, or on every server, depending on which list the
|
|
# author forgot -- and nothing says so until a machine is missing its config.
|
|
#
|
|
# The lists are lifted from the script itself rather than restated here, so
|
|
# renaming them fails loudly instead of freezing a stale copy.
|
|
|
|
set -uo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
link_dotfiles="$repo_dir/setup/scripts/link-dotfiles"
|
|
dot_dir="$repo_dir/config/dot"
|
|
|
|
findings=()
|
|
note() { findings+=("$1"); }
|
|
|
|
lists="$(sed -n '/^universal_dirs=(/,/^handled_dirs=(.*)$/p' "$link_dotfiles")"
|
|
if [[ -z "$lists" ]]; then
|
|
printf 'dotfile classification contract: link-dotfiles no longer defines the three lists\n' >&2
|
|
exit 1
|
|
fi
|
|
eval "$lists"
|
|
|
|
claimed=("${universal_dirs[@]}" "${desktop_dirs[@]}" "${handled_dirs[@]}")
|
|
|
|
for dir in "$dot_dir"/*/; do
|
|
name="$(basename "$dir")"
|
|
hits=0
|
|
for entry in "${claimed[@]}"; do
|
|
[[ "$entry" == "$name" ]] && hits=$((hits + 1))
|
|
done
|
|
if (( hits == 0 )); then
|
|
note "config/dot/$name is claimed by no list, so servers silently skip or acquire it"
|
|
elif (( hits > 1 )); then
|
|
note "config/dot/$name is claimed by $hits lists"
|
|
fi
|
|
done
|
|
|
|
for entry in "${claimed[@]}"; do
|
|
[[ -d "$dot_dir/$entry" ]] \
|
|
|| note "link-dotfiles classifies '$entry', which config/dot does not contain"
|
|
done
|
|
|
|
if (( ${#findings[@]} > 0 )); then
|
|
mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
|
|
printf 'dotfile classification contract: %d finding(s)\n' "${#findings[@]}" >&2
|
|
printf ' - %s\n' "${findings[@]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf 'dotfile classification contract: PASS (%d directories classified)\n' "${#claimed[@]}"
|