Files
44r0n7 0265afa054 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.
2026-05-02 11:49:07 -04:00

386 lines
13 KiB
Bash
Executable File

#!/usr/bin/env bash
# Sysadmin Chronicles — Installer
# Usage: bash install.sh
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$SCRIPT_DIR"
source "$PROJECT_ROOT/tools/lib/ui.sh"
source "$PROJECT_ROOT/tools/lib/config.sh"
source "$PROJECT_ROOT/tools/lib/deps.sh"
source "$PROJECT_ROOT/tools/lib/libvirt.sh"
source "$PROJECT_ROOT/tools/lib/vm.sh"
OWNER_USER="${SUDO_USER:-$USER}"
OWNER_HOME="$(getent passwd "$OWNER_USER" | cut -d: -f6)"
OWNER_HOME="${OWNER_HOME:-$HOME}"
SC_LOG_DIR="$OWNER_HOME/.local/share/sysadmin-chronicles"
export SC_INSTALL_LOG="$SC_LOG_DIR/install.log"
export LIBVIRT_DEFAULT_URI="${LIBVIRT_DEFAULT_URI:-qemu:///system}"
# ---------------------------------------------------------------------------
# Phase 1 — Welcome
# ---------------------------------------------------------------------------
sc_header "SYSADMIN CHRONICLES — SETUP"
cat << 'WELCOME'
Welcome! This installer will:
• Install a few system tools (KVM, QEMU, libvirt)
• Set up a private virtual network for the game
• Build three virtual machines (~30 minutes, once only)
WELCOME
DEFAULT_GAME_DIR="$OWNER_HOME/Games/sysadmin-chronicles"
[ "$PROJECT_ROOT" = "$DEFAULT_GAME_DIR" ] || DEFAULT_GAME_DIR="$PROJECT_ROOT"
SC_GAME_DIR="$(sc_prompt "Where would you like to install the game?" "$DEFAULT_GAME_DIR")"
while [[ "$SC_GAME_DIR" == *//* ]]; do
SC_GAME_DIR="${SC_GAME_DIR//\/\//\/}"
done
while [ "$SC_GAME_DIR" != "/" ] && [ "${SC_GAME_DIR%/}" != "$SC_GAME_DIR" ]; do
SC_GAME_DIR="${SC_GAME_DIR%/}"
done
echo ""
SC_IMAGES_DIR="$SC_GAME_DIR/images"
# Build scripts normally default to /var/lib/libvirt when using qemu:///system.
# The installer asks for a custom game directory, so force the VM tooling to use
# the libvirt storage pool path selected above instead of silently falling back
# to /var/lib/libvirt/images/sysadmin-chronicles.
export SC_HOME="$SC_LOG_DIR"
export SC_IMAGE_ROOT="$SC_IMAGES_DIR"
export SC_POOL_NAME="sc-images"
export SC_NETWORK_NAME="sc-internal"
export SC_OWNER_USER="$OWNER_USER"
export SC_OWNER_HOME="$OWNER_HOME"
sc_log_append() {
mkdir -p "$SC_LOG_DIR"
{
printf '\n[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
} >> "$SC_INSTALL_LOG" 2>/dev/null || true
}
sc_log_cmd() {
mkdir -p "$SC_LOG_DIR"
{
printf '\n[%s] $' "$(date '+%Y-%m-%d %H:%M:%S')"
printf ' %q' "$@"
printf '\n'
"$@"
} >> "$SC_INSTALL_LOG" 2>&1
}
# ---------------------------------------------------------------------------
# Phase 2 — Silent system check
# ---------------------------------------------------------------------------
detect_distro
if [ "$SC_DISTRO" = "unknown" ]; then
sc_warn "Could not detect your Linux distribution."
sc_warn "Dependency auto-install may not work — you may need to install packages manually."
echo ""
fi
mapfile -t _missing_deps < <(check_deps 2>/dev/null || true)
# ---------------------------------------------------------------------------
# Phase 3 — Dependency install (only if needed)
# ---------------------------------------------------------------------------
if [ "${#_missing_deps[@]}" -gt 0 ]; then
sc_section "System check"
echo ""
echo " Your system is missing the following tools:"
echo ""
_shown_labels=""
for _dep in "${_missing_deps[@]}"; do
[ -z "$_dep" ] && continue
_label="$(dep_label "$_dep")"
[ -z "$_label" ] && continue
case "|${_shown_labels}|" in
*"|${_label}|"*) ;;
*) echo "$_label"; _shown_labels="${_shown_labels}|${_label}" ;;
esac
done
echo ""
if sc_confirm "Install them now? You'll be asked for your password." "Y"; then
echo ""
mkdir -p "$SC_LOG_DIR"
# Write log header
cat > "$SC_INSTALL_LOG" << EOF
# Sysadmin Chronicles — Install Log
# Created: $(date '+%Y-%m-%d %H:%M:%S')
# Distro: ${SC_DISTRO} ($(uname -r))
# Game dir: $SC_GAME_DIR
# Images: $SC_IMAGES_DIR
EOF
install_deps "${_missing_deps[@]}"
log_present_deps
echo ""
# Append manual removal hint
{
echo ""
echo "# To remove manually:"
printf '# sudo %s\n' "$(case "$SC_DISTRO" in
arch) echo 'pacman -Rns libvirt qemu-system-x86 virt-install virt-viewer' ;;
debian|ubuntu) echo 'apt-get remove libvirt-daemon-system qemu-kvm virt-manager' ;;
fedora) echo 'dnf remove libvirt qemu-kvm virt-install' ;;
*) echo 'remove the packages listed above' ;;
esac)"
} >> "$SC_INSTALL_LOG"
sc_ok "Dependencies installed."
echo ""
echo " Install log: $SC_INSTALL_LOG"
echo ""
else
echo ""
sc_warn "Skipping dependency install. Some features may not work."
echo ""
fi
else
mkdir -p "$SC_LOG_DIR"
if [ ! -f "$SC_INSTALL_LOG" ]; then
cat > "$SC_INSTALL_LOG" << EOF
# Sysadmin Chronicles — Install Log
# Created: $(date '+%Y-%m-%d %H:%M:%S')
# Distro: ${SC_DISTRO} ($(uname -r))
# Game dir: $SC_GAME_DIR
# Images: $SC_IMAGES_DIR
EOF
log_present_deps
fi
fi
# ---------------------------------------------------------------------------
# Phase 4 — Network, storage, and SSH key setup
# ---------------------------------------------------------------------------
# Ensure libvirtd is running before touching networks or pools.
# Pacman installs the socket unit but does not start/enable it.
_libvirtd_ready=false
_need_relogin=false
# Ensure user is in the libvirt group (needed for virsh access without sudo)
if ! groups "$OWNER_USER" 2>/dev/null | grep -qw libvirt; then
sc_info "Adding $OWNER_USER to the libvirt group..."
sudo usermod -aG libvirt "$OWNER_USER" 2>/dev/null || true
_need_relogin=true
fi
# Clear any failed unit state from a previous attempt
sudo systemctl reset-failed libvirtd.service libvirtd.socket 2>/dev/null || true
if ! systemctl is-active --quiet libvirtd.service 2>/dev/null; then
sc_info "Starting virtual machine daemon..."
# Enable the socket for future boots, start the service directly now
sudo systemctl enable libvirtd.socket >/dev/null 2>&1 || true
if ! sudo systemctl start libvirtd.service >/dev/null 2>&1; then
echo ""
echo " libvirtd failed to start. Details:"
sudo systemctl status libvirtd.service --no-pager -l 2>&1 | tail -25 | sed 's/^/ /'
echo ""
sc_fail "Fix the error above, then run install.sh again."
fi
fi
# Wait up to 30s for libvirtd.service to reach active state (pure systemd
# check — no virsh connection needed, works regardless of group membership
# in the current session).
for _i in $(seq 1 60); do
if systemctl is-active --quiet libvirtd.service 2>/dev/null; then
_libvirtd_ready=true
break
fi
sleep 0.5
done
if [ "$_libvirtd_ready" != true ]; then
echo ""
echo " libvirtd did not reach active state. Current status:"
sudo systemctl status libvirtd.service --no-pager -l 2>&1 | tail -25 | sed 's/^/ /'
echo ""
sc_fail "Fix the error above, then run install.sh again."
fi
# If the group was just added, the current shell does not have it yet. The
# install can still proceed by using sudo for virsh operations.
if virsh -c "$LIBVIRT_DEFAULT_URI" list >/dev/null 2>&1; then
export SC_VIRSH_SUDO=false
else
export SC_VIRSH_SUDO=true
sc_log_append "virsh is not usable by $OWNER_USER in this session; using sudo for installer libvirt operations."
fi
sc_log_append "Installer paths: SC_GAME_DIR=$SC_GAME_DIR SC_IMAGES_DIR=$SC_IMAGES_DIR SC_IMAGE_ROOT=$SC_IMAGE_ROOT LIBVIRT_DEFAULT_URI=$LIBVIRT_DEFAULT_URI SC_VIRSH_SUDO=${SC_VIRSH_SUDO:-false}"
sc_log_cmd id || true
sc_log_cmd groups "$OWNER_USER" || true
sc_log_cmd systemctl is-active libvirtd.service || true
sc_section "Setting up game network"
echo ""
NETWORK_XML="$PROJECT_ROOT/tools/vm/network-sc-internal.xml"
sc_spinner "Creating private game network"
if ! ensure_network "sc-internal" "$NETWORK_XML"; then
sc_spinner_stop
echo ""
sc_fail "Could not create the game network.
Make sure the virtual machine tools are installed and running:
sudo systemctl enable --now libvirtd.socket
Then run install.sh again."
fi
sc_spinner_stop
sc_ok "Private game network created"
sc_spinner "Configuring VM image storage"
if ! ensure_pool "sc-images" "$SC_IMAGES_DIR"; then
sc_spinner_stop
sc_fail "Could not configure image storage at $SC_IMAGES_DIR"
fi
sc_spinner_stop
sc_ok "VM image storage configured at $SC_IMAGES_DIR"
# Pre-create subdirectories used by tools/vm/lib/common.sh. Since these live
# under the user-selected install path, they should be user-writable. This also
# fails early with a useful message instead of producing a one-line build log.
if ! mkdir -p "$SC_IMAGES_DIR/base" "$SC_IMAGES_DIR/seed"; then
sc_fail "Could not create VM image directories under $SC_IMAGES_DIR"
fi
chown -R "$OWNER_USER:$OWNER_USER" "$SC_IMAGES_DIR" 2>/dev/null || true
sc_log_cmd ls -lah "$SC_IMAGES_DIR" || true
SC_SSH_KEY="$OWNER_HOME/.ssh/sc_host_key"
if [ ! -f "$SC_SSH_KEY" ]; then
sc_spinner "Generating game access keys"
mkdir -p "$(dirname "$SC_SSH_KEY")"
ssh-keygen -t ed25519 -N "" -C "sysadmin-chronicles-host" -f "$SC_SSH_KEY" >/dev/null 2>&1
chmod 600 "$SC_SSH_KEY"
chmod 644 "${SC_SSH_KEY}.pub"
chown "$OWNER_USER:$OWNER_USER" "$SC_SSH_KEY" "${SC_SSH_KEY}.pub" 2>/dev/null || true
sc_spinner_stop
sc_ok "Game access keys generated"
else
sc_ok "Game access keys already present"
fi
# Write config (survives game dir moves)
config_write SC_GAME_DIR "$SC_GAME_DIR"
config_write SC_IMAGES_DIR "$SC_IMAGES_DIR"
config_write SC_LIBVIRT_URI "$LIBVIRT_DEFAULT_URI"
config_write SC_INSTALL_DATE "$(date '+%Y-%m-%d')"
echo ""
# ---------------------------------------------------------------------------
# Phase 5 — VM build
# ---------------------------------------------------------------------------
sc_section "Building your game world"
echo ""
echo " This happens once and takes about 30 minutes."
echo " You can leave this running in the background."
echo ""
_build_vm() {
local label="$1"
local n="$2"
local total="$3"
local profile="$4"
local logfile="$SC_LOG_DIR/build-${profile}.log"
local start_ts elapsed mins secs
start_ts="$(date +%s)"
printf " Building %-20s (%d/%d) " "$label" "$n" "$total"
{
echo "# Sysadmin Chronicles VM build log"
echo "# Created: $(date '+%Y-%m-%d %H:%M:%S')"
echo "# Profile: $profile"
echo "# Game dir: $SC_GAME_DIR"
echo "# Images: $SC_IMAGES_DIR"
echo "# SC_IMAGE_ROOT: $SC_IMAGE_ROOT"
echo "# LIBVIRT_DEFAULT_URI: $LIBVIRT_DEFAULT_URI"
echo "# SC_VIRSH_SUDO: ${SC_VIRSH_SUDO:-false}"
echo ""
} > "$logfile"
if vm_build "$profile" >> "$logfile" 2>&1; then
elapsed=$(( $(date +%s) - start_ts ))
mins=$(( elapsed / 60 ))
secs=$(( elapsed % 60 ))
printf "✓ %dm %02ds\n" "$mins" "$secs"
else
printf "✗\n"
sc_warn "Build failed — see $logfile"
return 1
fi
}
echo "Generating TLS certificates..."
bash "$PROJECT_ROOT/tools/setup/generate-certs.sh"
echo ""
_build_vm "workstation" 1 3 "workstation"
_build_vm "web server" 2 3 "web-server"
_build_vm "build server" 3 3 "build-machine"
printf " Setting up quest scenarios "
bash "$PROJECT_ROOT/tools/setup/seed-vms.sh" --skip-build \
> "$SC_LOG_DIR/seed-vms.log" 2>&1 && printf "✓\n" || { printf "✗\n"; sc_warn "Quest setup had errors — see $SC_LOG_DIR/seed-vms.log"; }
echo ""
# ---------------------------------------------------------------------------
# Phase 6 — Application menu entry
# ---------------------------------------------------------------------------
sc_section "Application menu launcher"
echo ""
if sc_confirm "Create an application-menu launcher? This does not add a desktop icon." "Y"; then
DESKTOP_FILE="$OWNER_HOME/.local/share/applications/sysadmin-chronicles.desktop"
mkdir -p "$(dirname "$DESKTOP_FILE")"
cat > "$DESKTOP_FILE" << EOF
[Desktop Entry]
Name=Sysadmin Chronicles
Comment=A sysadmin simulation game
Exec=bash $SC_GAME_DIR/start-game.sh
Terminal=true
Type=Application
Categories=Game;
EOF
sc_ok "Application-menu launcher created"
fi
echo ""
# ---------------------------------------------------------------------------
# Phase 7 — Done
# ---------------------------------------------------------------------------
sc_done_banner
cat << EOF
Start the game:
bash $SC_GAME_DIR/start-game.sh
(or from your system application menu if you created the launcher; no desktop icon is installed)
Rebuild the virtual machines at any time:
bash $SC_GAME_DIR/tools/vm/rebuild-vms.sh
Install log:
$SC_INSTALL_LOG
EOF
if [ "$_need_relogin" = true ]; then
sc_warn "You were added to the 'libvirt' group during install."
sc_warn "Log out and back in before running the game, or run:"
echo " newgrp libvirt"
echo ""
fi