ci: gate pull requests into main on server and client checks #1
127
.gitea/workflows/pr-checks.yml
Normal file
127
.gitea/workflows/pr-checks.yml
Normal file
@@ -0,0 +1,127 @@
|
||||
# Gate every pull request into `main` on a fast, DB-free check suite, so a broken
|
||||
# build or a failing test can't reach the branch that gets released.
|
||||
#
|
||||
# Mirrors RunicGateway/website's pr-checks.yml — this module is two npm packages
|
||||
# shaped like that repo's `server/` and `client/`, and it is loaded into that
|
||||
# repo's process, so it is checked the same way with the same Node version.
|
||||
#
|
||||
# ── Package guard ────────────────────────────────────────────────────────────
|
||||
# This repo is in the planning phase and has no module code yet: the API contract
|
||||
# is settled in Phase 1 and Phase 3 is what extracts the UO half of `website/`
|
||||
# into this repo (docs/website/MODULE_SYSTEM.md §2.7). Rather than leave the repo
|
||||
# ungated until then — or land a workflow that red-Xes every governance/docs PR —
|
||||
# each half's gates are conditional on its package.json existing. Before the code
|
||||
# lands, the job reports green with a notice saying so. The moment a package.json
|
||||
# appears the gates arm themselves; nothing here has to change.
|
||||
#
|
||||
# The same trick guards the client build, which is the higher-risk half: it must
|
||||
# build with Vite in library mode with react/react-dom/react-router-dom EXTERNAL,
|
||||
# because there is exactly one React instance in the page and core owns it. A
|
||||
# module that bundles its own React loads and then breaks hooks at runtime, which
|
||||
# is precisely the kind of failure worth catching before merge.
|
||||
#
|
||||
# Not here yet, deliberately, because there is nothing for them to act on until
|
||||
# Phase 3: a release workflow (the `module-uo-<version>.tar.gz` artifact and its
|
||||
# sha256 manifest), the zero-internal-imports check, and the module's own frozen
|
||||
# route manifest. Each lands with the code it checks.
|
||||
#
|
||||
# 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, and keeps matching as jobs are added.
|
||||
#
|
||||
# Runner: the shared self-hosted `ubuntu-latest` runner. These jobs need only
|
||||
# Node — no Docker socket, no database.
|
||||
|
||||
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:
|
||||
server-tests:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Detect whether the server half exists yet
|
||||
id: detect
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -f server/package.json ]; then
|
||||
echo "pkg=true" >> "$GITHUB_OUTPUT"
|
||||
echo "==> server/package.json found - running the server gates."
|
||||
else
|
||||
echo "pkg=false" >> "$GITHUB_OUTPUT"
|
||||
echo "==> No server/package.json yet (planning phase)."
|
||||
echo " Skipping install/test. These gates arm themselves as soon"
|
||||
echo " as Phase 3 lands the server half - see MODULE_SYSTEM.md."
|
||||
fi
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
if: ${{ steps.detect.outputs.pkg == 'true' }}
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: server/package-lock.json
|
||||
|
||||
# `npm ci` rather than `npm install`: it also proves the lockfile is in
|
||||
# sync with package.json instead of silently updating it.
|
||||
- name: Install server deps
|
||||
if: ${{ steps.detect.outputs.pkg == 'true' }}
|
||||
run: npm ci --prefix server
|
||||
|
||||
- name: Run server tests
|
||||
if: ${{ steps.detect.outputs.pkg == 'true' }}
|
||||
run: npm test --prefix server
|
||||
|
||||
client-build:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Detect whether the client half exists yet
|
||||
id: detect
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if [ -f client/package.json ]; then
|
||||
echo "pkg=true" >> "$GITHUB_OUTPUT"
|
||||
echo "==> client/package.json found - running the client gates."
|
||||
else
|
||||
echo "pkg=false" >> "$GITHUB_OUTPUT"
|
||||
echo "==> No client/package.json yet (planning phase)."
|
||||
echo " Skipping install/test/build. These gates arm themselves as"
|
||||
echo " soon as Phase 3 lands the client half."
|
||||
fi
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
if: ${{ steps.detect.outputs.pkg == 'true' }}
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
cache-dependency-path: client/package-lock.json
|
||||
|
||||
- name: Install client deps
|
||||
if: ${{ steps.detect.outputs.pkg == 'true' }}
|
||||
run: npm ci --prefix client
|
||||
|
||||
- name: Run client tests
|
||||
if: ${{ steps.detect.outputs.pkg == 'true' }}
|
||||
run: npm test --prefix client
|
||||
|
||||
# Building the ESM chunk in CI is not just a check: it is how the chunk
|
||||
# that ships in the release is produced, since an operator never builds.
|
||||
- name: Build the client chunk
|
||||
if: ${{ steps.detect.outputs.pkg == 'true' }}
|
||||
run: npm run build --prefix client
|
||||
Reference in New Issue
Block a user