name: checks # verify: every pull request and every push to main, on GitHub's runners (the repository is public). It is the # check main requires, so it never has a condition: a skipped job counts as passed, and every run of this workflow # must report a real verify (a pull request that changes no Rust finishes it in seconds instead). # parity: the vanilla harnesses a pull request's changes can affect, on the Mac mini's runner, which holds the # maintainer's own Minecraft files (Horizon never downloads the game); .github/workflows/parity.yml. Only for a pull # request that changes a harness, oracle, probe or the runner: the rest are checked on main by parity-main. A fork's pull # request runs it only when a maintainer adds the run-parity label (.github/workflows/run-parity.yml), so labels # never start this workflow. See docs/release-flow.md. on: push: branches: [main] pull_request: types: [opened, synchronize, reopened] workflow_dispatch: permissions: contents: read concurrency: # A new push to a pull request replaces its running checks; main's runs all finish (releases follow them). group: checks-${{ github.event.pull_request.number || github.sha }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} env: # Test runs never reach the telemetry server: no live-count ping, no uploads (docs/telemetry.md). HORIZON_TELEMETRY_URL: "off" jobs: verify: runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-python@v5 with: python-version: '3.12' - name: Does this pull request need Rust checks? if: github.event_name == 'pull_request' id: rust env: BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: python3 tools/ci/rust_changes.py "$BASE_SHA" "$HEAD_SHA" >> "$GITHUB_OUTPUT" - name: Keep game artifacts out of Git run: | python tools/check_public_tree.py python tools/check_public_tree.py --history - name: Test offline research tooling run: python -m unittest discover -s tests -v - name: Install Rust toolchain and the ALSA and udev headers the client links against if: steps.rust.outputs.required != 'false' run: | sudo apt-get update && sudo apt-get install -y --no-install-recommends libasound2-dev libudev-dev rustup toolchain install stable --profile minimal --component rustfmt --component clippy --target wasm32-unknown-unknown # Dependencies and the workspace's own build, saved from main's runs (pull requests read main's cache). - uses: Swatinem/rust-cache@v2 if: steps.rust.outputs.required != 'false' with: shared-key: verify save-if: ${{ github.ref == 'refs/heads/main' }} - name: Check formatting and lint if: steps.rust.outputs.required != 'false' run: | cargo +stable fmt --all -- --check # With the game's code mods (on in every release build, off by default). cargo +stable clippy --workspace --all-targets --features horizon-client/mods -- -D warnings # The server build (released as horizon-engine): the game without sound or controllers. cargo +stable clippy -p horizon-client --all-targets --no-default-features -- -D warnings # Libraries, binaries and integration tests, then doc tests: not the ~275 examples, which clippy above already # type-checks and the parity job builds and runs (they were most of this step's build and link time). - name: Test Rust core and voxel kernel correctness if: steps.rust.outputs.required != 'false' run: | cargo +stable test --workspace --lib --bins --tests --features horizon-client/mods,horizon-mods/pack # The full content-root tests (#[ignore]d: minutes each on a laptop). The same build as above; they need the # prepared vanilla data, so without it they pass at once. cargo +stable test --workspace --lib --features horizon-client/mods,horizon-mods/pack mod_content::tests:: -- --ignored cargo +stable test --workspace --doc --features horizon-client/mods,horizon-mods/pack # The launcher is its own workspace (launcher/Cargo.toml), so verify's cargo runs never build it. A pull request that # changes it or a crate it builds is checked here, and so is every push to main: two pull requests can each pass # alone and break it together (#2059 and the registry ratings did, 2026-10-26). launcher: runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Does this pull request change the launcher or a crate it builds? if: github.event_name == 'pull_request' id: launcher env: BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: python3 tools/ci/rust_changes.py --launcher "$BASE_SHA" "$HEAD_SHA" >> "$GITHUB_OUTPUT" - name: Install Rust and the headers GPUI links against if: steps.launcher.outputs.required != 'false' run: | sudo apt-get update && sudo apt-get install -y --no-install-recommends libxkbcommon-x11-dev libwayland-dev \ libx11-xcb-dev libfontconfig-dev libvulkan-dev libasound2-dev libudev-dev rustup toolchain install stable --profile minimal --component rustfmt --component clippy - uses: Swatinem/rust-cache@v2 if: steps.launcher.outputs.required != 'false' with: workspaces: launcher shared-key: launcher save-if: ${{ github.ref == 'refs/heads/main' }} - name: Check the launcher's formatting, lint and tests if: steps.launcher.outputs.required != 'false' working-directory: launcher run: | cargo +stable fmt --check cargo +stable clippy --all-targets --locked -- -D warnings cargo +stable test --locked --lib # Most pull requests leave the harnesses to parity-main, which runs them on main a batch of merges at a time # (.github/workflows/parity-main.yml; releases wait for it). A pull request still runs them itself when it changes # a harness, an oracle, a probe or the runner, or carries the run-parity label (run-parity.yml, when it is added). parity-scope: if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest outputs: run: ${{ steps.scope.outputs.run }} steps: - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 persist-credentials: false - id: scope env: BASE_SHA: ${{ github.event.pull_request.base.sha }} LABELS: ${{ join(github.event.pull_request.labels.*.name, ' ') }} run: | changed=$(git diff --name-only "$(git merge-base "$BASE_SHA" HEAD)" HEAD) harness=$(grep -E '^(tools/test_[a-z0-9_]+_parity\.py|tools/(parity[a-z_]*|lab)\.py|tools/parity_hosts\.json|mods/horizon/src/test/|crates/[^/]+/examples/|crates/horizon-client/src/game/[a-z_]*(fixture|harness)\.rs|\.github/workflows/parity[a-z-]*\.yml)' <<< "$changed" || true) if [ -n "$harness" ] || [[ " $LABELS " == *" run-parity "* ]]; then echo "run=true" >> "$GITHUB_OUTPUT" echo "The pull request runs its harnesses: ${harness:-label run-parity}" else echo "run=false" >> "$GITHUB_OUTPUT" echo "No harness, oracle, probe or runner change: parity-main checks this on main" fi parity: needs: parity-scope if: needs.parity-scope.outputs.run == 'true' uses: ./.github/workflows/parity.yml with: base: ${{ github.event.pull_request.base.sha }} head: ${{ github.event.pull_request.head.sha }} full: ${{ contains(github.event.pull_request.labels.*.name, 'full-parity') }} # Horizon never downloads the game. These jobs need a self-hosted Linux runner # (label horizon-game) holding the maintainer's own copy: the official launcher's # game directory in HORIZON_MINECRAFT_DIR and the pinned server jar in # HORIZON_SERVER_JAR. Start them by hand: gh workflow run checks --ref . vanilla-parity: if: github.event_name == 'workflow_dispatch' runs-on: [self-hosted, linux, horizon-game] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install Rust toolchain run: rustup toolchain install stable --profile minimal - uses: actions/setup-java@v4 with: distribution: temurin java-version: '25' - name: Compare Rust foundations with pinned vanilla client and server bytecode env: RUSTUP_TOOLCHAIN: stable run: python tools/test_rust_parity.py --prepare - name: Check Rust world-data interoperability with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_world_parity.py - name: Compare scheduled ticks and saved tick data with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_tick_parity.py - name: Compare simulation geometry and cached block collision shapes env: RUSTUP_TOOLCHAIN: stable run: | python tools/test_geometry_parity.py python tools/test_collision_parity.py - name: Compare noise samplers and density kernels with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_noise_parity.py - name: Compare compiled density samplers with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_density_parity.py - name: Compare world-generation JSON codecs with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_worldgen_parity.py - name: Compare heightmap predicates and chunk mutations with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_heightmap_parity.py - name: Compare pre-light chunk status transitions and block writes env: RUSTUP_TOOLCHAIN: stable run: python tools/test_chunk_stage_parity.py - name: Compare feature placement into resident neighboring chunks env: RUSTUP_TOOLCHAIN: stable run: python tools/test_feature_region_parity.py - name: Compare nested feature selectors and sequences env: RUSTUP_TOOLCHAIN: stable run: python tools/test_feature_graph_parity.py - name: Compare named feature holders and placed-feature tags env: RUSTUP_TOOLCHAIN: stable run: python tools/test_feature_catalog_parity.py - name: Compare configured fill-layer block placement env: RUSTUP_TOOLCHAIN: stable run: python tools/test_fill_layer_parity.py - name: Compare replacement-rule codecs and evaluation env: RUSTUP_TOOLCHAIN: stable run: python tools/test_rule_test_parity.py - name: Compare simple-block placement and feature composition env: RUSTUP_TOOLCHAIN: stable run: python tools/test_simple_block_parity.py - name: Compare block-column sampling and obstruction env: RUSTUP_TOOLCHAIN: stable run: python tools/test_block_column_parity.py - name: Compare vertical pillars and cap feature placement env: RUSTUP_TOOLCHAIN: stable run: python tools/test_pillar_parity.py - name: Compare exactly-one-neighbor feature spreading env: RUSTUP_TOOLCHAIN: stable run: python tools/test_neighbor_spread_parity.py - name: Compare block-pile support and placement env: RUSTUP_TOOLCHAIN: stable run: python tools/test_block_pile_parity.py - name: Compare disk replacement and postprocessing env: RUSTUP_TOOLCHAIN: stable run: python tools/test_disk_parity.py - name: Compare block-blob support search and geometry env: RUSTUP_TOOLCHAIN: stable run: python tools/test_block_blob_parity.py - name: Compare spring placement and fluid tick scheduling env: RUSTUP_TOOLCHAIN: stable run: python tools/test_spring_parity.py - name: Compare vine attachment priority and placement env: RUSTUP_TOOLCHAIN: stable run: python tools/test_vines_parity.py - name: Compare underwater magma floor scans and occlusion env: RUSTUP_TOOLCHAIN: stable run: python tools/test_underwater_magma_parity.py - name: Compare blue-ice seeding and growth attempts env: RUSTUP_TOOLCHAIN: stable run: python tools/test_blue_ice_parity.py - name: Compare spike geometry and discontinuous roots env: RUSTUP_TOOLCHAIN: stable run: python tools/test_spike_parity.py - name: Compare End podium construction and destruction requests env: RUSTUP_TOOLCHAIN: stable run: python tools/test_end_podium_parity.py - name: Rust End gateway parity env: RUSTUP_TOOLCHAIN: stable run: python tools/test_end_gateway_parity.py - name: Compare bonus chest placement and conditional loot seeding env: RUSTUP_TOOLCHAIN: stable run: python tools/test_bonus_chest_parity.py - name: Compare recursive chorus generation and connection refreshes env: RUSTUP_TOOLCHAIN: stable run: python tools/test_chorus_plant_parity.py - name: Compare small speleothem patches and waterlogged points env: RUSTUP_TOOLCHAIN: stable run: python tools/test_speleothem_parity.py - name: Compare replacement blob search and Manhattan shells env: RUSTUP_TOOLCHAIN: stable run: python tools/test_replace_blobs_parity.py - name: Compare bamboo stalks, podzol patches and leafy caps env: RUSTUP_TOOLCHAIN: stable run: python tools/test_bamboo_parity.py - name: Compare Nether delta rims and shifted contents env: RUSTUP_TOOLCHAIN: stable run: python tools/test_delta_parity.py - name: Compare biome freezing and snow eligibility env: RUSTUP_TOOLCHAIN: stable run: python tools/test_biome_weather_parity.py - name: Compare frozen top layers and snowy ground updates env: RUSTUP_TOOLCHAIN: stable run: python tools/test_snow_freeze_parity.py - name: Compare generation-stage tick storage and routing env: RUSTUP_TOOLCHAIN: stable run: python tools/test_generation_tick_parity.py - name: Compare below-zero upgrade metadata codecs env: RUSTUP_TOOLCHAIN: stable run: python tools/test_retrogen_data_parity.py - name: Compare below-zero upgrade lifecycle and block transformations env: RUSTUP_TOOLCHAIN: stable run: python tools/test_retrogen_chunk_parity.py - name: Compare chunk-status metadata, ordering and codecs env: RUSTUP_TOOLCHAIN: stable run: python tools/test_chunk_status_parity.py - name: Compare proto-chunk block-entity storage and coordinates env: RUSTUP_TOOLCHAIN: stable run: python tools/test_block_entity_storage_parity.py - name: Compare ordered single-block replacement features env: RUSTUP_TOOLCHAIN: stable run: python tools/test_replace_block_parity.py - name: Compare scattered ore placement and air-exposure checks env: RUSTUP_TOOLCHAIN: stable run: python tools/test_scattered_ore_parity.py - name: Compare resident bulk-section access and raw writes env: RUSTUP_TOOLCHAIN: stable run: python tools/test_bulk_section_parity.py - name: Compare biome feature ordering and cycle detection env: RUSTUP_TOOLCHAIN: stable run: python tools/test_feature_sort_parity.py - name: Compare world-generation random wrappers and seeding env: RUSTUP_TOOLCHAIN: stable run: python tools/test_worldgen_random_parity.py - name: Compare biome filtering and top-level feature context env: RUSTUP_TOOLCHAIN: stable run: python tools/test_biome_placement_parity.py - name: Compare biome decoration feature scheduling and seeding env: RUSTUP_TOOLCHAIN: stable run: python tools/test_decoration_parity.py - name: Compare decoration against resident biome and terrain sections env: RUSTUP_TOOLCHAIN: stable run: python tools/test_decoration_region_parity.py - name: Compare block-state provider choices and rotation env: RUSTUP_TOOLCHAIN: stable run: python tools/test_state_provider_parity.py - name: Compare contained fluids and height queries with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_fluid_parity.py - name: Compare entity obstruction with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_entity_obstruction_parity.py - name: Compare block solidity and replaceability with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_block_properties_parity.py - name: Compare face slicing and block support with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_face_support_parity.py - name: Compare cached block support with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_block_support_parity.py - name: Compare dynamic block support with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_dynamic_support_parity.py - name: Compare moving-piston collision and interpolation with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_piston_shape_parity.py - name: Compare supported block survival rules with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_block_survival_parity.py - name: Compare noise-stage chunk filling with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_terrain_parity.py - name: Compare structure terrain-adaptation density with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_structure_density_parity.py - name: Compare seeded material bands and surface queries with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_material_surface_parity.py - name: Compare material condition codecs and rule evaluation with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_material_condition_parity.py - name: Compare biome temperatures and cache behavior with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_biome_temperature_parity.py - name: Compare badlands and frozen-ocean extensions with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_material_extension_parity.py - name: Compare bundled material-rule libraries with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_material_rule_library_parity.py - name: Compare fixed, checkerboard and End biome sources with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_biome_source_parity.py - name: Compare seeded cave paths and failure state with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_cave_carver_parity.py - name: Compare seeded canyon paths and width factors with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_canyon_carver_parity.py - name: Compare initial block-writing feature algorithms with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_basic_feature_parity.py - name: Compare feature placement chains with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_feature_placement_parity.py - name: Compare feature placement primitives with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_placement_parity.py - name: Compare anchored height providers with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_height_provider_parity.py - name: Compare scalar value providers with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_value_provider_parity.py - name: Compare cave and canyon geometric primitives with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_carving_geometry_parity.py - name: Compare carving-mask traversal and callback mutations with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_carving_mask_parity.py - name: Compare registry-bound biome-source codecs with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_biome_source_codec_parity.py - name: Compare all four biome-source kinds in chunk generation with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_biome_generation_parity.py - name: Compare chunk biome filling and failure state with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_biome_chunk_parity.py - name: Compare biome zoom and seed obfuscation with pinned vanilla env: RUSTUP_TOOLCHAIN: stable run: python tools/test_biome_zoom_parity.py - name: Compare surface-stage chunks with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_material_stage_parity.py - name: Compare block-state JSON codecs with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_block_state_codec_parity.py - name: Compare vertical anchors with pinned vanilla client and server env: RUSTUP_TOOLCHAIN: stable run: python tools/test_anchor_parity.py - name: Compare float logarithms at domain and rounding boundaries env: RUSTUP_TOOLCHAIN: stable run: python tools/test_float_log_parity.py jfr-analysis: if: github.event_name == 'workflow_dispatch' runs-on: [self-hosted, linux, horizon-game] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: '3.12' - uses: actions/setup-java@v4 with: distribution: temurin java-version: '25' - name: Verify streaming JFR analysis against the JDK exporter run: python tools/test_jfr.py - name: Verify ZGC pause and allocation event interpretation run: python tools/test_jfr.py --gc zgc