magicciv/scripts/dev-setup/bluefin.sh

216 lines
8.4 KiB
Bash
Raw Permalink Normal View History

#!/usr/bin/env bash
# Bluefin / rpm-ostree / bootc dev-environment setup for Magic Civilization
#
# Installs the Linux-host packages that this project's tooling needs
# but that are NOT in the base ublue/bluefin image:
#
# - weston — Wayland compositor used by the headless
# Godot screenshot pipeline + Phase Gate proof
# scenes (runs flatpak Godot inside a weston
# session so viewport capture produces real
# pixels instead of the dummy-driver null).
# - vulkan-tools — vulkaninfo / vkcube; diagnostic binaries for
# probing the installed Vulkan stack.
# - mesa-vulkan-drivers — always present on ublue; redeclared here so
# this script is a complete dev-deps manifest.
#
# Idempotent: re-running is a no-op once packages are installed. Uses
# `rpm-ostree install --apply-live` so the packages are usable in the
# current boot without waiting for reboot. PERSISTENCE across reboots
# comes from layering in the bootc image
# (`~/Code/bootc-bluefin/containerfiles/Containerfile.desktop-core`);
# this script runs the transient install for fast iteration.
#
# Usage:
# ./run setup:bluefin # install missing packages
# ./run setup:bluefin --check # exit 0 if all present, 1 otherwise
#
# Safe to run as your normal user — internally escalates with `sudo -n`
# for the one rpm-ostree call. If passwordless sudo is not configured,
# you'll be prompted for a password once.
set -uo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
DIM='\033[2m'
NC='\033[0m'
REQUIRED_PACKAGES=(
weston
vulkan-tools
mesa-vulkan-drivers
)
CHECK_ONLY=false
WITH_RUNNER=false
for arg in "$@"; do
case "$arg" in
--check) CHECK_ONLY=true ;;
--with-runner) WITH_RUNNER=true ;;
--help|-h)
echo "Usage: $0 [--check] [--with-runner]"
echo " (no args) Install missing packages via rpm-ostree --apply-live"
echo " --check Exit 0 if all packages are installed, 1 otherwise"
echo " --with-runner Also install + register a forgejo-runner"
echo " (requires FORGEJO_* env — see .env.example)"
exit 0
;;
*) echo "Unknown argument: $arg"; exit 2 ;;
esac
done
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# ── Preconditions ────────────────────────────────────────────────────
if ! command -v rpm-ostree &>/dev/null; then
echo -e "${RED}This script targets bootc / rpm-ostree systems (Bluefin, Silverblue, Kinoite).${NC}"
echo -e "${DIM}For plain Fedora/CentOS use: sudo dnf install ${REQUIRED_PACKAGES[*]}${NC}"
exit 2
fi
# ── Inventory which packages are missing ────────────────────────────
missing=()
present=()
for pkg in "${REQUIRED_PACKAGES[@]}"; do
if rpm -q "$pkg" &>/dev/null; then
present+=("$pkg")
else
missing+=("$pkg")
fi
done
echo -e "${BLUE}Magic Civilization — Bluefin dev-setup${NC}"
echo -e "${DIM}required: ${REQUIRED_PACKAGES[*]}${NC}"
if [ ${#present[@]} -gt 0 ]; then
echo -e " ${GREEN}present${NC} ${present[*]}"
fi
if [ ${#missing[@]} -eq 0 ]; then
echo -e " ${GREEN}all required packages already installed — nothing to do${NC}"
exit 0
fi
echo -e " ${YELLOW}missing${NC} ${missing[*]}"
if [ "$CHECK_ONLY" = "true" ]; then
echo -e "${YELLOW}--check mode: ${#missing[@]} package(s) missing${NC}"
exit 1
fi
# ── Install missing via rpm-ostree --apply-live ─────────────────────
# Use --allow-inactive so rpm-ostree accepts the "already requested but
# not applied" state from a prior partial run and just applies live this
# time. --apply-live makes the current boot see the new binaries without
# reboot; the same packages still need to be added to
# ~/Code/bootc-bluefin/containerfiles/Containerfile.desktop-core for
# persistence across the next bootc image rebuild.
echo ""
echo -e "${BLUE}Installing missing packages (rpm-ostree --apply-live)…${NC}"
echo -e "${DIM}This requires sudo — you may be prompted once.${NC}"
echo ""
if sudo -n true 2>/dev/null; then
SUDO="sudo"
else
SUDO="sudo" # will prompt interactively
fi
# `rpm-ostree install` refuses if the package is "already requested" in a
# previous partial run that didn't finish apply-live. Handle both cases.
for pkg in "${missing[@]}"; do
if rpm-ostree status --json 2>/dev/null | grep -q "\"$pkg\""; then
echo -e " ${DIM}$pkg already staged — apply-live will pick it up${NC}"
fi
done
set +e
$SUDO rpm-ostree install --apply-live --assumeyes --allow-inactive "${missing[@]}"
rc=$?
set -e
if [ $rc -ne 0 ]; then
# Common case: an earlier install --apply-live failed the live step but
# DID stage a deployment. Try `apply-live` on its own.
echo -e "${YELLOW}rpm-ostree install returned $rc; attempting apply-live on the staged deployment…${NC}"
set +e
$SUDO rpm-ostree apply-live --allow-replacement
rc=$?
set -e
fi
# ── Verify ───────────────────────────────────────────────────────────
echo ""
echo -e "${BLUE}Post-install verification${NC}"
fail_count=0
for pkg in "${REQUIRED_PACKAGES[@]}"; do
if rpm -q "$pkg" &>/dev/null; then
echo -e " ${GREEN}installed${NC} $pkg"
else
echo -e " ${RED}MISSING${NC} $pkg"
fail_count=$((fail_count + 1))
fi
done
if [ $fail_count -gt 0 ]; then
echo ""
echo -e "${RED}$fail_count package(s) still missing — re-run or debug rpm-ostree manually.${NC}"
exit 1
fi
# ── Remind user about persistence ────────────────────────────────────
echo ""
echo -e "${GREEN}All required packages installed (live in current boot).${NC}"
echo ""
echo -e "${YELLOW}Persistence note:${NC}"
echo -e " Transient --apply-live installs are wiped on reboot."
echo -e " To persist, add ${REQUIRED_PACKAGES[*]} to your bootc image"
echo -e " layer — typically at:"
echo -e " ${DIM}~/Code/bootc-bluefin/containerfiles/Containerfile.desktop-core${NC}"
echo -e " Rebuild + redeploy via ${DIM}~/Code/bootc-bluefin/rebuild-with-parser.sh${NC}"
echo -e " (or equivalent build.sh / deploy.sh)."
# ── Optional: forgejo-runner install + register + persist ──────────
if $WITH_RUNNER; then
echo ""
echo -e "${BLUE}─────────────────────────────────────────────────${NC}"
echo -e "${BLUE} Forgejo runner (Linux / amd64 / $(hostname -s))${NC}"
echo -e "${BLUE}─────────────────────────────────────────────────${NC}"
# shellcheck source=../run/common.sh
source "$REPO_ROOT/scripts/run/common.sh"
# shellcheck source=./lib/runner.sh
source "$REPO_ROOT/scripts/dev-setup/lib/runner.sh"
export RUNNER_OS=linux
export RUNNER_ARCH=amd64
export RUNNER_NAME="$(hostname -s)"
export RUNNER_LABELS="self-hosted,linux,${RUNNER_NAME}"
runner_install_binary
runner_register
# systemd-user persistence (survives reboot thanks to `loginctl enable-linger`).
UNIT="$HOME/.config/systemd/user/forgejo-runner.service"
mkdir -p "$(dirname "$UNIT")"
cat > "$UNIT" <<UNITFILE
[Unit]
Description=Forgejo Actions runner (${RUNNER_NAME})
Documentation=https://code.forgejo.org/forgejo/runner
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=%h/.local/bin/forgejo-runner daemon --config %h/.local/share/forgejo-runner/config.yaml
WorkingDirectory=%h/.local/share/forgejo-runner
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=default.target
UNITFILE
systemctl --user daemon-reload
systemctl --user enable --now forgejo-runner.service
loginctl enable-linger "$USER" 2>/dev/null || true
echo " runner: systemd user unit enabled (linger=on, persists across reboot)"
runner_verify_online || true
fi