Files

206 lines
8.3 KiB
YAML

name: Deploy to GitHub Pages
# Builds the site and publishes it to GitHub Pages on push to main.
# Requires Pages → Source = "GitHub Actions" (set up once via
# gh api --method POST repos/KytyPS5/kytyps5.github.io/pages -f build_type=workflow
# or the repo's Settings → Pages tab).
#
# Two deploy paths keep merged compatibility reports cheap:
# - Content-only: a push touching ONLY src/content/compat/**, public/screenshots/**
# or public/data/** (i.e. a merged report PR). The previous build is restored
# from the Actions cache, the compat JSONs + sitemap are regenerated from the
# reports and overlaid, and the artifact is republished — no npm ci, no tsc,
# no vite build. The site fetches data/compat-index.json + per-game
# data/compat/<KEY>.json at runtime, so a new report goes live ~a minute
# after the merge.
# - Full build: anything else (code, scripts, games database, …). npm ci +
# build as before, and the dist is cached for the next content-only deploy.
# Also the fallback when a content-only deploy finds no cached build (first
# deploy, or the cache expired).
# Pushes touching only docs/, the README, LICENSE, .gitignore or workflow files
# skip the deploy entirely (paths-ignore below).
on:
push:
branches: [main]
paths-ignore:
- "docs/**"
- "README.md"
- "LICENSE"
- ".gitignore"
- ".github/**"
workflow_dispatch:
inputs:
force_full:
description: "Force full build (bypass content-only optimization)"
type: boolean
default: false
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: true
jobs:
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Full history so the change scope can be diffed against the previous commit.
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 }}
FORCE_FULL: ${{ github.event.inputs.force_full }}
BEFORE_SHA: ${{ github.event.before }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
if [ "$FORCE_FULL" = "true" ]; then
echo "scope=full" >> "$GITHUB_OUTPUT"
exit 0
fi
CHANGED="$(git diff --name-only HEAD~1 HEAD 2>/dev/null || true)"
else
BASE="$BEFORE_SHA"
if [ -z "$BASE" ] || [ "$BASE" = "0000000000000000000000000000000000000000" ]; then
BASE="HEAD~1"
fi
CHANGED="$(git diff --name-only "$BASE" HEAD 2>/dev/null || true)"
fi
if [ -z "$CHANGED" ]; then
echo "scope=full" >> "$GITHUB_OUTPUT"
exit 0
fi
# Content-only = every changed file is a compat report, a screenshot
# or generated data. Anything else (code, scripts, games.json, …)
# needs the full build.
if printf '%s\n' "$CHANGED" | grep -vqE '^(src/content/compat/|public/screenshots/|public/data/)'; then
echo "scope=full" >> "$GITHUB_OUTPUT"
else
echo "scope=content" >> "$GITHUB_OUTPUT"
fi
# ---- Content-only path: restore the last full build from the cache ----
- name: Restore previous build (content-only)
if: steps.scope.outputs.scope == 'content'
uses: actions/cache/restore@v4
with:
path: dist
key: dist-${{ github.sha }}
restore-keys: dist-
- name: Check restored build
id: dist-check
if: steps.scope.outputs.scope == 'content'
shell: bash
run: |
if [ ! -f dist/index.html ] || [ ! -f dist/.built-commit ]; then
echo "Cached build missing index.html or .built-commit (legacy/missing cache); falling back to full build."
echo "restored=false" >> "$GITHUB_OUTPUT"
exit 0
fi
CACHED_SHA="$(tr -d '[:space:]' < dist/.built-commit)"
if [ -z "$CACHED_SHA" ]; then
echo "Cached .built-commit is empty; falling back to full build."
echo "restored=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if ! git cat-file -e "${CACHED_SHA}^{commit}" 2>/dev/null; then
echo "Cached commit $CACHED_SHA not found in git history; falling back to full build."
echo "restored=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if ! git merge-base --is-ancestor "$CACHED_SHA" HEAD 2>/dev/null; then
echo "Cached commit $CACHED_SHA is not an ancestor of HEAD; falling back to full build."
echo "restored=false" >> "$GITHUB_OUTPUT"
exit 0
fi
DIFF="$(git diff --name-only "$CACHED_SHA" HEAD 2>/dev/null || true)"
if [ -n "$DIFF" ] && printf '%s\n' "$DIFF" | grep -vqE '^(src/content/compat/|public/screenshots/|public/data/)'; then
echo "Code changes detected between cached commit $CACHED_SHA and HEAD; triggering full build."
echo "restored=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "Restored cache at commit $CACHED_SHA is valid ancestor with only content changes."
echo "restored=true" >> "$GITHUB_OUTPUT"
# ---- Full build (also the fallback when the content-only cache missed) ----
- name: Install dependencies
if: |
steps.scope.outputs.scope == 'full' ||
(steps.scope.outputs.scope == 'content' && steps.dist-check.outputs.restored == 'false')
run: npm ci
# 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).
- name: Build
if: |
steps.scope.outputs.scope == 'full' ||
(steps.scope.outputs.scope == 'content' && steps.dist-check.outputs.restored == 'false')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_SHA: ${{ github.sha }}
run: |
npm run build
echo "$GITHUB_SHA" > dist/.built-commit
- name: Cache built site
if: |
steps.scope.outputs.scope == 'full' ||
(steps.scope.outputs.scope == 'content' && steps.dist-check.outputs.restored == 'false')
# A retried run of the same SHA may already have cached dist — that's fine.
continue-on-error: true
uses: actions/cache/save@v4
with:
path: dist
key: dist-${{ github.sha }}
# ---- Content-only: regenerate data from the merged reports and overlay ----
# These scripts are dependency-free (node builtins only), so the fast path
# needs no npm ci. Screenshots + the compat JSONs + sitemap are copied into
# the restored dist, which keeps everything else from the last full build.
- name: Regenerate and overlay content
if: steps.scope.outputs.scope == 'content' && steps.dist-check.outputs.restored == 'true'
shell: bash
env:
GITHUB_SHA: ${{ github.sha }}
run: |
node scripts/validate-compat.mjs
node scripts/export-compat-json.mjs
node scripts/export-site-compat-json.mjs
node scripts/generate-sitemap.mjs
rm -rf dist/data/compat dist/screenshots
mkdir -p dist/data/compat dist/screenshots
cp public/data/compatibility.json dist/data/compatibility.json
cp public/data/compat-index.json dist/data/compat-index.json
[ -f public/data/updates.json ] && cp public/data/updates.json dist/data/updates.json || true
cp -r public/data/compat/. dist/data/compat/
cp public/sitemap.xml dist/sitemap.xml
[ -d public/screenshots ] && cp -r public/screenshots/. dist/screenshots/ || true
echo "$GITHUB_SHA" > dist/.built-commit
echo "Content-only deploy: skipped npm ci / tsc / vite build."
- uses: actions/configure-pages@v6
- uses: actions/upload-pages-artifact@v5
with:
path: dist
- name: Deploy
id: deployment
uses: actions/deploy-pages@v5