diff --git a/.gitea/workflows/pr-checks.yml b/.gitea/workflows/pr-checks.yml new file mode 100644 index 0000000..d1d8939 --- /dev/null +++ b/.gitea/workflows/pr-checks.yml @@ -0,0 +1,101 @@ +# Gate every pull request into `main` on the same Rust checks the release runs, +# so a formatting slip, a lint regression, or a failing test can't reach the +# deployable branch. +# +# Why this exists: release.yml runs only AFTER merge (on push to `main`) and its +# FIRST Rust step is `cargo fmt --check`. Before this workflow, an unformatted +# commit merged cleanly and then killed the release job before it could build, +# tag, or publish anything — the repo had no pull_request workflow at all. These +# gates are deliberately a mirror of release.yml's, in the same order, so a green +# PR means the release will get past its gates too. +# +# Enforcement (one-time, in the Gitea UI): +# Repository Settings → Branches → Branch Protection (rule for `main`) +# • Enable Status Check +# • Status check patterns: PR Checks / * +# Note: Gitea only lists a context in its dropdown after it has reported once, +# so let this workflow run on one PR first. The `PR Checks / *` glob matches +# without needing the dropdown. +# +# Scope note: this gates PRs into `main` only. Feature work that lands on an +# integration branch first (e.g. `edge`) is still caught on the branch's PR into +# `main`. To gate that earlier hop too, add the branch to the `branches:` list +# below — nothing else needs to change. +# +# Runner: the same self-hosted `ubuntu-latest` runner release.yml uses. Rust is +# not assumed to be preinstalled, so the toolchain step bootstraps it the same +# way release.yml does (minus the MinGW cross-compile deps — PRs build for the +# host only; the Windows cross-build stays a release-time concern). + +name: PR Checks + +on: + pull_request: + branches: [main] + +# A newer push to the same PR cancels the in-flight run. +concurrency: + group: pr-checks-${{ github.ref }} + cancel-in-progress: true + +env: + WORKDIR: sidecar + +jobs: + rust-gates: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + + # One job runs all three gates on purpose: installing the toolchain costs + # far more than the checks themselves, so splitting fmt/clippy/test into + # parallel jobs would pay that cost three times for no wall-clock win. + - name: Install Rust toolchain (rustfmt + clippy) + run: | + set -euo pipefail + SUDO=""; [ "$(id -u)" -ne 0 ] && SUDO="sudo" + $SUDO apt-get update + $SUDO apt-get install -y --no-install-recommends \ + build-essential curl ca-certificates git + + if ! command -v cargo >/dev/null 2>&1; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --profile minimal --default-toolchain stable + fi + echo "${HOME}/.cargo/bin" >> "$GITHUB_PATH" + export PATH="${HOME}/.cargo/bin:${PATH}" + rustup component add rustfmt clippy + cargo --version && cargo fmt --version && cargo clippy --version + + # Keyed on Cargo.lock: dependency builds are reused until a dep actually + # changes. A cache miss only makes the run slower, never wrong. + - name: Cache cargo registry and build dir + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + sidecar/target + key: ${{ runner.os }}-cargo-${{ hashFiles('sidecar/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + # Cheapest gate first — parses only, no compile, so a formatting slip + # fails in seconds instead of after a full build. + - name: cargo fmt --check + working-directory: sidecar + run: cargo fmt --check + + # --all-targets covers tests and examples, not just the binary. + # -D warnings makes a lint a failure; the crate is clean at this bar today, + # so anything new here is a regression introduced by the PR. + - name: cargo clippy + working-directory: sidecar + run: cargo clippy --locked --all-targets -- -D warnings + + # --locked matches release.yml: it also proves Cargo.lock is in sync with + # Cargo.toml, rather than letting the build silently update it. + - name: cargo test + working-directory: sidecar + run: cargo test --locked diff --git a/README.md b/README.md index d6e8b88..bf6af7d 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ network-facing component, which is what keeps the game unreachable from the inte | Path | What | |------|------| | `sidecar/` | The Rust sidecar crate — terminates the loopback link to the shard, exposes WS + REST to the website. See [`sidecar/README.md`](sidecar/README.md). | +| `.gitea/workflows/pr-checks.yml` | Gates every PR into `main` on `cargo fmt --check`, `cargo clippy -D warnings`, and `cargo test`. | | `.gitea/workflows/release.yml` | Builds + releases the sidecar binary (Linux + Windows) on every merge to `main`. | ## Build & run @@ -42,6 +43,17 @@ cargo run --release every merge to `main` (conventional-commit versioning). See [`sidecar/README.md`](sidecar/README.md) for configuration and the wire protocol. +Before that, `.gitea/workflows/pr-checks.yml` runs the same gates on every pull request into `main` — +`cargo fmt --check`, `cargo clippy --all-targets -- -D warnings`, then `cargo test --locked`. Run them +locally before pushing and the PR will be green: + +```bash +cd sidecar +cargo fmt # or --check to just report +cargo clippy --locked --all-targets -- -D warnings +cargo test --locked +``` + ## Deployment & compatibility The plugin ([RunicGateway/servuo-plugins](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins))