Commit Graph

33 Commits

Author SHA1 Message Date
Ludovic Marie 3755bb7d77 parity: stop harnesses on SIGTERM, and reap orphaned harness JVMs
Nine vanilla Java servers from harness runs had been orphaned on the Mac mini for 2 to 5 days. Each harness runs in
its own session (killpg on a timeout), so when a CI job was cancelled, its harnesses and their servers outlived
parity.py.
- parity.py: SIGTERM and SIGHUP exit through the finally blocks, which now stop the local executor's harnesses
  too, not only the remote hosts'. The worker (--worker) stops its harnesses the same way.
- runner-gc.sh (every 6 h from launchd): stops Java processes whose parent is gone (launchd, init or
  systemd --user) and whose command line names a runner's checkout or a worktree's .local/, once ORPHAN_HOURS
  (default 3) old.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 21:10:41 +01:00
Ludovic Marie edd7f712b6 Merge pull request #2293 from ludovic111/ci/thinkpad-off
Tools: the ThinkPad no longer runs harnesses or builds; playhorizon is the Linux host
2026-10-26 18:35:33 +01:00
Ludovic Marie e0423bba77 Merge remote-tracking branch 'origin/main' into ci/launcher-check
# Conflicts:
#	tools/ci/rust_changes.py
2026-10-26 17:15:21 +01:00
Ludovic Marie 9f1784dc0c rust_changes: append the repository root to sys.path instead of prepending tools/
Prepending tools/ made unittest discovery import tools/test_decoration_parity.py
in place of tests/test_decoration_parity.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 17:14:46 +01:00
Ludovic Marie 0bd6a576a9 Tools: the ThinkPad no longer runs harnesses or builds; playhorizon is the Linux host
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 17:12:10 +01:00
Ludovic Marie 4fe648b674 Move the website to its own repository (ludovic111/horizon-site)
playhorizon.dev now deploys from the private ludovic111/horizon-site, which holds website/'s history. The files
its build still reads from here move out of website/:

- the design tokens to crates/horizon-theme/tokens.css (xtask tokens writes them, tokens_css_is_up_to_date checks them);
- the roadmap's JSON to roadmap/roadmap.json (tools/roadmap.py build);
- the benchmark fixtures to crates/horizon-telemetry-server/tests/fixtures/benchmarks/ (the site keeps a copy);
- the web icons to docs/media/brand/web/ (xtask icons).

