# 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. # # Mirrors RunicGateway/link's pr-checks.yml — same gates in the same order as # release.yml, so a green PR means the release will get past its own gates too. # The one structural difference is the crate guard below. # # ── Crate guard ────────────────────────────────────────────────────────────── # This repo is in the planning phase and has no Cargo project yet (the design of # record is docs/installer/PLAN.md; Phase 1 is what creates the crate). Rather # than leave the repo ungated until then — or land a workflow that red-Xes every # governance/docs PR — the gates are conditional on a root Cargo.toml existing. # Before the crate lands, the job reports green with a notice. The moment # Phase 1 adds Cargo.toml the gates arm themselves; nothing here has to change. # # The crate is expected at the REPO ROOT (not a subdirectory like link/sidecar): # this repo's sole product is the one installer binary, so there is nothing to # namespace it against. # # 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. # # 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 jobs: rust-gates: runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/checkout@v4 - name: Detect whether a crate exists yet id: detect run: | set -euo pipefail if [ -f Cargo.toml ]; then echo "crate=true" >> "$GITHUB_OUTPUT" echo "==> Cargo.toml found — running the full gate set." else echo "crate=false" >> "$GITHUB_OUTPUT" echo "==> No Cargo.toml at the repo root yet (planning phase)." echo " Skipping fmt/clippy/test. These gates arm themselves as" echo " soon as Phase 1 lands the crate — see docs/installer/PLAN.md." fi # 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) if: ${{ steps.detect.outputs.crate == 'true' }} 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 if: ${{ steps.detect.outputs.crate == 'true' }} uses: actions/cache@v4 with: path: | ~/.cargo/registry ~/.cargo/git target key: ${{ runner.os }}-cargo-${{ hashFiles('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 if: ${{ steps.detect.outputs.crate == 'true' }} run: cargo fmt --check # --all-targets covers tests and examples, not just the binary. # -D warnings makes a lint a failure, so the crate starts clean at this bar # and anything new is a regression introduced by the PR. - name: cargo clippy if: ${{ steps.detect.outputs.crate == 'true' }} 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 if: ${{ steps.detect.outputs.crate == 'true' }} run: cargo test --locked