magicciv/infra/packer/provision.sh
Natalie 158ef4d1bd feat(@projects/@magic-civilization): 🩹 p3-29 T2 — Rust turn emits UnitHealed
The live GDScript turn emitted `unit_healed` inline; the headless healing
phase recovered HP silently. The healing phase runs in the end-of-turn
`fn(&mut GameState)` registry (no event sink), so follow the FloraSuccession
buffer pattern: stash `(player, unit_id, applied_amount, col, row)` into a new
transient `GameState.pending_heal_events`, drain it in `step()` into
`TurnEvent::UnitHealed`. The buffered amount is the CLAMPED delta actually
applied (not the nominal heal rate). No wire surface — dispatch drops it; the
live UI consumes it via the kind-tagged `event_to_dict` dict.

Verified headless: mc-replay 19/0 (unit_healed_serde), mc-turn 289/0
(healing_buffers_unit_heal_event_with_applied_amount +
healing_buffers_clamped_amount_near_full_hp + event_collector_wiring).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 06:12:07 -04:00

69 lines
3.4 KiB
Bash
Executable file

#!/usr/bin/env bash
# Golden-image provisioner. Runs as root on a fresh Ubuntu 24.04 box during
# `packer build`. Produces a build-ready image for the distributed test fleet:
# - a build user (flatpak runs --user as this account; root flatpak is unsupported)
# - the repo cloned at $HOME/Code/@projects/@magic-civilization (run_ap3.sh hard-codes this path)
# - the full toolchain via the repo's own scripts/dev-setup/linux.sh (DRY)
# - a prebuilt GDExtension .so (remote autoplay-batch skips the rebuild)
# - a warm Godot import cache + warm cargo registry
#
# Env (injected by Packer): GIT_REMOTE (required), GIT_REF, BUILD_USER.
set -euo pipefail
GIT_REMOTE="${GIT_REMOTE:?GIT_REMOTE must be set}"
GIT_REF="${GIT_REF:-main}"
BUILD_USER="${BUILD_USER:-mc}"
REPO_PATH="Code/@projects/@magic-civilization" # relative to the build user's HOME
echo "=== [1/7] base packages ==="
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y --no-install-recommends \
git curl ca-certificates build-essential pkg-config libssl-dev \
unzip sudo python3-pip flatpak rsync
echo "=== [2/7] build user '$BUILD_USER' ==="
if ! id "$BUILD_USER" >/dev/null 2>&1; then
useradd --create-home --shell /bin/bash "$BUILD_USER"
fi
BUILD_UID="$(id -u "$BUILD_USER")"
# Enable lingering so /run/user/$UID (and the user D-Bus flatpak needs for
# headless --import) exists without an interactive login.
loginctl enable-linger "$BUILD_USER" || true
# Helper: run a command as the build user in a login shell with the user
# runtime dir wired up (matches autoplay-batch.sh's XDG_RUNTIME_DIR handling).
as_user() {
sudo -u "$BUILD_USER" -H \
env "XDG_RUNTIME_DIR=/run/user/${BUILD_UID}" \
bash -lc "$1"
}
echo "=== [3/7] clone repo @ \$HOME/$REPO_PATH (ref $GIT_REF) ==="
as_user "mkdir -p ~/$(dirname "$REPO_PATH")"
as_user "git clone '$GIT_REMOTE' ~/$REPO_PATH"
as_user "cd ~/$REPO_PATH && git checkout -f '$GIT_REF'"
echo "=== [4/7] toolchain via scripts/dev-setup/linux.sh ==="
# WITH_RUNNER must be defined: linux.sh references it unguarded under set -u and
# we use GitLab CI, not a forgejo runner, so keep it false.
as_user "cd ~/$REPO_PATH && WITH_RUNNER=false bash scripts/dev-setup/linux.sh"
echo "=== [5/7] python RL deps ==="
as_user "pip3 install --user --break-system-packages -r ~/$REPO_PATH/tooling/rl_self_play/requirements.txt || pip3 install --user -r ~/$REPO_PATH/tooling/rl_self_play/requirements.txt"
echo "=== [6/7] prebuild GDExtension + warm cargo registry ==="
# Remote autoplay-batch mode does NOT rebuild the .so (tools/autoplay-batch.sh:144),
# so the golden image must ship a fresh one.
as_user "cd ~/$REPO_PATH/src/simulator && source ~/.cargo/env && cargo fetch && bash build-gdext.sh"
echo "=== [7/7] place run_ap3.sh in ~/bin + warm Godot import cache ==="
# autoplay-batch.sh expects the runner at \$HOME/bin/run_ap3.sh (tools/autoplay-batch.sh:372).
as_user "mkdir -p ~/bin && cp ~/$REPO_PATH/scripts/autoplay/run_ap3.sh ~/bin/run_ap3.sh && chmod +x ~/bin/run_ap3.sh"
# First-import gotcha: a fresh checkout needs one --headless --import to build
# .godot/*.cfg or GDExtension classes resolve as 'not declared'. Non-fatal if it
# flakes here — the first real run rebuilds it — but baking it makes boots clean.
as_user "cd ~/$REPO_PATH && flatpak run --user org.godotengine.Godot --path src/game --headless --import" || \
echo "WARN: headless --import did not complete cleanly — validate in the live smoke"
echo "=== golden image provisioned OK ==="