pr_ready loses its website area and the tokens special case; rust_changes, the roadmap's links and every doc
that pointed into website/ follow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 16:43:32 +01:00
Ludovic Marie dbbe3a45d9 CI: check the launcher's workspace on every push to main and on pull requests that touch it
verify's cargo runs never build launcher/ (its own workspace), so #2059 and the
registry ratings commit each passed alone and left main's launcher failing to
compile (E0061). The new launcher job runs its rustfmt, clippy -D warnings and lib
tests when a pull request changes the launcher, a crate it builds or the root
manifests, and on every push to main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 16:29:09 +01:00
Ludovic Marie a4839d322b CI: Linux runner on Ubuntu (playhorizon), tokens without gh, CPU and memory knobs
linux-runner.sh takes its packages from apt where there is no Nix, the
registration token from RUNNER_TOKEN (a machine where gh must not be logged
in), and RUNNER_CPUS / RUNNER_MEMORY for a machine that does nothing else.
playhorizon, a partner's 8-thread server, joins parity_hosts.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 15:28:21 +01:00
Ludovic Marie 5335354658 CI: runners clone what the shared source gained after their first copy
The release runner, now parity-main's, cloned the shared source before
the sounds were added to it; vanilla-data.sh only copied on the first
run, so ambient_sound, audio_attributes and music failed there with
"Sound assets missing". Each run now clones any entry the runner's copy
lacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 05:10:19 +01:00
Ludovic Marie 559ea59fcb Merge pull request #1952 from ludovic111/ci/linux-runner
🤖 Agent: ready. Linux runner support (runner-env/vanilla-data Linux paths, linux-runner.sh, runner_pause.py with Ludovic's lock/idle-15-min rule, heat, game, GPU and pause-file checks), desktop parity host, nightly and parity-main pinned to the Mac. verify and parity passed at this head. The desktop runner stays without the horizon-game label until its trial is identical: that is a runner setting, not in this PR. Desktop full run: 342/343 in 39.3 min (the 1 is a harness timing flake, fixed by #1997).
2026-10-26 02:36:59 +01:00
Ludovic Marie 160c1e8b7a CI: a failed preparation leaves nothing in .local
When horizon-prepare failed, vanilla-data.sh deleted the preparation folder but kept what it had
already written to .local/ (horizon-client, horizon-client-motion, ...). The next job then saw those
real folders as already there: its preparation listed only horizon-client-structures in .entries,
and the checkout kept using the stale resources outside the per-key cache.

Now a failed preparation removes the .local/ entries it created; a job first removes real folders
named like preparation outputs, and makes again a preparation whose .entries lacks horizon-client.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 01:54:02 +01:00
Ludovic Marie 49bbdf8954 CI: the desktop's CI slice gets half the threads
On all 24, a cargo build held ludodesktop's CPU at 92 °C and the watcher kept pausing; at half,
harnesses run at a median of 80 °C.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 01:38:23 +01:00
Ludovic Marie 9db6a0ee48 CI: parity-main stays on the Mac; heat check on a median, boot as the first sign; nix roots fixed
nix-build names a dev output's GC root <name>-dev, so the runner's PKG_CONFIG_PATH pointed at links
that didn't exist and every client build failed on libudev; it now uses the store path nix-build
prints. The package temperature spikes for a second under load, so the heat check takes the median
of 5 readings. Restarting the watcher no longer throttles a running job.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 01:23:27 +01:00
Ludovic Marie 0baf6cbd62 CI: the desktop runner works while its screen is locked or untouched for 15 min
Ludovic's rule (2026-10-26): runner_pause.py pauses at once on keyboard, mouse or gamepad input
(read from /dev/input, only whether bytes came), on an unlocked session touched in the last
15 min, at 90 °C, on a game or the GPU. Fixes the service start: the link loop's last test
failed the script under set -e.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 00:27:22 +01:00
Ludovic Marie d81aa97e84 CI: the desktop runner works only while nobody is at the screen; desktop as a parity host
runner_pause.py pauses on any seat0 session (locked too), games, GPU use or a pause file, and is
the only thing that starts the runner; a job under way is throttled, then the runner stops. The
desktop host in parity_hosts.json refuses work while paused and its workers stop on a pause. The
nightly audit stays on the Mac mini. Docs and a unit test of the watcher.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 23:12:09 +01:00
Ludovic Marie 8463325648 CI: Linux runners for the parity job (desktop, ThinkPad), paused while a game runs
runner-env.sh and vanilla-data.sh find Python 3.13, the pinned JDK and sccache on NixOS;
linux-runner.sh installs a runner as a systemd user service with its own screen; runner-pause.py
stops it from taking jobs while a game runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 23:12:09 +01:00
Ludovic Marie e66c35b2ef Merge pull request #390 from ludovic111/feat/version-port-agent
🤖 Agent: merged (version-port tooling, rebased on main). Ludovic asked on 10-21 to clear the PR queue.
2026-10-21 18:53:08 +02:00
Ludovic Marie d8eafd7dc0 version-port-daily.sh: no fleet-load priority change (it lowered the calling session); launchd's Nice and LowPriorityIO do it
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:22:21 +02:00
Ludovic Marie 94b5d2dbc4 tools/ci: the daily version-port agent (detect, port, harnesses, report, publish dry run), not installed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:22:21 +02:00
Ludovic Marie 846f681b9a Signed updates: releases from before signing stay installable with pinned files; tests need a release key
- crates/horizon-release-sign/legacy-releases.txt pins the SHA-256 of every archive of the 131 releases published
  before signing (GitHub's upload digests, written by tools/ci/legacy_releases.py). The launcher and
  horizon-server update install those releases only with exactly those files, so rolling back to one works and
  nobody can swap its files later; every other release needs its signed SHA256SUMS.
- the_launcher_ships_with_a_release_key and horizon_server_ships_with_a_release_key fail while
  release-keys.txt lists no key, so this can't ship refusing everything.
- Test tags moved to v2099.* so they never collide with a pinned release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:20:36 +02:00
Ludovic Marie 19220000be CODEOWNERS: the launcher's downloader, links.json and the Cargo.toml files; Ludovic alone may bypass the review
- backend.rs downloads, installs and self-updates; links.json says where from; launcher/Cargo.toml and
  crates/horizon-server/Cargo.toml pick the crates that check downloads.
- protect_pipeline.sh: ludovic111 is the only owner and can't approve his own pull requests, so he is the one
  bypass_pull_request_allowances user. enforce_admins stays on, so verify is still required for everyone.
  --apply reads the protection back and fails if GitHub didn't keep it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 15:42:17 +02:00
Ludovic Marie 1c341d58a6 CODEOWNERS for the release pipeline, and the protection that makes it count
.github/, tools/ci/, the release tools, the signing crate and the code that checks updates are
Ludovic's to review. tools/ci/protect_pipeline.sh prints (and with --apply sets) main's protection
with code owners' reviews required and no other approvals, keeping the required verify check;
it is for the orchestrator to apply once agents work as the GitHub App.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 15:42:09 +02:00
Ludovic Marie d38c07c2fa Releases: sign in a job of its own that runs only sha256sum and OpenSSL
Any repository code running earlier in the job that holds the key could reach it (a build patched
through Cargo.toml, or a step writing $GITHUB_ENV or $GITHUB_PATH for the next one). The `sign`
job checks out only tools/ci/sign_release.sh, which signs with the runner's OpenSSL; the publish
job, without the key, checks the result with horizon-release-sign as launchers will.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:18:54 +02:00
Ludovic Marie f32a91d793 Releases: signed checksums (SHA256SUMS, SHA256SUMS.sig) for game and launcher releases
crates/horizon-release-sign signs a release's checksums with an Ed25519 key that exists only as a
secret of the `release` environment, and checks them as launchers will, against the public keys
built in from release-keys.txt. Dry runs sign with a throwaway key. Until release-keys.txt lists a
key, releases keep going out with SHA256SUMS alone and a warning; once it does, a missing or
unknown key fails the release before anything is published. tools/ci/new_release_key.sh pipes a
new key straight into the secret and prints only the public key. ring and base64 are already in
the lockfile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:03:03 +02:00
Cosmic b27e4b9323 Skip Rust verify steps for content-only pull requests 2026-10-16 11:46:49 -04:00
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
Ludovic Marie 7a07251acf CI disk: short artifact retention, runner cleanup, sccache on the Mac runners
Releases now ship as often as changes land, so nothing they leave behind may
grow without bound. Workflow uploads keep 3 days (nightly parity logs 7); the
releases carry their own assets.

tools/ci/runner-gc.sh, run every 6 hours by launchd on the Mac mini from a copy
of main, skips a runner in a job, removes a checkout's cargo build folders past
12 GB and harness work folders under .local/ untouched for 3 days, and keeps the
2 newest shared preparations while no runner works. The runners build through
the machine's sccache, so what it removes comes back from the cache.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:05:59 +02:00
Ludovic Marie beac11eaa5 Nightly parity audit on the Mac mini, with an issue per regressed harness
nightly.yml runs every harness from scratch on main at 03:00 Paris time on the Mac mini's runner, after
preparing the resources again if the preparer changed. tools/ci/parity_issues.py compares the results with
the previous audit: a harness that passed and now fails gets one issue labelled parity-regression (an open
one is commented on), and an issue closes when its harness passes again. The bugs session picks them up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:18:31 +02:00
Ludovic Marie 9e016b6235 vanilla-data.sh: clone the sounds into each preparation instead of linking them
Vanilla's resource packs refuse symbolic links inside a pack: with linked sounds the vanilla sound oracles found
no sound files while Rust did (sound_event, sound_engine on #133's parity run).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:17:20 +02:00
Ludovic Marie cfad57d0b1 Mac runner: the game's sounds in each preparation; save_roundtrip Linux-only
The first audit on the Mac mini failed 7 audio harnesses (no sounds in the prepared resources: the Mac mini has
no official launcher for horizon-prepare sounds to copy from) and save_roundtrip (it needs vscreen). The
sounds depend only on the Minecraft version: kept once in shared-local/sounds/<version>/ and linked into each
preparation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:17:20 +02:00
Ludovic Marie 7b82ee8c16 Two Horizon runners on the Mac mini: one preparation at a time; docs
A second runner (ludovics-mac-mini-2, same labels) shares the first one's vanilla data through a link, so
vanilla-data.sh prepares under a lock; a 30-minute audit no longer holds a release's macOS build back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:17:20 +02:00
Ludovic Marie 989f843b1c vanilla-data.sh: keep everything horizon-prepare writes per preparer version, beside the shared data
The first parity run kept only .local/horizon-client per preparer version; the exporters' outputs beside it
stayed in the checkout, and the dedicated server, which finds registries.json at
<resources>/../../datagen, didn't find it next to the prepared folder (world_spawn failed).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:17:20 +02:00
Ludovic Marie 874d111d60 checks: cached verify, and a parity job on the Mac mini for the harnesses a PR can affect
verify keeps its dependencies and workspace build in a cache saved by main's runs (Swatinem/rust-cache).
The new parity job runs on the Mac mini's self-hosted runner (label horizon-game), which holds the
maintainer's own Minecraft files: tools/ci/vanilla-data.sh links .local/ to the runner's copy and prepares
the resources with the pull request's own horizon-prepare, once per preparer version; then
tools/parity.py --changed <base> runs only the harnesses whose traced dependencies changed since the merge
base and that have no pass for their current inputs. A fork's pull request runs it only when a maintainer
adds run-parity, once per label, on the commit it had then.

parity.py: --changed REF, and linux_only in tools/parity_hosts.json for harnesses that differ on other
platforms for a reason that is not a Horizon bug (skipped there with the reason, unless named).

docs/release-flow.md starts with the pull request checks and merging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:17:20 +02:00