mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-24 12:49:03 -05:00
generate_site.py writes the Starlight project in website/ and nothing else: mkdocs.yml, the MkDocs hooks, theme overrides and stylesheet go, and the wiki is written in Starlight's syntax. Both workflows set up Node with the official actions/setup-node pinned by commit, run npm ci from the committed lockfile, build with the strict link validator and check the result with validate_site.py. The search weighs a page by what it answers: Start here pages first, a system page's cards and file names before a guide citing them, deep guides, launchers, aliases and frozen copies after. Status badges, hash blocks and the export change lists leave the index. The ranking sits at the centre of a range where the 13 queries of search_gate.py all answer, in the search box itself. 839 pages, every URL and 45,611 ids match the MkDocs build. The tests read the Starlight content and syntax.
308 lines
11 KiB
YAML
308 lines
11 KiB
YAML
name: Validation
|
|
|
|
# The same checks on both roads into main. Work lands here by direct push as
|
|
# often as by pull request, and a test suite reachable only from a PR guards
|
|
# the road nobody takes.
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- "bios/**"
|
|
- "platforms/**"
|
|
- "emulators/**"
|
|
- "schemas/**"
|
|
- "scripts/**"
|
|
- "tests/**"
|
|
# The bootstraps pin install.py's SHA-256 and a test enforces the pin,
|
|
# so editing one without the other has to fail the PR.
|
|
- "install.py"
|
|
- "install.sh"
|
|
- "install.ps1"
|
|
# The handheld port bundles install.py and its tests read both.
|
|
- "ports/**"
|
|
# Every input of the site build, so a page that would fail the deploy
|
|
# fails here first (tests/test_workflow_paths.py).
|
|
- "wiki/**"
|
|
- "docs_assets/**"
|
|
- "provenance/**"
|
|
- "exports/**"
|
|
- "install/**"
|
|
- "database.json"
|
|
- "release.json"
|
|
- "derivable.json"
|
|
- "website/**"
|
|
# Spelled out twice because the workflow parser reads no YAML anchor. A test
|
|
# holds the two lists equal.
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- "bios/**"
|
|
- "platforms/**"
|
|
- "emulators/**"
|
|
- "schemas/**"
|
|
- "scripts/**"
|
|
- "tests/**"
|
|
- "install.py"
|
|
- "install.sh"
|
|
- "install.ps1"
|
|
- "ports/**"
|
|
- "wiki/**"
|
|
- "docs_assets/**"
|
|
- "provenance/**"
|
|
- "exports/**"
|
|
- "install/**"
|
|
- "database.json"
|
|
- "release.json"
|
|
- "derivable.json"
|
|
- "website/**"
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: write
|
|
|
|
concurrency:
|
|
# A push series collapses to the tip: what has to stay verified is the head
|
|
# of main, not every commit that passed under it.
|
|
group: validate-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
validate-bios:
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'pull_request'
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Install dependencies
|
|
run: pip install pyyaml
|
|
|
|
- name: Get changed BIOS files
|
|
id: changed
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
run: |
|
|
# NUL-separated and filtered to added/modified/renamed: a deleted
|
|
# file has no bytes left to hash, and a path with a space or a
|
|
# quote must survive the hand-off to validate_pr.py.
|
|
git diff --name-only --diff-filter=AMR -z "$BASE_SHA"..."$HEAD_SHA" \
|
|
-- bios/ > changed_files.zlist
|
|
|
|
# The report has to reach the pull request whatever the outcome, so the
|
|
# exit code is recorded here and acted on after the comment is posted.
|
|
# Swallowing it outright left the job green on a file that failed its
|
|
# hash check.
|
|
- name: Validate BIOS files
|
|
id: validate
|
|
run: |
|
|
if [ -s changed_files.zlist ]; then
|
|
set +e
|
|
xargs -0 python scripts/validate_pr.py --markdown -- \
|
|
< changed_files.zlist > report.md 2>&1
|
|
echo "rc=$?" >> "$GITHUB_OUTPUT"
|
|
set -e
|
|
else
|
|
echo "No BIOS files changed" > report.md
|
|
echo "rc=0" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
cat report.md
|
|
|
|
# The job summary is the report every contributor sees, fork or not:
|
|
# it needs no write token.
|
|
- name: Write the report to the job summary
|
|
if: always()
|
|
run: |
|
|
if [ -f report.md ]; then
|
|
cat report.md >> "$GITHUB_STEP_SUMMARY"
|
|
else
|
|
echo "The validation step stopped before writing a report." >> "$GITHUB_STEP_SUMMARY"
|
|
fi
|
|
|
|
# A pull request from a fork runs with a read-only GITHUB_TOKEN, so a
|
|
# comment would fail and turn a valid file red. The comment is posted
|
|
# only when the branch lives in this repository. pull_request_target is
|
|
# not an option: it hands a write token to a run that would read the
|
|
# contributor's files and scripts.
|
|
- name: Post validation report
|
|
if: always() && github.event.pull_request.head.repo.full_name == github.repository
|
|
run: |
|
|
gh pr comment "${{ github.event.pull_request.number }}" --body-file report.md
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
|
|
- name: Fail when a file did not validate
|
|
if: steps.validate.outputs.rc != '0'
|
|
run: |
|
|
echo "::error::validate_pr.py exited ${{ steps.validate.outputs.rc }}. The report is in the job summary of this run."
|
|
exit 1
|
|
|
|
validate-configs:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
|
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Install dependencies
|
|
run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0"
|
|
|
|
- name: Validate platform configs and emulator profiles
|
|
run: python scripts/validate_schemas.py --source-only
|
|
|
|
# A profile written from a clone can cite lines that do not hold the value
|
|
# it declares, and schemas and tests pass it. Each profile a change adds is
|
|
# confronted with its upstream, both roads into main. An edited profile is
|
|
# not held to it: the backlog of older refs is reviewed by hand.
|
|
check-new-profiles:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
with:
|
|
python-version: "3.12"
|
|
|
|
- name: Install dependencies
|
|
run: pip install pyyaml
|
|
|
|
- name: Confront added profiles with their upstream
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
BEFORE: ${{ github.event.before }}
|
|
run: |
|
|
# A pull request is checked out as a merge commit whose first
|
|
# parent is the base, while a push names the commit it moved main from.
|
|
if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
|
|
base=HEAD^1
|
|
elif [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ]; then
|
|
echo "first push of the branch: no base to diff against"
|
|
exit 0
|
|
else
|
|
base=$BEFORE
|
|
fi
|
|
python scripts/profile_sync.py --added --base "$base" --strict
|
|
|
|
run-tests:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
|
|
# install.py stays on Python 3.8: the suite runs it end to end under a
|
|
# real 3.8 interpreter, which import-only and grammar checks miss. 3.9
|
|
# is the floor pipeline.py states for the build scripts: a real 3.9
|
|
# imports every module under scripts/, so it needs their dependencies.
|
|
# jsonschema 4.26 requires 3.10, 4.25.1 is the last release for 3.9.
|
|
# The last version listed is the default python.
|
|
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7
|
|
with:
|
|
python-version: |
|
|
3.8
|
|
3.9
|
|
3.12
|
|
|
|
- name: Install the build scripts' dependencies for Python 3.9
|
|
run: python3.9 -m pip install pyyaml "jsonschema[format-nongpl]==4.25.1"
|
|
|
|
# markdown: the site validation test renders a wiki line to prove the
|
|
# heading guard's premise, and skipped on every run without it.
|
|
- name: Install dependencies
|
|
run: pip install pyyaml "jsonschema[format-nongpl]==4.26.0" "markdown==3.10.2"
|
|
|
|
# The site is built as deploy-site.yml builds it, from the lockfile.
|
|
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
|
|
with:
|
|
node-version: "24"
|
|
cache: npm
|
|
cache-dependency-path: website/package-lock.json
|
|
|
|
- run: npm ci --prefix website
|
|
|
|
# The exporter fidelity cases compare what we hand back against the
|
|
# file each platform publishes, so they need those files. They skip
|
|
# without them rather than fail, which is why an unreachable upstream
|
|
# must not stop the run: the rest of the suite still has to report.
|
|
- name: Fetch the platforms' own files
|
|
continue-on-error: true
|
|
run: python scripts/export_native.py --all --fetch --output-dir "$RUNNER_TEMP/native"
|
|
|
|
# A pack built without its data directories exits non-zero: it is not
|
|
# the pack a user gets. Tests that build packs need the same trees a
|
|
# release build has, as deploy-site.yml fetches them.
|
|
- name: Fetch the data directories
|
|
run: python scripts/refresh_data_dirs.py
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
# The pages are gitignored, and the tests that read them fail
|
|
# in CI without it. The suite runs whatever this step returned, so a
|
|
# generator that stops still leaves every other test reporting.
|
|
- name: Generate the site
|
|
run: python scripts/generate_site.py
|
|
|
|
- name: Run test suite
|
|
if: ${{ !cancelled() }}
|
|
# No test downloads a release asset: 252 MB files fetched into the
|
|
# shared cache filled the runner's disk and changed what later tests
|
|
# resolved. The download tests mock the network and clear it.
|
|
env:
|
|
RETROBIOS_OFFLINE: "1"
|
|
run: python -m unittest discover -s tests -t . -v
|
|
|
|
# deploy-site.yml runs this after the merge, and a dead link or a stray
|
|
# heading failed it there with nothing in front of it. Run whatever the
|
|
# suite returned, so one red step does not hide the other. The release
|
|
# assets are not restored: they change counts, not links or headings.
|
|
- name: Build the site
|
|
if: ${{ !cancelled() }}
|
|
run: |
|
|
npm run build --prefix website
|
|
python scripts/validate_site.py --site-dir website/dist
|
|
|
|
label-pr:
|
|
runs-on: ubuntu-latest
|
|
if: github.event_name == 'pull_request'
|
|
permissions:
|
|
pull-requests: write
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
with:
|
|
fetch-depth: 0
|
|
|
|
# Labels need a write token, which a pull request from a fork does not
|
|
# get. The step is skipped there instead of reporting a success that
|
|
# set nothing.
|
|
- name: Auto-label PR
|
|
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
run: |
|
|
labels=""
|
|
files=$(gh pr diff "${{ github.event.pull_request.number }}" --name-only)
|
|
|
|
if echo "$files" | grep -q '^bios/'; then
|
|
labels="$labels bios"
|
|
for sys in $(echo "$files" | grep '^bios/' | cut -d/ -f2 | sort -u); do
|
|
labels="$labels system:$(echo "$sys" | tr '[:upper:]' '[:lower:]')"
|
|
done
|
|
fi
|
|
if echo "$files" | grep -q '^platforms/'; then
|
|
labels="$labels platform-config"
|
|
fi
|
|
if echo "$files" | grep -q '^scripts/'; then
|
|
labels="$labels automation"
|
|
fi
|
|
|
|
for label in $labels; do
|
|
gh pr edit "${{ github.event.pull_request.number }}" --add-label "$label" \
|
|
|| echo "::warning::label $label was not added"
|
|
done
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|