Files
installer/.gitea/workflows/pr-checks.yml
wtclaude ea9aad6e9b
All checks were successful
PR Checks / rust-gates (pull_request) Successful in 11s
ci(installer): add pr-checks, release, and project-tree sync workflows
Bring this repo's CI up to parity with the other Runic Gateway repos. All
three are retargeted from RunicGateway/link, which is the closest analog
(same Rust toolchain, same release engine, same runner).

pr-checks.yml
  Gates PRs into main on cargo fmt --check, clippy -D warnings, and
  cargo test --locked, in that order, one job — mirroring release.yml's
  gates so a green PR implies a green release.

release.yml
  The conventional-commit release engine from link/, with the Rust
  adapter retargeted: crate at the repo root, binary
  runicgateway-installer, cross-compiled for x86_64 Linux and Windows.
  Artifact names follow PLAN.md §3. The generated changelog now carries
  the checksum-verification block, because releases are deliberately
  unsigned and SHA256SUMS is the trust anchor (PLAN.md §3) — that makes
  the verify instructions part of the release, not a doc someone has to
  find.

sync-project-tree.yml (+ .gitea/scripts/gen_tree.py)
  Regenerates docs/installer/PROJECT_TREE.md on every push to main and
  opens or force-updates a PR against the docs repo. Verbatim from link/
  apart from the repo/path/label env block.

Crate guard
  This repo has no Cargo project yet — Phase 1 creates it. Landing the
  workflows unguarded would red-X every governance and docs PR until
  then, and holding them back leaves the repo ungated exactly while its
  conventions are being set. So both Rust workflows check for a root
  Cargo.toml first: pr-checks skips its gates with a notice, and
  release.yml's plan step sets RELEASE=false and exits. Both arm
  themselves the moment Cargo.toml lands, with no edit here.

Verified before pushing: all three files parse as YAML, every run block
passes bash -n, and the release plan step was simulated against a throwaway
git repo both without a crate (release=false, exit 0) and with one
(first-release path -> v0.1.0 with the changelog rendered).

Not included: the bundle-manifest workflow (PLAN.md §7) and the
release-dispatch hook, which are Phase 0 item 3 and depend on
servuo-plugins having a release workflow first.

Note for setup: release.yml and sync-project-tree.yml need REGISTRY_USER
and REGISTRY_TOKEN (write:repository, plus read/write on RunicGateway/docs)
configured for this repo.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 09:14:56 -05:00

119 lines
5.2 KiB
YAML

# 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