Files

148 lines
5.9 KiB
YAML

name: CI
# Two gate levels keep report PRs cheap:
# - Report-only PRs (from compat-report.yml — changes limited to
# src/content/compat/**, public/screenshots/** or public/data/**) run only
# npm ci + typecheck + report validation. Report merges don't rebuild the
# site (deploy.yml republishes with fresh runtime data), so a full build
# and test suite add nothing on the PR branch.
# - Everything else (code, scripts, games database, pushes to main) runs the
# full pipeline: typecheck, tests, report validation and the production
# build (prebuild also regenerates the GitHub snapshot + compat JSONs).
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
issues: write
pull-requests: write
actions: write
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Full history so report-only PRs can be detected by diffing against
# the PR base (same allowlist as deploy.yml's content-only scope).
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Detect change scope
id: scope
shell: bash
env:
EVENT_NAME: ${{ github.event_name }}
BASE: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
# Pushes to main are always the full pipeline.
if [ "$EVENT_NAME" = "push" ]; then
echo "scope=full" >> "$GITHUB_OUTPUT"
exit 0
fi
if [ -z "$BASE" ] || [ -z "$HEAD_SHA" ]; then
echo "::warning::Missing PR base or head SHA; treating as full."
echo "scope=full" >> "$GITHUB_OUTPUT"
exit 0
fi
CHANGED="$(git diff --name-only "$BASE" "$HEAD_SHA" 2>/dev/null || true)"
if [ -z "$CHANGED" ]; then
echo "scope=full" >> "$GITHUB_OUTPUT"
exit 0
fi
# Report-only = every changed file is a compat report, a screenshot
# or generated data (mirrors deploy.yml). Anything else → full.
if printf '%s\n' "$CHANGED" | grep -vqE '^(src/content/compat/|public/screenshots/|public/data/)'; then
echo "scope=full" >> "$GITHUB_OUTPUT"
else
echo "scope=report" >> "$GITHUB_OUTPUT"
fi
- run: npm ci
- run: npm run typecheck
# Report validation is cheap and dependency-free — gate it on every run.
- name: Validate compatibility reports
run: node scripts/validate-compat.mjs
- name: Validate compatibility exports & sitemap
run: |
node scripts/export-compat-json.mjs
node scripts/export-site-compat-json.mjs
node scripts/generate-sitemap.mjs
- name: Test
if: steps.scope.outputs.scope != 'report'
env:
CHECK_SEED_SYNC: ${{ github.head_ref == 'data/games-refresh' }}
run: npm test
# prebuild validates compat reports and regenerates the GitHub snapshot,
# the GUI + site compat JSON and the sitemap (GITHUB_TOKEN avoids rate
# limits on the API fetches). Skipped for report-only PRs — report merges
# don't rebuild the site, so the full build adds nothing on the branch.
- name: Build
if: steps.scope.outputs.scope != 'report'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: npm run build
- name: Reopen mirror issue if tests failed on a compat PR
if: failure() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && startsWith(github.head_ref, 'compat/')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_BODY: ${{ github.event.pull_request.body }}
run: |
gh pr merge "$PR_NUMBER" --disable-auto || true
ISSUE_NUM="$(printf '%s' "$PR_BODY" | sed -n 's/.*<!-- mirror-issue: \([0-9]\+\) -->.*/\1/p' | head -1)"
if [ -n "$ISSUE_NUM" ]; then
gh issue reopen "$ISSUE_NUM" || true
gh issue comment "$ISSUE_NUM" -b "⚠️ Checks failed on report PR #$PR_NUMBER. Auto-merge disabled; please fix the report or tests." || true
fi
auto-deploy:
needs: [test]
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository && (startsWith(github.head_ref, 'compat/') || github.head_ref == 'data/games-refresh')
runs-on: ubuntu-latest
steps:
- name: Trigger deploy if auto-merged
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
for i in {1..36}; do
STATE="$(gh pr view "$PR_NUMBER" --json state --jq .state 2>/dev/null || true)"
if [ "$STATE" = "MERGED" ]; then
echo "PR #$PR_NUMBER merged successfully."
MERGE_SHA="$(gh pr view "$PR_NUMBER" --json mergeCommit --jq '.mergeCommit.oid // empty' 2>/dev/null || true)"
if [ -n "$MERGE_SHA" ]; then
EXISTING="$(gh run list --workflow=deploy.yml --commit="$MERGE_SHA" --json databaseId -q '.[0].databaseId // empty' 2>/dev/null || true)"
if [ -n "$EXISTING" ]; then
echo "Deploy already queued or running for commit $MERGE_SHA. Skipping manual dispatch."
exit 0
fi
fi
echo "Dispatching deploy.yml for main..."
gh workflow run deploy.yml --ref main
exit 0
fi
sleep 5
done
echo "PR #$PR_NUMBER was not merged within 180s."