#!/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" </dev/null || true echo " runner: systemd user unit enabled (linger=on, persists across reboot)" runner_verify_online || true fi