From c26938c4c5e9f03fdb0243b7ab2c7bb463f09ebd Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 31 Jul 2026 13:01:13 -0400 Subject: [PATCH] Make a fresh checkout self-sufficient with bootstrap.sh The repo committed none of its third-party dependencies, but the script to fetch them only covered two of the four. A clean clone could not build: the MegaMek *source* tarball (which the patchset applies against) and Webswing Lite itself were both fetched by hand during the spike and never scripted. Several steps after that were also undocumented tribal knowledge. fetch-deps.sh now pulls all four - JDK 21 (checksum-verified), the MegaMek release, the MegaMek source, and Webswing Lite - with clearer progress output and a note that this is the AGPL fork, not commercial Webswing. It also drops the AGPL text into webswing-dist, which ships without a LICENSE file of its own. bootstrap.sh is the new single entry point: fetch, build the harness, apply the patchset into MegaMek-patched.jar, generate the fontconfig, and prime units.cache so the first launch is not 17s slower. Every step is idempotent. start.sh no longer derives its jetty properties from the shipped webswing-dist/jetty.properties. That file defaults to https=true pointing at an ssl/keystore.jks the distribution does not contain, so a fresh extract failed to start - it only worked here because the file had been hand-edited months of commits ago, and that edit was gitignored. The properties are now generated outright. Verified by restoring the pristine jetty.properties from the zip and starting an instance: HTTP 200, both ports up. Bootstrap re-run from the existing tree applies all four patches and rebuilds cleanly. README gains a Quickstart, the host requirements (notably fontconfig, whose absence bootstrap only warns about), and a corrected patch count. Co-Authored-By: Claude Opus 5 --- README.md | 28 ++++++++++++----- scripts/bootstrap.sh | 64 +++++++++++++++++++++++++++++++++++++ scripts/fetch-deps.sh | 70 +++++++++++++++++++++++++++++++++++------ spike-webswing/start.sh | 12 +++++-- 4 files changed, 156 insertions(+), 18 deletions(-) create mode 100755 scripts/bootstrap.sh diff --git a/README.md b/README.md index 9872fe6..b22acc7 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,13 @@ the stock MegaMek Swing client. See [TODO.md](TODO.md) for what's next and ## Constraint **Minimal, upstreamable patches.** Everything builds against an unmodified -release jar, the harness uses only public API, and the two patches that exist are -latent MegaMek bugs rather than adaptations. A new MegaMek release should be a +release jar, the harness uses only public API, and all four patches that exist +are latent MegaMek bugs rather than adaptations. A new MegaMek release should be a recompile plus at most a patch refresh, never a rebase. -## Running the browser spike +## Quickstart + ./scripts/bootstrap.sh # ~1.1GB of downloads, then builds everything ./spike-webswing/start.sh # blocks; Ctrl-C tears everything down Open . You play TraineeA, Princess plays @@ -55,6 +56,19 @@ out and Player Settings throws an NPE. Webswing Lite session settings do not hel `maxClients: 1` refuses the second browser, and `CONTINUE_FOR_USER` + `allowStealSession` lets it evict the first and locks out both. +### What bootstrap does + +Nothing third-party is committed — the repo is ~40 files. `bootstrap.sh` fetches +the pinned JDK 21, the MegaMek 0.51.0 release *and* source tarball, and Webswing +Lite 26.4.5; then builds the harness, applies the patchset into +`MegaMek-patched.jar`, generates the fontconfig, and primes MegaMek's +`units.cache`. It is idempotent, so re-run it freely. + +Host requirements: `curl`, `tar`, `unzip`, `sha256sum`, and **`fontconfig`** +(`fc-match`). Without fontconfig the client dies at startup with +"Fontconfig head is null" — bootstrap warns rather than failing, so the message +is easy to miss. + ## Patches Unified diffs in `patches/patchset/`, applied `-p1` against a pristine tree — @@ -111,15 +125,15 @@ Applied, and verified to be the only differences from the stock jar: the adapter-pattern prototype) - `spike-webswing/` — Webswing Lite + MegaMek settings templates, `start.sh` - `patches/` — patchset, archive, patched-jar builder -- `scripts/` — `fetch-deps.sh`, `gen-fontconfig.sh` (legacy fontconfig Webswing Lite's - toolkit needs), `cdp-screenshot.py`, `summarize.py` +- `scripts/` — `bootstrap.sh` (one-shot setup), `fetch-deps.sh`, + `gen-fontconfig.sh` (legacy fontconfig Webswing Lite's toolkit needs), + `cdp-screenshot.py`, `summarize.py` - `results/` — bench CSVs and jstacks kept as evidence for Findings ## Benchmarks ```sh -./scripts/fetch-deps.sh -./harness/build.sh +./scripts/bootstrap.sh ./harness/run.sh --games 8 --settle-round 3 --csv results/out.csv python3 scripts/summarize.py results/out.csv ``` diff --git a/scripts/bootstrap.sh b/scripts/bootstrap.sh new file mode 100755 index 0000000..7951c13 --- /dev/null +++ b/scripts/bootstrap.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Take a fresh checkout to the point where you can play a match. +# +# ./scripts/bootstrap.sh +# ./spike-webswing/start.sh # then open http://localhost:8080/megamek/ +# +# Steps, all idempotent: +# 1. fetch third-party deps (JDK, MegaMek release + source, Webswing Lite) +# 2. compile the harness against the stock MegaMek jar +# 3. build MegaMek-patched.jar by applying patches/patchset/ +# 4. generate the legacy fontconfig Webswing Lite's toolkit requires +# 5. prime MegaMek's units.cache so the first launch is not 17s slower +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$ROOT" + +step() { printf '\n=== %s ===\n' "$1"; } + +step "1/5 third-party dependencies" +./scripts/fetch-deps.sh + +step "2/5 harness" +./harness/build.sh + +step "3/5 patched MegaMek jar" +./patches/build-patched-jar.sh + +step "4/5 fontconfig" +# Needs fontconfig tooling on the host; MegaMek's symbol glyphs depend on it. +if command -v fc-match >/dev/null; then + ./scripts/gen-fontconfig.sh +else + echo "SKIPPED: fc-match not found. Install fontconfig, then run" >&2 + echo " ./scripts/gen-fontconfig.sh" >&2 + echo "Without it the MegaMek client dies at startup with 'Fontconfig head is null'." >&2 +fi + +step "5/5 prime units.cache" +# MegaMek builds this by walking ~11k unit files on first run - 20s instead of 3s. +# Doing it here keeps it out of the first match. +MM="$ROOT/MegaMek-0.51.00" +if [ -f "$MM/data/mekfiles/units.cache" ]; then + echo " already present" +else + echo " building (this takes ~20s) ..." + ( cd "$MM" && "$ROOT/jdk-21.0.12+8/bin/java" \ + -Dlog4j2.configurationFile="$ROOT/harness/log4j2-quiet.xml" \ + --add-opens java.base/java.util=ALL-UNNAMED \ + --add-opens java.base/java.util.concurrent=ALL-UNNAMED \ + -cp "$ROOT/harness/out:$ROOT/MegaMek-patched.jar:$(ls "$MM"/lib/*.jar | tr '\n' ':')" \ + bench.IsolationCheck >/dev/null 2>&1 ) || true + [ -f "$MM/data/mekfiles/units.cache" ] && echo " built" || echo " (not built; first launch will be slower)" +fi + +cat <<'DONE' + +=== ready === + + ./spike-webswing/start.sh then open http://localhost:8080/megamek/ + ./spike-webswing/stop.sh stop it (never pkill - see README) + + You play TraineeA, Princess plays TraineeB. Press "Done" in the lobby. +DONE diff --git a/scripts/fetch-deps.sh b/scripts/fetch-deps.sh index c57a8f4..e191c49 100755 --- a/scripts/fetch-deps.sh +++ b/scripts/fetch-deps.sh @@ -1,20 +1,72 @@ #!/usr/bin/env bash -# Fetch the pinned JDK and MegaMek release. Both are gitignored; this makes the -# repo reproducible without committing ~700MB of third-party artifacts. +# Fetch every third-party artifact the spike needs. All of them are gitignored, +# so the repo stays small and this script is what makes a fresh checkout usable. +# +# Idempotent: already-downloaded archives and already-extracted trees are skipped, +# so it is safe to re-run. +# +# ./scripts/fetch-deps.sh +# +# Downloads (~1.1GB total, mostly MegaMek's data directory): +# Temurin JDK 21 MegaMek needs Java 21 exactly; checksum-verified +# MegaMek release the jar plus data/ (units, boards, images, fonts) +# MegaMek source needed by patches/build-patched-jar.sh to apply the patchset +# Webswing Lite AGPL-3.0 fork by manticore-projects - NOT commercial Webswing set -euo pipefail + ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT" mkdir -p dl JDK_URL="https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.12%2B8/OpenJDK21U-jdk_x64_linux_hotspot_21.0.12_8.tar.gz" JDK_SHA="e4446ff06a276155697597cc0f1b15da004ff083f4964a35271ecee567177370" -MM_URL="https://github.com/MegaMek/megamek/releases/download/v0.51.0/MegaMek-0.51.0.tar.gz" +JDK_DIR="jdk-21.0.12+8" + +MM_VERSION="0.51.0" +MM_URL="https://github.com/MegaMek/megamek/releases/download/v${MM_VERSION}/MegaMek-${MM_VERSION}.tar.gz" +MM_SRC_URL="https://github.com/MegaMek/megamek/archive/refs/tags/v${MM_VERSION}.tar.gz" +MM_DIR="MegaMek-0.51.00" # note: the release tree has a trailing 0 +MM_SRC_DIR="megamek-${MM_VERSION}" + +WS_VERSION="26.4.5" +WS_URL="https://github.com/manticore-projects/webswing/releases/download/${WS_VERSION}/webswing-${WS_VERSION}.zip" +WS_DIR="webswing-dist" + +need() { command -v "$1" >/dev/null || { echo "ERROR: '$1' not found" >&2; exit 1; }; } +need curl; need tar; need unzip; need sha256sum + +get() { # url dest + [ -f "$2" ] && { echo " have $(basename "$2")"; return 0; } + echo " downloading $(basename "$2") ..." + curl -fsSL -o "$2" "$1" +} + +echo "JDK 21 (Temurin)" +get "$JDK_URL" dl/jdk21.tar.gz +echo "${JDK_SHA} dl/jdk21.tar.gz" | sha256sum -c - >/dev/null || { + echo "ERROR: JDK checksum mismatch - delete dl/jdk21.tar.gz and retry" >&2; exit 1; } +[ -d "$JDK_DIR" ] || tar xzf dl/jdk21.tar.gz +echo " -> $JDK_DIR" + +echo "MegaMek $MM_VERSION (release)" +get "$MM_URL" "dl/MegaMek-${MM_VERSION}.tar.gz" +[ -d "$MM_DIR" ] || tar xzf "dl/MegaMek-${MM_VERSION}.tar.gz" +echo " -> $MM_DIR" -[ -f dl/jdk21.tar.gz ] || curl -sSL -o dl/jdk21.tar.gz "$JDK_URL" -echo "${JDK_SHA} dl/jdk21.tar.gz" | sha256sum -c - -[ -d jdk-21.0.12+8 ] || tar xzf dl/jdk21.tar.gz +echo "MegaMek $MM_VERSION (source, needed to apply the patchset)" +get "$MM_SRC_URL" "dl/megamek-src-${MM_VERSION}.tar.gz" +[ -d "$MM_SRC_DIR" ] || tar xzf "dl/megamek-src-${MM_VERSION}.tar.gz" +echo " -> $MM_SRC_DIR" -[ -f dl/MegaMek-0.51.0.tar.gz ] || curl -sSL -o dl/MegaMek-0.51.0.tar.gz "$MM_URL" -[ -d MegaMek-0.51.00 ] || tar xzf dl/MegaMek-0.51.0.tar.gz +echo "Webswing Lite $WS_VERSION (AGPL-3.0 fork; not commercial Webswing)" +get "$WS_URL" "dl/webswing-${WS_VERSION}.zip" +[ -d "$WS_DIR" ] || unzip -q "dl/webswing-${WS_VERSION}.zip" -d "$WS_DIR" +# The distribution ships no LICENSE file of its own; add the AGPL text so the +# extracted tree states its terms. See GUIDELINES.md. +[ -f "$WS_DIR/LICENSE.txt" ] || \ + curl -fsSL -o "$WS_DIR/LICENSE.txt" "https://www.gnu.org/licenses/agpl-3.0.txt" || \ + echo " (warning: could not fetch AGPL text for $WS_DIR/LICENSE.txt)" +echo " -> $WS_DIR" -echo "deps ready" +echo +echo "deps ready. Next: ./scripts/bootstrap.sh" diff --git a/spike-webswing/start.sh b/spike-webswing/start.sh index 50e96db..bff094e 100755 --- a/spike-webswing/start.sh +++ b/spike-webswing/start.sh @@ -62,8 +62,16 @@ fi # Materialise the Webswing config with absolute paths for this checkout. sed -e "s|@ROOT@|$ROOT|g" -e "s|@PORT_MM@|$PORT_MM|g" \ "$ROOT/spike-webswing/webswing.config.template" > "$WS_CONFIG" -sed "s|^org.webswing.server.http.port=.*|org.webswing.server.http.port=$PORT_WS|" \ - "$WS/jetty.properties" > "$WS_JETTY" +# Written from scratch rather than derived from the shipped jetty.properties: +# that file defaults to https=true pointing at an ssl/keystore.jks the +# distribution does not contain, so a fresh extract fails to start. Generating it +# means a clean checkout works with no hand-editing of webswing-dist. +cat > "$WS_JETTY" <