#!/usr/bin/env bash # Build the match runner: the container image, then sds.jar inside it. # # ./scripts/build.sh # # MM_HOME extracted MegaMek release (default: ~/.cache/mul-build/megamek) # SDS_IMAGE image to build (default: lance-blue/sds-runner:df, from # the Dockerfile's own contents - see below) # SDS_REBUILD set to 1 to rebuild an image that already exists # # The tag is derived from the Dockerfile rather than being `:latest`, and an # existing tag is never rebuilt over. Both halves are load-bearing, and the # reason is that several agents share this machine's docker daemon: # # - `:latest` is one mutable name. A rebuild by anybody replaces the image every # other run is using, between one match and the next, and nothing fails # loudly. It had already happened here: `:latest` and a fresh build of this # Dockerfile were two different image IDs. # - A content tag alone is not enough. Two checkouts of the same commit derive # the same tag, so a rebuild in one would still overwrite the other - unless # the build is skipped when the tag is already present, which it now is. # # The tag says "built from this Dockerfile", not "bit-identical forever": the # apt layer resolves whatever versions the archive holds that day. What it # guarantees is that a run's image cannot change under it, and that a recorded # image tag names something reproducible from a known Dockerfile. # # MegaMek is a mount, not a layer: the image is a JDK and a Python and stays # valid across MegaMek versions, and none of MegaMek's CC BY-NC-SA data ends up # in an image this repository could publish. set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" MM_HOME="${MM_HOME:-$HOME/.cache/mul-build/megamek}" DOCKERFILE_HASH="$(sha256sum "$HERE/bridge/Dockerfile" | cut -c1-12)" SDS_IMAGE="${SDS_IMAGE:-lance-blue/sds-runner:df$DOCKERFILE_HASH}" [ -f "$MM_HOME/MegaMek.jar" ] || { echo "ERROR: no MegaMek.jar under $MM_HOME" >&2 echo "Point MM_HOME at an extracted MegaMek release." >&2 exit 1; } MM_HOME="$(cd "$MM_HOME" && pwd)" if [ "${SDS_REBUILD:-0}" != "1" ] && docker image inspect "$SDS_IMAGE" >/dev/null 2>&1; then echo "==> image $SDS_IMAGE (already built; SDS_REBUILD=1 to force)" else echo "==> image $SDS_IMAGE" docker build -q -t "$SDS_IMAGE" -f "$HERE/bridge/Dockerfile" "$HERE/bridge" fi # `:latest` is deliberately NOT moved here. It is the mutable shared name this # whole change exists to stop runs depending on, and re-pointing it from a build # script is the same hazard wearing a different hat: a bench started before the # change still resolves the bare name, and moving it swaps that run's # environment between one match and the next. Tag it by hand if a human wants # the shorthand. echo "==> compiling bridge/sds/*.java against $MM_HOME" mkdir -p "$HERE/bridge/build" docker run --rm \ --user "$(id -u):$(id -g)" \ -v "$HERE:/work" \ -v "$MM_HOME:/mm:ro" \ -w /work \ "$SDS_IMAGE" \ bash -c ' set -euo pipefail CP="/mm/MegaMek.jar" for jar in /mm/lib/*.jar; do CP="$CP:$jar"; done rm -rf bridge/build/classes mkdir -p bridge/build/classes javac -nowarn -Xlint:-options -classpath "$CP" -d bridge/build/classes bridge/sds/*.java jar --create --file bridge/build/sds.jar -C bridge/build/classes sds ' echo "built -> bridge/build/sds.jar" # The bot. Built on the host and mounted into the match container, not baked # into the image: the image is a JDK and a Python and should not need rebuilding # every time a weight changes. # # -j 2 throughout. A full-parallel build has taken this machine down before, and # a match container is usually running while this is. if command -v cargo >/dev/null 2>&1; then echo "==> cargo build --release -j 2" ( cd "$HERE" && cargo build --release -j 2 ) echo "built -> target/release/sds-bot" else echo "note: no cargo on PATH, skipping the Rust bot." >&2 echo " export PATH=\"\$HOME/.cargo/bin:\$PATH\"" >&2 fi