mirror of
https://github.com/KytyPS5/kytyps5.github.io.git
synced 2026-10-09 08:49:37 -07:00
206 lines
8.3 KiB
YAML
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
|