Files
horizon-airdef/tools/ci/vanilla-data.sh
T
Ludovic Marie 48c85fa19f Add tools/pr_ready.py, CONTRIBUTING.md and a pull request template
pr_ready is the one self-check to run before asking for review: from the
files a branch changes it runs rustfmt, clippy with warnings denied, the
tests of the touched crates (mapped through cargo metadata), the launcher's
own workspace when it is affected, the tokens.css check, the Cargo.lock
rule, the Python tests and the public-tree check, and the vanilla harnesses
the change can affect, picked by tools/parity.py exactly as CI's parity job
picks them. It runs locally or on a fleet machine through ~/fleet/rbuild;
on the Mac mini the harnesses use the parity runner's own workspace and
scripts. It prints one Markdown block for the pull request body and exits
non-zero on any failure.

tools/parity.py gains --changed-files for copies without Git history, and
tools/ci/vanilla-data.sh accepts the preparation key from outside.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:13:39 +02:00

93 lines
5.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# The vanilla data for this checkout on the self-hosted runner. Horizon never downloads the game: the runner
# holds the maintainer's own copy, imported and decompiled once (HORIZON_SHARED_SOURCE, default
# ~/code/horizon-parity/shared-local). It is cloned once into the runner's work folder ($RUNNER_WORKSPACE/
# shared-local; APFS clones, so no second copy of the bytes), and .local/ links to it as in a parity worktree.
# .local/horizon-client and the exporter outputs beside it, what horizon-prepare makes, are prepared by this
# checkout's own preparer once per preparer version (its sources, the exporters and research-lock.json), so a
# change to the preparer is checked with what it prepares; the four newest preparations are kept.
set -euo pipefail
shared="${RUNNER_WORKSPACE:?}/shared-local"
source="${HORIZON_SHARED_SOURCE:-$HOME/code/horizon-parity/shared-local}"
version=$(sed -n 's/.*"version": *"\([^"]*\)".*/\1/p' research-lock.json | head -1)
if [ ! -f "$shared/minecraft/$version/prepared.json" ]; then
[ -f "$source/minecraft/$version/prepared.json" ] || { echo "::error::no imported Minecraft $version in $source"; exit 1; }
mkdir -p "$shared"
if [ "$(uname)" = Darwin ]; then
for entry in "$source"/*; do
[ "$(basename "$entry")" = horizon-client ] || cp -cR "$entry" "$shared/"
done
else
for entry in "$source"/*; do
[ "$(basename "$entry")" = horizon-client ] || cp -R --reflink=auto "$entry" "$shared/"
done
fi
fi
mkdir -p .local "$shared/prepared"
for entry in "$shared"/*; do
name=$(basename "$entry")
case "$name" in prepared|sounds|*.log) continue ;; esac
if [ -L ".local/$name" ] || [ ! -e ".local/$name" ]; then ln -sfn "$entry" ".local/$name"; fi
done
# HORIZON_PREPARE_KEY: the same key, computed where the Git history is (tools/pr_ready.py on another machine).
key=${HORIZON_PREPARE_KEY:-$(git ls-files -s crates/horizon-prepare mods/horizon/src/test/java research-lock.json | shasum -a 256 | cut -c1-16)}
prepared="$shared/prepared/$key"
# Two runners share the vanilla data (~/.actions-runners/horizon-2/_work/horizon/shared-local links to the first
# one's): one preparation at a time. A lock older than 30 minutes belongs to a job that died.
lock="$shared/prepared/.lock"
until mkdir "$lock" 2>/dev/null; do
if [ -n "$(find "$lock" -maxdepth 0 -mmin +30 2>/dev/null)" ]; then rmdir "$lock" 2>/dev/null || true; fi
sleep 5
done
trap 'rmdir "$lock" 2>/dev/null || true' EXIT
# horizon-prepare writes .local/horizon-client and its exporters' outputs beside it: all of them live in
# $prepared, listed in $prepared/.entries, and .local/ links to them.
if [ ! -f "$prepared/.entries" ]; then
echo "Preparing resources with this checkout's horizon-prepare ($key)"
rm -rf "$prepared" && mkdir -p "$prepared"
# Links to another preparation's outputs go first, so what this one writes shows up as new entries.
for entry in .local/*; do
if [ -L "$entry" ] && [[ "$(readlink "$entry")" == "$shared/prepared/"* ]]; then rm "$entry"; fi
done
before=$(ls -1A .local | LC_ALL=C sort)
started=$SECONDS
cargo build --release --locked -p horizon-prepare
if ! target/release/horizon-prepare --root "$PWD" > "$RUNNER_TEMP/prepare.log" 2>&1; then
tail -40 "$RUNNER_TEMP/prepare.log"
rm -rf "$prepared"
exit 1
fi
LC_ALL=C comm -13 <(echo "$before") <(ls -1A .local | LC_ALL=C sort) > "$RUNNER_TEMP/prepared-entries"
while read -r name; do mv ".local/$name" "$prepared/$name"; done < "$RUNNER_TEMP/prepared-entries"
mv "$RUNNER_TEMP/prepared-entries" "$prepared/.entries"
echo "Prepared in $((SECONDS - started)) s: $(tr '\n' ' ' < "$prepared/.entries")"
fi
# The game finds some data relative to its resources (<resources>/../../datagen): beside the prepared folders,
# the same neighbours as in the shared folder.
for entry in "$shared"/*; do
name=$(basename "$entry")
case "$name" in prepared|sounds|*.log) continue ;; esac
[ -e "$prepared/$name" ] || ln -sfn "$entry" "$prepared/$name"
done
while read -r name; do
if [ -L ".local/$name" ] || [ ! -e ".local/$name" ]; then ln -sfn "$prepared/$name" ".local/$name"; else
echo "::error::.local/$name is not a link to the prepared resources"; exit 1; fi
done < "$prepared/.entries"
# The game's sounds (horizon-prepare sounds copies them from the official launcher's asset store) depend only on the
# Minecraft version: kept once in $shared/sounds/<version>/ (sounds.json and sounds/) and copied into each preparation
# as clones (APFS, or reflinks on Linux: no second copy of the bytes). Not links: vanilla's resource packs refuse
# symbolic links inside a pack, so its sound oracles would find no sound files.
assets="$prepared/horizon-client/$version/assets/minecraft"
if [ -f "$shared/sounds/$version/sounds.json" ] && [ -d "$assets" ]; then
for entry in sounds.json sounds; do
[ -L "$assets/$entry" ] && rm "$assets/$entry"
[ -e "$assets/$entry" ] && continue
if [ "$(uname)" = Darwin ]; then cp -cR "$shared/sounds/$version/$entry" "$assets/"
else cp -R --reflink=auto "$shared/sounds/$version/$entry" "$assets/"; fi
done
fi
touch "$prepared"
# The four newest preparations stay.
ls -1dt "$shared/prepared"/*/ 2>/dev/null | tail -n +5 | while read -r old; do rm -rf "$old"; done