mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-08 17:19:10 -10:00
CI: run verify's checks as three parallel jobs
verify ran the public-tree check, the Python tests, clippy and the Rust tests one after another (~8 min). They now run as the python, clippy and tests jobs at once, and verify gathers them (always(), failing unless all three passed), so the required check keeps its name. A pull request's public-tree check reads only its own commits (check_public_tree.py --range base..head) instead of every ref's whole history; main's pushes still run --history. Closes #4978 Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
4de90a13d4
commit
f2a99d6238
6 files changed
+93
-22
No files matched your search
+70
-12
@@ -1,7 +1,7 @@
|
||||
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).
|
||||
# check main requires, so it always runs: a skipped job counts as passed, and every run of this workflow must report
|
||||
# a real verify. It gathers the python, clippy and tests jobs (a change to docs alone skips their Rust steps).
|
||||
# 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
|
||||
@@ -24,7 +24,10 @@ env:
|
||||
HORIZON_TELEMETRY_URL: "off"
|
||||
|
||||
jobs:
|
||||
verify:
|
||||
# verify's checks run as three jobs at once (python, clippy, tests: ~4 min instead of ~8 one after another), and
|
||||
# verify gathers them. A failed job makes its dependents skip, and a skipped required check counts as passed, so
|
||||
# verify runs always() and fails unless all three passed.
|
||||
python:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
@@ -34,6 +37,28 @@ jobs:
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
# A pull request adds only its own commits to main, so it reads those; main's pushes read every ref's history.
|
||||
- name: Keep game artifacts out of Git
|
||||
env:
|
||||
EVENT: ${{ github.event_name }}
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
python tools/check_public_tree.py
|
||||
if [ "$EVENT" = pull_request ]; then
|
||||
python tools/check_public_tree.py --range "$BASE_SHA..$HEAD_SHA"
|
||||
else
|
||||
python tools/check_public_tree.py --history
|
||||
fi
|
||||
- name: Test offline research tooling
|
||||
run: python -m unittest discover -s tests -v
|
||||
clippy:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Does this pull request need Rust checks?
|
||||
if: github.event_name == 'pull_request'
|
||||
id: rust
|
||||
@@ -41,22 +66,16 @@ jobs:
|
||||
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
|
||||
# Dependencies and the workspace's own build, saved from main's runs (pull requests read main's cache).
|
||||
# Dependencies and the workspace's own check, 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
|
||||
shared-key: verify-clippy
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: Check formatting and lint
|
||||
if: steps.rust.outputs.required != 'false'
|
||||
@@ -65,8 +84,35 @@ jobs:
|
||||
cargo +stable clippy --workspace --all-targets -- -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
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- 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: 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
|
||||
# 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' }}
|
||||
# Libraries, binaries and integration tests, then doc tests: not the ~275 examples, which clippy already
|
||||
# type-checks and the parity job builds and runs (they were most of this step's build and link time).
|
||||
# cargo test, not nextest: nextest's process per test reloads each binary's shared tables for every test, and on
|
||||
# these 4-core runners it was no faster (#4978).
|
||||
- name: Test Rust core and voxel kernel correctness
|
||||
if: steps.rust.outputs.required != 'false'
|
||||
run: |
|
||||
@@ -75,6 +121,18 @@ jobs:
|
||||
# prepared vanilla data, so without it they pass at once.
|
||||
cargo +stable test --workspace --lib mod_content::tests:: -- --ignored
|
||||
cargo +stable test --workspace --doc
|
||||
verify:
|
||||
if: always()
|
||||
needs: [python, clippy, tests]
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Did python, clippy and tests pass?
|
||||
env:
|
||||
RESULTS: ${{ join(needs.*.result, ' ') }}
|
||||
run: |
|
||||
echo "python, clippy, tests: $RESULTS"
|
||||
for result in $RESULTS; do [ "$result" = success ] || exit 1; done
|
||||
# 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).
|
||||
|
||||
@@ -13,15 +13,16 @@ reopened; labels never start it):
|
||||
|
||||
| Job | Runs on | What | Time |
|
||||
| --- | --- | --- | --- |
|
||||
| `verify` | GitHub `ubuntu-latest` | Public-tree check, Python tests, rustfmt, clippy with warnings denied, Rust tests. Dependencies and the workspace build come from a cache saved by main's runs | ~4 min cold, less warm |
|
||||
| `verify` | GitHub `ubuntu-latest` | Gathers three jobs that run at once: `python` (public-tree check, Python tests), `clippy` (rustfmt, clippy with warnings denied) and `tests` (Rust tests). Dependencies come from caches saved by main's runs | ~4 min |
|
||||
| `parity / parity` | A runner with the label `horizon-game`: the Mac mini's and playhorizon's (below) | Only for a change to a harness, an oracle, a probe or the runner (or the `run-parity` label): the vanilla harnesses the change can affect (`tools/parity.py --changed <base>`). Other pull requests skip it: [parity-main](#parity-on-main) checks them on main | seconds when nothing is affected; minutes per affected harness |
|
||||
|
||||
Pushes to main run `verify` only; the nightly audit covers main's harnesses.
|
||||
|
||||
`verify` is the check main requires, so it has no condition and runs in every run of `checks`: GitHub counts a
|
||||
*skipped* required check as passed, and labels used to start runs whose `verify` was skipped, which let
|
||||
auto-merge merge before the real `verify` finished. A pull request that changes no Rust (docs, tools) runs only its
|
||||
quick steps and finishes in seconds.
|
||||
`verify` is the check main requires, so it runs `always()` in every run of `checks` and fails unless `python`,
|
||||
`clippy` and `tests` all passed: GitHub counts a *skipped* required check as passed, and labels used to start runs
|
||||
whose `verify` was skipped, which let auto-merge merge before the real `verify` finished. A pull request that changes
|
||||
only documentation skips the Rust steps and finishes in about a minute. A pull request's public-tree check reads only
|
||||
its own commits (`--range base..head`); main's pushes read every ref's history.
|
||||
|
||||
### The parity job
|
||||
|
||||
|
||||
+2
-2
@@ -50,8 +50,8 @@ and some are waiting for Ludovic:
|
||||
untrusted, with attachments as plain links. Text that looks aimed at an AI (talking to "the AI", asking to run
|
||||
commands, asking for passwords or keys, linking to scripts, hidden characters) is marked **suspicious**, and the
|
||||
agent coordinating the others sees the mark.
|
||||
- **`verify` always runs.** Labels no longer start the checks, and the required `verify` job has no condition, so
|
||||
the only `verify` a change can have is one that ran. Main requires it, from GitHub Actions only, for everyone,
|
||||
- **`verify` always runs.** Labels no longer start the checks, and the required `verify` job runs `always()` and fails
|
||||
unless the jobs it gathers passed, so the only `verify` a change can have is one that ran. Main requires it, from GitHub Actions only, for everyone,
|
||||
admins included.
|
||||
- **Checked before it is published.** Every release is published as the latest, which launchers and servers
|
||||
install by themselves, so the release run publishes it only after its smoke test passed (the game started and
|
||||
|
||||
@@ -199,6 +199,15 @@ class PublicationTests(unittest.TestCase):
|
||||
self.assertIn(".local/artifact.txt", check_public_tree.paths_in_repository())
|
||||
git("-c", "user.name=Test", "-c", "[email protected]", "commit", "-m", "fixture")
|
||||
self.assertIn(".local/artifact.txt", check_public_tree.paths_in_repository(history=True))
|
||||
# A range reads only its own commits: the artifact's commit lies before it.
|
||||
(directory / ".local" / "artifact.txt").unlink()
|
||||
git("rm", "-q", "--cached", ".local/artifact.txt")
|
||||
git("-c", "user.name=Test", "-c", "[email protected]", "commit", "-m", "removal")
|
||||
self.assertIn(".local/artifact.txt", check_public_tree.paths_in_repository(True, ["HEAD~1..HEAD"]))
|
||||
(directory / "README.md").write_text("public\n")
|
||||
git("add", "README.md")
|
||||
git("-c", "user.name=Test", "-c", "[email protected]", "commit", "-m", "public")
|
||||
self.assertEqual(check_public_tree.paths_in_repository(True, ["HEAD~1..HEAD"]), ["README.md", ""])
|
||||
|
||||
|
||||
def library(name, os_name=None):
|
||||
|
||||
@@ -52,9 +52,12 @@ def main():
|
||||
mode = parser.add_mutually_exclusive_group()
|
||||
mode.add_argument("--history", action="store_true", help="Inspect paths in all local Git history")
|
||||
mode.add_argument("--pre-push", action="store_true", help="Inspect history reachable from Git's ref updates on stdin")
|
||||
# CI's pull requests: only the commits a merge would add, not every ref's whole history.
|
||||
mode.add_argument("--range", metavar="BASE..HEAD", help="Inspect the commits of a revision range")
|
||||
args = parser.parse_args()
|
||||
revisions = pushed_revisions(sys.stdin) if args.pre_push else None
|
||||
rejected = sorted({path for path in paths_in_repository(args.history or args.pre_push, revisions)
|
||||
revisions = pushed_revisions(sys.stdin) if args.pre_push else [args.range] if args.range else None
|
||||
history = args.history or args.pre_push or bool(args.range)
|
||||
rejected = sorted({path for path in paths_in_repository(history, revisions)
|
||||
if path and prohibited(path)})
|
||||
if rejected:
|
||||
print("Private game/profiling artifacts must stay outside Git:", file=sys.stderr)
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ def parity_summary(output):
|
||||
return text
|
||||
|
||||
|
||||
# CI's verify job (.github/workflows/ci.yml): clippy over the whole workspace, and over the server build (the game
|
||||
# CI's clippy job (.github/workflows/ci.yml): clippy over the whole workspace, and over the server build (the game
|
||||
# without sound or controllers).
|
||||
CI_CLIPPY = "cargo clippy --workspace --all-targets --locked -- -D warnings"
|
||||
CI_CLIPPY_SERVER = "cargo clippy -p horizon-client --all-targets --locked --no-default-features -- -D warnings"
|
||||
|
||||
Reference in new issue
Block a user