chore: bootstrap lean sysadmin-chronicles repo

Import the runnable game code, content, docs, scripts, and repo guidance while leaving local agent state, dependency installs, build output, and backup copies out of the published tree.
This commit is contained in:
2026-05-02 11:49:07 -04:00
commit 0265afa054
252 changed files with 37574 additions and 0 deletions
+311
View File
@@ -0,0 +1,311 @@
#!/usr/bin/env bash
# Rebuild or revert game virtual machines.
#
# Usage:
# rebuild-vms.sh Interactive menu
# rebuild-vms.sh --vm workstation Rebuild a single VM (interactive)
# rebuild-vms.sh --revert Revert all VMs to baseline snapshot
# rebuild-vms.sh --revert --vm workstation
# rebuild-vms.sh --snapshot --vm workstation --name before-risky-thing
# rebuild-vms.sh --snapshot --all --name pre-shift-4
# rebuild-vms.sh --revert --name before-risky-thing --vm workstation
# rebuild-vms.sh --dry-run [other flags]
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
source "$PROJECT_ROOT/tools/lib/ui.sh"
source "$PROJECT_ROOT/tools/lib/config.sh"
source "$PROJECT_ROOT/tools/lib/libvirt.sh"
source "$PROJECT_ROOT/tools/lib/vm.sh"
config_read || true
_normalize_dir_path() {
local path="${1:-}"
while [[ "$path" == *//* ]]; do
path="${path//\/\//\/}"
done
while [ "$path" != "/" ] && [ "${path%/}" != "$path" ]; do
path="${path%/}"
done
printf '%s\n' "$path"
}
if [ -n "${SC_GAME_DIR:-}" ]; then
normalized_game_dir="$(_normalize_dir_path "$SC_GAME_DIR")"
if [ "$normalized_game_dir" != "$SC_GAME_DIR" ]; then
SC_GAME_DIR="$normalized_game_dir"
config_write SC_GAME_DIR "$SC_GAME_DIR"
fi
fi
if [ -n "${SC_IMAGES_DIR:-}" ]; then
normalized_images_dir="$(_normalize_dir_path "$SC_IMAGES_DIR")"
elif [ -n "${SC_GAME_DIR:-}" ]; then
normalized_images_dir="$SC_GAME_DIR/images"
else
normalized_images_dir=""
fi
if [ -n "$normalized_images_dir" ]; then
if [ "${SC_IMAGES_DIR:-}" != "$normalized_images_dir" ]; then
SC_IMAGES_DIR="$normalized_images_dir"
config_write SC_IMAGES_DIR "$SC_IMAGES_DIR"
fi
export SC_IMAGE_ROOT="$SC_IMAGES_DIR"
fi
export LIBVIRT_DEFAULT_URI="${SC_LIBVIRT_URI:-${LIBVIRT_DEFAULT_URI:-qemu:///system}}"
export SC_POOL_NAME="${SC_POOL_NAME:-sc-images}"
export SC_NETWORK_NAME="${SC_NETWORK_NAME:-sc-internal}"
# VM display names
declare -A VM_LABEL=(
[sc-workstation]="workstation"
[sc-web-server]="web server"
[sc-build-machine]="build server"
)
declare -A VM_PROFILE=(
[sc-workstation]=workstation
[sc-web-server]=web-server
[sc-build-machine]=build-machine
)
ALL_VMS=(sc-workstation sc-web-server sc-build-machine)
DRY_RUN=false
MODE=""
SINGLE_VM=""
SNAP_NAME=""
while [[ $# -gt 0 ]]; do
case "$1" in
--dry-run) DRY_RUN=true; shift ;;
--revert) MODE=revert; shift ;;
--snapshot) MODE=snapshot; shift ;;
--vm) SINGLE_VM="sc-$2"; shift 2 ;;
--name) SNAP_NAME="$2"; shift 2 ;;
--all) SINGLE_VM=""; shift ;;
*) echo "Unknown argument: $1"; exit 1 ;;
esac
done
run() {
if [ "$DRY_RUN" = true ]; then
echo " [dry-run] $*"
else
"$@"
fi
}
# Revert one VM to its newest baseline snapshot; prints result.
_revert_to_baseline() {
local vm="$1"
local label="${VM_LABEL[$vm]:-$vm}"
local candidate local_snap=""
for candidate in "baseline.recovery" "baseline.day-one" "baseline.clean"; do
if snapshot_exists "$vm" "$candidate" 2>/dev/null; then
local_snap="$candidate"
break
fi
done
if [ -n "$local_snap" ]; then
sc_info "Reverting $label to $local_snap..."
run snapshot_revert "$vm" "$local_snap"
sc_ok "$label reverted to $local_snap"
else
sc_warn "No baseline snapshot found for $vm — skipping"
fi
}
_target_vms() {
if [ -n "$SINGLE_VM" ]; then
echo "$SINGLE_VM"
else
printf '%s\n' "${ALL_VMS[@]}"
fi
}
# ---------------------------------------------------------------------------
# Non-interactive flag modes
# ---------------------------------------------------------------------------
if [ "$MODE" = "revert" ] && [ -n "$SNAP_NAME" ]; then
sc_header "REVERTING TO SNAPSHOT: $SNAP_NAME"
while IFS= read -r vm; do
label="${VM_LABEL[$vm]:-$vm}"
sc_info "Reverting $label..."
if snapshot_exists "$vm" "$SNAP_NAME"; then
run snapshot_revert "$vm" "$SNAP_NAME"
sc_ok "$label reverted to $SNAP_NAME"
else
sc_warn "Snapshot '$SNAP_NAME' not found on $vm — skipping"
fi
done < <(_target_vms)
exit 0
fi
if [ "$MODE" = "snapshot" ]; then
[ -n "$SNAP_NAME" ] || { echo " --snapshot requires --name"; exit 1; }
while IFS= read -r vm; do
label="${VM_LABEL[$vm]:-$vm}"
sc_info "Snapshotting $label as '$SNAP_NAME'..."
run vm_snapshot_create "$vm" "$SNAP_NAME"
sc_ok "$label$SNAP_NAME"
done < <(_target_vms)
exit 0
fi
if [ "$MODE" = "revert" ]; then
sc_header "REVERTING TO BASELINE"
while IFS= read -r vm; do
_revert_to_baseline "$vm"
done < <(_target_vms)
exit 0
fi
# ---------------------------------------------------------------------------
# Interactive menu
# ---------------------------------------------------------------------------
sc_header "SYSADMIN CHRONICLES — VM TOOLS"
while true; do
echo " What would you like to do?"
echo ""
echo " 1) Revert all VMs to last known good (fast — ~30s)"
echo " 2) Rebuild workstation (~8 min)"
echo " 3) Rebuild web server (~4 min)"
echo " 4) Rebuild build server (~5 min)"
echo " 5) Rebuild everything (~20 min)"
echo " 6) Take a snapshot"
echo " 7) Revert to a named snapshot"
echo ""
echo " q) Cancel"
echo ""
printf " > " >/dev/tty
read -r choice </dev/tty
echo ""
case "$choice" in
q|Q)
echo " Cancelled."
exit 0
;;
1)
sc_section "Reverting all VMs to baseline"
for vm in "${ALL_VMS[@]}"; do
_revert_to_baseline "$vm"
done
break
;;
2|3|4|5)
case "$choice" in
2) targets=(sc-workstation) ;;
3) targets=(sc-web-server) ;;
4) targets=(sc-build-machine) ;;
5) targets=("${ALL_VMS[@]}") ;;
esac
overall_status=0
for vm in "${targets[@]}"; do
label="${VM_LABEL[$vm]:-$vm}"
profile="${VM_PROFILE[$vm]}"
echo ""
sc_warn "This will permanently rebuild $label."
sc_warn "Quest progress on this VM will be lost."
echo ""
if sc_confirm "Back up save data first?" "Y"; then
BACKUP="$HOME/.local/share/sysadmin-chronicles/saves/pre-rebuild-$(date +%Y%m%d-%H%M%S).json"
[ -f "$HOME/.local/share/sysadmin-chronicles/saves/autosave.json" ] \
&& cp "$HOME/.local/share/sysadmin-chronicles/saves/autosave.json" "$BACKUP" \
&& sc_ok "Save backed up to $BACKUP" || sc_info "(no autosave found)"
fi
if ! sc_confirm "Rebuild $label now?" "N"; then
sc_info "Skipping $label."
continue
fi
sc_section "Rebuilding $label"
logfile="$HOME/.local/share/sysadmin-chronicles/rebuild-${profile}.log"
printf " Rebuilding %-18s " "$label"
start_ts="$(date +%s)"
if run vm_rebuild "$profile" $( [ "$DRY_RUN" = true ] && echo "--dry-run" ) \
> "$logfile" 2>&1; then
elapsed=$(( $(date +%s) - start_ts ))
printf "✓ %dm %02ds\n" $(( elapsed / 60 )) $(( elapsed % 60 ))
else
printf "✗\n"
sc_warn "Rebuild failed — see $logfile"
overall_status=1
continue
fi
# Re-run quest prep and re-snapshot
sc_info "Re-running quest prep for $vm..."
if run bash "$PROJECT_ROOT/tools/setup/seed-vms.sh" --skip-build --vm "${profile//-/_}" \
>> "$logfile" 2>&1; then
sc_ok "$label rebuild complete"
else
sc_warn "Quest prep had errors — see $logfile"
overall_status=1
fi
done
[ "$overall_status" -eq 0 ] || exit "$overall_status"
break
;;
6)
echo " Take a snapshot"
echo ""
echo " Which VM?"
for i in "${!ALL_VMS[@]}"; do
vm="${ALL_VMS[$i]}"
printf " %d) %s\n" $(( i + 1 )) "${VM_LABEL[$vm]:-$vm}"
done
printf " > " >/dev/tty
read -r vm_choice </dev/tty
echo ""
vm="${ALL_VMS[$(( vm_choice - 1 ))]:-}"
[ -n "$vm" ] || { sc_warn "Invalid choice"; continue; }
printf " Snapshot name (letters, numbers, hyphens): " >/dev/tty
read -r snap_name </dev/tty
echo ""
run vm_snapshot_create "$vm" "$snap_name" \
&& sc_ok "Snapshot created: $snap_name on ${VM_LABEL[$vm]:-$vm}" \
|| sc_warn "Snapshot failed."
break
;;
7)
echo " Revert to a named snapshot"
echo ""
echo " Which VM?"
for i in "${!ALL_VMS[@]}"; do
vm="${ALL_VMS[$i]}"
printf " %d) %s\n" $(( i + 1 )) "${VM_LABEL[$vm]:-$vm}"
done
printf " > " >/dev/tty
read -r vm_choice </dev/tty
echo ""
vm="${ALL_VMS[$(( vm_choice - 1 ))]:-}"
[ -n "$vm" ] || { sc_warn "Invalid choice"; continue; }
echo " Available snapshots on ${VM_LABEL[$vm]:-$vm}:"
virsh snapshot-list "$vm" --name 2>/dev/null | grep -v '^$' | sed 's/^/ /' || true
echo ""
printf " Snapshot name to revert to: " >/dev/tty
read -r snap_name </dev/tty
echo ""
snap_date="$(virsh snapshot-info "$vm" "$snap_name" 2>/dev/null | grep 'Creation Time' | awk '{print $3, $4}' || echo "")"
[ -n "$snap_date" ] && sc_info "Snapshot date: $snap_date"
if sc_confirm "Revert ${VM_LABEL[$vm]:-$vm} to '$snap_name'?" "N"; then
run vm_snapshot_revert "$vm" "$snap_name" \
&& sc_ok "Reverted to $snap_name" \
|| sc_warn "Revert failed."
fi
break
;;
*)
sc_warn "Invalid choice — enter 17 or q."
;;
esac
done
echo ""