diff --git a/scripts/apricot-run.sh b/scripts/apricot-run.sh index d5823b04..6989c400 100755 --- a/scripts/apricot-run.sh +++ b/scripts/apricot-run.sh @@ -1,16 +1,19 @@ #!/usr/bin/env bash -# apricot-run.sh — Isolated build + batch pipeline on apricot. +# apricot-run.sh — Isolated build + batch pipeline on apricot, sourced from forge. # -# apricot is a multi-tenant RUN host. magic-civilization does NOT develop here — -# any persistent checkouts live under ~/Code/project-buildspace/ and are excluded -# from the autocommit daemon (commits.service). This script keeps even further -# clear and uses ~/.cache/ for the per-run scratch + results: +# Source-of-truth flow: forge → apricot canonical checkout → per-run worktree. +# Plum is NOT in this loop. Agents push their work via ACS (commits-tray on plum +# pushes every ~5 min), then this script builds the latest origin/main on apricot. # -# 1. Rsync this EDIT-host source tree to ~/.cache/mc-src-/ on apricot. -# 2. Build (cargo) in ~/.cache/mc-src-/, target dir stays there (ephemeral). -# 3. Run the batch with RESULTS_DIR under ~/.cache/mc-batches// +# 1. Fetch latest origin/main into the canonical checkout +# (~/Code/project-buildspace/magic-civilization, hard-fenced via pre-commit hook). +# 2. `git worktree add` a per-run scratch tree at ~/.cache/mc-src-/ +# (shared object pool with canonical, isolated working tree, fast + reproducible). +# 3. Build (cargo) in the worktree, target dir stays there (ephemeral). +# 4. Run the batch with RESULTS_DIR under ~/.cache/mc-batches// # (persistent, XDG cache convention, flatpak-visible via --filesystem=home). -# 4. Fetch verdict JSON back to EDIT host for review. +# 5. Fetch verdict JSON back to EDIT host for review. +# 6. Remove the worktree (canonical + objects retained for next run). # # Usage: # scripts/apricot-run.sh smoke [seeds=10] [turns=300] @@ -20,6 +23,8 @@ # Environment: # APRICOT_SSH_ALIAS — ssh alias for the RUN host (default: apricot). # STAMP — override the timestamp (for reproducing a specific run). +# BUILD_REF — git ref to build (default: origin/main). Lets you reproduce +# a prior run by SHA without changing your local plum tree. set -euo pipefail @@ -92,8 +97,9 @@ export RAYON_NUM_THREADS # Source + build scratch lives under $HOME/.cache (flatpak-visible via # --filesystem=home). /tmp was tried first but flatpak's sandbox can't see # /tmp, so Godot rejected the --path argument with "Invalid project path". -# $HOME/.cache/ still satisfies the apricot-isolation rule (not under ~/Code, -# not shared with other devs) and is convention-cleanable. +# $HOME/.cache/ also satisfies the apricot-isolation rule (not under ~/Code/@projects, +# not under project-buildspace where the canonical checkout lives) and is +# convention-cleanable. SCRATCH="\$HOME/.cache/mc-src-${STAMP}" # expanded on apricot RESULTS="\$HOME/.cache/mc-batches/${STAMP}" # expanded on apricot @@ -101,11 +107,19 @@ RESULTS="\$HOME/.cache/mc-batches/${STAMP}" # expanded on apricot SCRATCH_ABS="$(ssh "${APRICOT}" "echo \$HOME/.cache/mc-src-${STAMP}")" RESULTS_ABS="$(ssh "${APRICOT}" "echo \$HOME/.cache/mc-batches/${STAMP}")" +# Canonical checkout: persistent clone of magicciv from forge, lives in the +# autocommit-excluded buildspace dir. Worktrees branch off this for per-run +# scratch trees with shared object pool — fast + reproducible. +CANONICAL_ABS="$(ssh "${APRICOT}" "echo \$HOME/Code/project-buildspace/magic-civilization")" +BUILD_REF="${BUILD_REF:-origin/main}" + echo "============================================================" echo "apricot-run.sh mode=${MODE} stamp=${STAMP}" echo " EDIT host: $(hostname)" echo " RUN host: ${APRICOT}" -echo " SCRATCH: ${SCRATCH_ABS} (per-run source + build scratch)" +echo " CANONICAL: ${CANONICAL_ABS} (persistent clone of forge)" +echo " BUILD_REF: ${BUILD_REF}" +echo " SCRATCH: ${SCRATCH_ABS} (per-run worktree, ephemeral)" echo " RESULTS: ${RESULTS_ABS} (persistent batch output)" echo " PARALLEL: ${PARALLEL_EFFECTIVE} (source: ${PARALLEL_SOURCE})" echo " RAYON_NUM_THREADS/instance: ${RAYON_NUM_THREADS} (source: ${RAYON_SOURCE})" @@ -113,17 +127,24 @@ echo " Total CPU saturation: ${PARALLEL_EFFECTIVE} × ${RAYON_NUM_THREADS} = $( echo " AI_GPU_ROLLOUT: ${AI_GPU_ROLLOUT:-true (default on for smoke/clan)}" echo "============================================================" -# ── Step 1: rsync EDIT → SCRATCH ───────────────────────────────────────────── -echo "[$(date +%H:%M:%S)] rsync EDIT source → ${SCRATCH_ABS}..." -rsync -a --delete \ - --exclude='.git' \ - --exclude='.local/build' \ - --exclude='.local/iter' \ - --exclude='.local/batches' \ - --exclude='node_modules' \ - --exclude='target' \ - --exclude='*.dylib' \ - "${PROJECT_DIR}/" "${APRICOT}:${SCRATCH_ABS}/" +# ── Step 1: fetch forge → canonical → per-run worktree ─────────────────────── +# Source flow is forge → canonical → worktree. Plum is NOT in this path; agents +# push via ACS (commits-tray on plum pushes every ~5 min). To test a specific +# SHA, set BUILD_REF= in the environment. +echo "[$(date +%H:%M:%S)] fetch origin in canonical, then worktree ${BUILD_REF} → ${SCRATCH_ABS}..." +ssh "${APRICOT}" "set -euo pipefail; \ + test -d '${CANONICAL_ABS}/.git' || { \ + echo 'ERROR: canonical checkout missing at ${CANONICAL_ABS}' >&2; \ + echo 'one-time setup: git clone http://forge.black.local:3000/magicciv/magicciv.git ${CANONICAL_ABS}' >&2; \ + echo 'then: git -C ${CANONICAL_ABS} config core.hooksPath \$HOME/Code/project-buildspace/.hooks' >&2; \ + exit 1; \ + }; \ + git -C '${CANONICAL_ABS}' fetch origin --quiet; \ + git -C '${CANONICAL_ABS}' worktree add --detach '${SCRATCH_ABS}' '${BUILD_REF}'" + +# Resolve the actual SHA we built (BUILD_REF may be a branch name). +BUILT_SHA="$(ssh "${APRICOT}" "git -C '${SCRATCH_ABS}' rev-parse --short HEAD")" +echo " built SHA: ${BUILT_SHA}" # ── Step 2: build + deploy via build-gdext.sh ──────────────────────────────── # Canonical build script: runs `cargo build --release --target x86_64-unknown-linux-gnu` @@ -277,7 +298,14 @@ echo "[$(date +%H:%M:%S)] fetch verdict/summary to ${LOCAL_RESULTS}..." scp -r "${APRICOT}:${RESULTS_ABS}/" "${LOCAL_RESULTS}/" 2>/dev/null || \ echo "WARN: scp returned non-zero; check manually on ${APRICOT}:${RESULTS_ABS}" -# ── Step 6: prune old local copies — keep only the 3 most recent ───────────── +# ── Step 6: remove the per-run worktree ────────────────────────────────────── +# Canonical .git/ + objects retained for the next run. Working tree + cargo +# target dir are gone. RESULTS dir under ~/.cache/mc-batches/ is untouched. +echo "[$(date +%H:%M:%S)] remove worktree ${SCRATCH_ABS}..." +ssh "${APRICOT}" "git -C '${CANONICAL_ABS}' worktree remove --force '${SCRATCH_ABS}' 2>&1 || \ + rm -rf '${SCRATCH_ABS}'" + +# ── Step 7: prune old local copies — keep only the 3 most recent ───────────── ITER_ROOT="${PROJECT_DIR}/.local/iter" if [[ -d "${ITER_ROOT}" ]]; then # List apricot-* dirs newest-first, skip the first 3, delete the rest.