forked from Ports/goldeneye-pc-port
Deck/Windows playtest fixes: fog rework (D540) and RSP-style CPU near-plane clip with per-vertex N64 fog (D543), with fog recomputed on near-plane-clipped vertices as the RSP does, so the haze around the player stays steady (D553); colour-combiner clamp for banded walls (D548); rocket and thrown-item stack overflows (D545, D549); menu double-trigger mitigation (D541); Dam ending first-shot camera swivel restored by removing an old port look-at workaround (D552); settings standardisation (D546), live All unlocked (D547), F10 mouse (D544); static window title and the project mark (the in-game crosshair shape, site gold) as window/taskbar icon and favicon (D550). Reference frames re-based on Windows, Linux and Steam Deck (A/B-confirmed); capture_p7.sh runs on Windows. Opt-in update check, off by default (D551: one HTTPS request to GitHub per launch when enabled; winhttp.dll loaded only then; the tag CI job checks the tag against GE007_VERSION). Release notes, ROADMAP, internals and security docs updated; tree-hygiene pass. Refs: D540 D541 D543 D544 D545 D546 D547 D548 D549 D550 D551 D552 D553 D554 D555 D556 D557 D558 D559 D560 D561 D562 D563 D564
201 lines
8.8 KiB
YAML
201 lines
8.8 KiB
YAML
# Self-hosted runtime (ROM-gated)
|
|
#
|
|
# Runs the build + the 21-level headless sweep (tools_pc/verify.sh) on the
|
|
# self-hosted runner, the one machine that has a legal ROM.
|
|
# This is the job class ci.yml explicitly does NOT do: CI is build-only, no
|
|
# ROM, so runtime checks live here. Setup + rollout plan (gitignored, local):
|
|
# docs/dev/notes/SELFHOSTED-RUNNER-PLAN.md
|
|
#
|
|
# Persistence model — everything lives under the runner's own paths, managed
|
|
# by the pipeline; no hand-maintained directories:
|
|
# - ccache -> actions/cache (stored by the runner under _work/_temp)
|
|
# - ROM -> copied every run from a seed dir on the runner:
|
|
# {runner_home}/_rom-seed/, or the repo variable
|
|
# GE_ROM_SEED_DIR. Bootstrap: put ge007.ntsc-final.z64,
|
|
# pcmodels-ntsc-final/ and pccg-ntsc-final/ there once.
|
|
# Integrity is re-verified every run against the committed
|
|
# ge007.u.sha1 (repo-tracked US ROM hash).
|
|
# The ROM is deliberately NOT stored in actions/cache (it
|
|
# used to be, key ge-rom-data-ntsc-v1). Cache entries live on
|
|
# GitHub's side, and pull_request workflows, including ci.yml
|
|
# runs from fork PRs, can restore caches created on the base
|
|
# branch, so a PR could read the ROM out of the cache. (The
|
|
# old save never actually persisted on this box, so no entry
|
|
# was ever created; checked 2026-09-30.)
|
|
# - MSYS2 -> machine-level install at C:\msys64 (verify.sh hardcodes the
|
|
# prefix), installed and its packages maintained BY HAND on
|
|
# the box. msys2/setup-msys2 still runs every job (it's what
|
|
# registers the `msys2 {0}` shell wrapper), but with
|
|
# `release: false` and update/install both omitted — verified
|
|
# against the action's source to do no pacman -Syu / pacman -S
|
|
# / network round-trip, just a fast reuse of the existing
|
|
# C:\msys64. Package presence is checked (not installed) by
|
|
# tools_pc/check-selfhosted-deps.sh, which fails fast with
|
|
# exact install instructions if something's missing.
|
|
#
|
|
# Trigger gating (non-negotiable):
|
|
# - workflow_dispatch ONLY for now. Flip on `push: branches: [main]` once
|
|
# the first manual runs are reliably green.
|
|
# - NEVER add `pull_request`: unreviewed fork code must not be routable to
|
|
# a runner that can see the ROM.
|
|
#
|
|
# Leak rules (docs/dev/notes/AGENT-VM-FORGEJO-PLAN.md checklist):
|
|
# - The ROM + sidecars live only in the runner's local seed dir (never in
|
|
# actions/cache); nothing in this workflow uploads from data/.
|
|
# - Artifacts here are console logs + a JSON verdict only. Rendered frames
|
|
# (ppm/) are ROM-derived content and are NOT uploaded.
|
|
|
|
name: Self-hosted runtime
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
levels:
|
|
description: "Sweep subset, SWEEP_LEVELS format (blank = all 21)"
|
|
required: false
|
|
default: ""
|
|
type: string
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
# One ROM job at a time per ref; never cancel an in-flight sweep out from
|
|
# under itself (the sweep is serialized by its own lockdir + watchdog). Group
|
|
# + cancel-in-progress:false is what actually serializes runs — GitHub
|
|
# Actions' workflow-level `concurrency` has no `max-parallel` key (that only
|
|
# exists under a job's `strategy:` for matrix jobs); a stray one here is a
|
|
# hard parse error that blocks every dispatch.
|
|
concurrency:
|
|
group: selfhosted-${{ github.ref }}
|
|
cancel-in-progress: false
|
|
|
|
env:
|
|
GE_ROM_SEED_DIR: ${{ vars.GE_ROM_SEED_DIR }}
|
|
|
|
jobs:
|
|
build-and-sweep:
|
|
runs-on: [ self-hosted, windows ]
|
|
defaults:
|
|
run:
|
|
shell: msys2 {0}
|
|
env:
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
CCACHE_MAXSIZE: 500M
|
|
steps:
|
|
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
persist-credentials: false
|
|
|
|
# `release: false` reuses the box's own C:\msys64 as-is instead of
|
|
# downloading/installing MSYS2 fresh (verified against the action's
|
|
# source: with this set, and update/install both omitted, it does
|
|
# exactly one `uname -a` + a pacman.conf sed tweak — no pacman -Syu,
|
|
# no pacman -S, no network round-trip). This step still has to run
|
|
# every time — it's what registers the `msys2 {0}` shell wrapper
|
|
# above — but does no install/update work.
|
|
- uses: msys2/setup-msys2@ec48f7c5447b3140e2b088413ae3a55687bccb6e # v2.33.0
|
|
with:
|
|
msystem: MINGW64
|
|
release: false
|
|
|
|
# Packages themselves are hand-maintained on the box (never installed
|
|
# by this workflow) — fails fast with exact install instructions if
|
|
# one's missing, instead of a confusing failure deeper in the build.
|
|
- name: Check self-hosted dependencies
|
|
run: tools_pc/check-selfhosted-deps.sh
|
|
|
|
# --- ROM + sidecars: runner-local seed dir (never actions/cache) ------
|
|
# {runner_home} = two levels up from RUNNER_WORKSPACE ({home}/_work/{repo}).
|
|
# The seed dir is the only copy the workflow uses; it is never deleted.
|
|
- name: Stage ROM + sidecars (runner seed dir)
|
|
run: |
|
|
seed="${GE_ROM_SEED_DIR:-$(dirname "$(dirname "$RUNNER_WORKSPACE")")/_rom-seed}"
|
|
for f in ge007.ntsc-final.z64 pcmodels-ntsc-final pccg-ntsc-final; do
|
|
[ -e "$seed/$f" ] || {
|
|
echo "missing $seed/$f" >&2
|
|
echo "One-time bootstrap: copy ge007.ntsc-final.z64, pcmodels-ntsc-final/" >&2
|
|
echo "and pccg-ntsc-final/ into $seed (or set the repo variable GE_ROM_SEED_DIR)." >&2
|
|
exit 2
|
|
}
|
|
done
|
|
mkdir -p data
|
|
# -r: pcmodels-*/pccg-* are directories, not single files (found by
|
|
# testing — a plain cp failed with "omitting directory").
|
|
cp -r "$seed/ge007.ntsc-final.z64" "$seed/pcmodels-ntsc-final" \
|
|
"$seed/pccg-ntsc-final" data/
|
|
echo "staged ROM store from $seed"
|
|
|
|
- name: Verify ROM integrity (committed US sha1)
|
|
run: |
|
|
cd data
|
|
want="$(cut -d' ' -f1 ../ge007.u.sha1)"
|
|
got="$(sha1sum ge007.ntsc-final.z64 | cut -d' ' -f1)"
|
|
[ "$want" = "$got" ] || { echo "ROM hash mismatch: want $want got $got" >&2; exit 2; }
|
|
echo "ROM sha1 ok: $got"
|
|
|
|
# --- build -------------------------------------------------------------
|
|
# Same opt-in ccache CMakeLists.txt wires up (speed-ups plan Step 5);
|
|
# persisted via actions/cache like ci.yml's windows-build.
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
|
with:
|
|
path: ${{ env.CCACHE_DIR }}
|
|
key: ccache-selfhosted-${{ hashFiles('CMakeLists.txt') }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-selfhosted-${{ hashFiles('CMakeLists.txt') }}-
|
|
ccache-selfhosted-
|
|
|
|
- name: Regenerate ROM-asset symbol file (US)
|
|
run: python scripts/gen_romassets.py u
|
|
|
|
- name: Build (ntsc-final)
|
|
run: ./build-pc.sh ntsc-final
|
|
|
|
# verify.sh: build-if-stale -> headless GE_PCDUMP per level -> crash
|
|
# symbolication -> pixcount -> framediff vs tools_pc/golden/. Serialized
|
|
# + watchdog + one retry by design (D117: not frame-deterministic).
|
|
- name: Level sweep (all 21, or dispatch subset)
|
|
env:
|
|
INPUT_LEVELS: ${{ inputs.levels }}
|
|
run: |
|
|
if [ -n "$INPUT_LEVELS" ]; then
|
|
if ! printf '%s' "$INPUT_LEVELS" | grep -Eq '^[A-Za-z0-9]+:[0-9]+( [A-Za-z0-9]+:[0-9]+)*$'; then
|
|
echo "levels input must be space-separated Name:NN entries, e.g. 'Dam:33 Facility:34'" >&2
|
|
exit 1
|
|
fi
|
|
export SWEEP_LEVELS="$INPUT_LEVELS"
|
|
fi
|
|
tools_pc/verify.sh sweep --json > sweep.log 2>&1
|
|
rc=$?
|
|
cat sweep.log
|
|
exit $rc
|
|
|
|
# Pull the JSON verdict array out of the mixed stdout for easy review.
|
|
- name: Extract JSON verdict
|
|
if: always()
|
|
run: |
|
|
python -c "
|
|
import json, sys
|
|
s = open('sweep.log', encoding='utf-8', errors='replace').read()
|
|
i = s.rfind('[{')
|
|
out = '[]'
|
|
if i >= 0:
|
|
d, _ = json.JSONDecoder().raw_decode(s[i:])
|
|
out = json.dumps(d, indent=1)
|
|
open('sweep-verdict.json', 'w').write(out + '\n')
|
|
print(out)
|
|
"
|
|
|
|
# Console logs + verdict only — no frames, no ROM bytes (leak rules).
|
|
- name: Upload sweep report
|
|
if: always()
|
|
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: selfhosted-sweep-${{ github.run_number }}
|
|
path: |
|
|
sweep.log
|
|
sweep-verdict.json
|
|
ge007.crash.log
|
|
if-no-files-found: ignore
|
|
retention-days: 14
|