ci(module-uo): gate pull requests into main on server and client checks
Mirrors RunicGateway/website's pr-checks.yml, since this module is two npm packages shaped like that repo's server/ and client/ and is loaded into that repo's process: same Node 20, same npm ci + test + build, same concurrency cancel and the same `PR Checks / *` status pattern for branch protection. The repo 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 here — so each half's gates are conditional on its package.json existing. Before the code lands the jobs report green with a notice explaining why; the moment a package.json appears they arm themselves with no edit to this file. That is the same guard the installer repo used through its own planning phase, and it beats both alternatives: leaving the repo ungated, or red-Xing every governance PR. Landing it now also gets the status contexts reported once, which is what makes them selectable when the `main` branch-protection rule is configured. Deliberately not here yet, because there is nothing for them to act on until Phase 3: the release workflow that publishes module-uo-<version>.tar.gz and its sha256, the zero-internal-imports check, and the module's own frozen route manifest. Each lands with the code it checks. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
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