mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
f92364a002
CI on a headless ubuntu-24.04-arm runner caught what a desktop Pi could not: the AppImage only started on a machine that already had a full desktop stack installed. Three distinct causes, all from bundling Debian's builds of libraries that Debian builds for a co-versioned system, which is the opposite of an AppImage's situation. 1. Hard-linked backends. Debian's libSDL2 lists libpulse, libasound, libX11 and libwayland-client as DT_NEEDED rather than dlopening them, so the loader demanded all four at startup; the CI job failed with "libpulse.so.0 => not found". Debian's OpenAL does the same through libsndio, which itself hard-links libasound. Built from source with --enable-*-shared and ALSOFT_DLOPEN, both dlopen their backends, so the image now runs on a Wayland-only session, a KMSDRM handheld with no X server, or a box with ALSA and no PulseAudio. 2. A stray link. Debian's libtheoradec is linked against libcairo, which drags in X11, xcb, fontconfig and freetype for a video decoder. --disable-examples leaves it needing only libogg. 3. SONAME collision with the host. OpenAL dlopens ALSA, ALSA's config loads its PulseAudio hook plugin, and that plugin pulls the host's libsndfile into the process. libsndfile links libogg, libvorbis and libmpg123 -- the same three we bundle -- and since the loader resolves a SONAME once per process it bound to our bullseye copies. A bullseye libmpg123 has no mpg123_info2 (added in 1.32), so the plugin failed to relocate, ALSA config collapsed, and the game ran with no audio device at all. Building them current means our copies satisfy the host's libsndfile instead of starving it. The general rule, now stated as an assertion instead of a comment: never bundle a library the host's own stack may also load unless ours is at least as new as theirs. build_appimage.sh fails if any shipped object hard-requires anything beyond glibc, libstdc++ and the font stack, and CI re-checks it on the extracted artifact. Host requirements drop from "a working desktop" to glibc 2.29+, libstdc++, libfreetype6 and zlib. Bundled libraries drop from 13 to 10: libcairo, libpixman and libsndio are gone entirely. Verified on a Raspberry Pi 5 (trixie, Wayland): boots, imports, plays, and audio works -- SDL 2.30 now picks the native Wayland backend rather than falling back to XWayland as bullseye's 2.0.14 did. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
512 lines
21 KiB
YAML
512 lines
21 KiB
YAML
name: ci
|
|
|
|
# The ROM-free test run (21-testing-and-ci §CI).
|
|
#
|
|
# CI has no ROM and never will: data/generated/ is produced by a SHA-1
|
|
# verified import of a cartridge dump, and no ROM bytes are ever committed.
|
|
# That is why the suite is tiered -- T1 (primitives), T2 (engine invariants)
|
|
# and T4 (mod SDK) run against the committed tests/fixture_data dataset, so
|
|
# they need no ROM, no display and no assets beyond what is in the repo.
|
|
# The T3 content tier asserts Pokemon Red facts; scripts/test.sh detects
|
|
# data/generated/ is absent and skips it rather than failing.
|
|
#
|
|
on:
|
|
push:
|
|
# Integration branch + release branch. PRs already run via pull_request
|
|
# (any base); this list is only for post-merge push runs.
|
|
branches: [dev, main]
|
|
pull_request:
|
|
|
|
# a force-push while CI is mid-run should cancel the stale run, not queue
|
|
concurrency:
|
|
group: ci-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
ios-changes:
|
|
name: detect iOS changes
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
changed: ${{ steps.paths.outputs.changed }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- id: paths
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
HEAD_SHA: ${{ github.sha }}
|
|
run: |
|
|
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -Eq '^(mobile/ios/|scripts/build_ios\.sh$)'; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
ios-build:
|
|
name: iOS build
|
|
needs: ios-changes
|
|
if: needs.ios-changes.outputs.changed == 'true'
|
|
runs-on: ${{ fromJSON(github.repository == 'bryanthaboi/gen1recomp' && '["self-hosted", "macOS"]' || '"macos-latest"') }}
|
|
outputs:
|
|
ipa_url: ${{ steps.upload-ipa.outputs.artifact-url }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: import signing certificate
|
|
if: github.repository == 'bryanthaboi/gen1recomp'
|
|
run: |
|
|
keychain_path="$RUNNER_TEMP/gen1recomp-ci-signing.keychain-db"
|
|
ci_dir="${POKEMON_CI_DIR:-$HOME/.config/pokemon-ci}"
|
|
p12="$ci_dir/signing.p12"
|
|
passfile="$ci_dir/signing.pass"
|
|
[ -f "$p12" ] && [ -f "$passfile" ] || exit 1
|
|
p12pw="$(cat "$passfile")"
|
|
kcpw="$(openssl rand -base64 24)"
|
|
echo "::add-mask::$kcpw"
|
|
security delete-keychain "$keychain_path" 2>/dev/null || true
|
|
security create-keychain -p "$kcpw" "$keychain_path"
|
|
security set-keychain-settings "$keychain_path"
|
|
security unlock-keychain -p "$kcpw" "$keychain_path"
|
|
security import "$p12" -P "$p12pw" -k "$keychain_path" -T /usr/bin/codesign -T /usr/bin/security
|
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$kcpw" "$keychain_path" >/dev/null
|
|
existing="$(security list-keychains -d user | sed -e 's/^[[:space:]]*//' -e 's/"//g')"
|
|
security list-keychains -d user -s "$keychain_path" $existing
|
|
- name: install xcbeautify
|
|
run: brew list xcbeautify >/dev/null 2>&1 || brew install xcbeautify
|
|
- name: build iOS release
|
|
env:
|
|
CANONICAL_REPOSITORY: ${{ github.repository == 'bryanthaboi/gen1recomp' }}
|
|
run: |
|
|
if [ "$CANONICAL_REPOSITORY" = true ]; then
|
|
scripts/build_ios.sh --fetch --device --release
|
|
else
|
|
scripts/build_ios.sh --fetch --release
|
|
fi
|
|
- name: upload iOS release artifact
|
|
id: upload-ipa
|
|
if: github.repository == 'bryanthaboi/gen1recomp'
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: gen1recomp-ios-ipa
|
|
path: dist/ios/gen1recomp.ipa
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
- name: clean up signing keychain
|
|
if: ${{ always() && github.repository == 'bryanthaboi/gen1recomp' }}
|
|
run: security delete-keychain "$RUNNER_TEMP/gen1recomp-ci-signing.keychain-db" 2>/dev/null || true
|
|
|
|
switch-changes:
|
|
name: detect Switch changes
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
changed: ${{ steps.paths.outputs.changed }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- id: paths
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
HEAD_SHA: ${{ github.sha }}
|
|
run: |
|
|
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -Eq '^(scripts/build_switch\.sh$|scripts/switch/|docs/switch-.*\.md$|tests/switch_ci_workflows_test\.lua$|tests/switch_transfer_docs_test\.lua$|\.github/workflows/(ci|release|switch-artifact-comment)\.yml$|src/core/(NxAssetOverlay|Platform|GameVersion)\.lua$|src/import/CacheFs\.lua$|tests/engine/(assets_version_fallback|nx_generated_guard|nx_yellow_boot|switch_diagnostics)_test\.lua$|tests/engine/platform_nx)'; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
switch-selftest:
|
|
name: Switch offline selftest
|
|
needs: switch-changes
|
|
if: needs.switch-changes.outputs.changed == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: install luajit
|
|
run: sudo apt-get update && sudo apt-get install -y luajit
|
|
- name: Switch offline selftest
|
|
run: bash scripts/switch/selftest_build_switch.sh
|
|
- name: verify_payload self-test
|
|
run: bash scripts/switch/verify_payload.sh --self-test
|
|
- name: Switch CI workflow content gate
|
|
run: luajit tests/switch_ci_workflows_test.lua
|
|
- name: Switch transfer docs content gate
|
|
run: luajit tests/switch_transfer_docs_test.lua
|
|
# NX runtime regressions gate this job via switch-changes; run the NX
|
|
# engine suites here too so a PR touching them gets feedback on the
|
|
# fork-safe ubuntu runner before the self-hosted Mac build.
|
|
- name: NX engine suites (headless)
|
|
run: |
|
|
luajit tests/engine/assets_version_fallback_test.lua
|
|
luajit tests/engine/nx_generated_guard_test.lua
|
|
luajit tests/engine/nx_yellow_boot_test.lua
|
|
|
|
switch-build:
|
|
name: Switch fused build
|
|
needs: [switch-changes, switch-selftest]
|
|
if: |
|
|
always()
|
|
&& needs.switch-changes.outputs.changed == 'true'
|
|
&& needs.switch-selftest.result == 'success'
|
|
&& github.repository == 'bryanthaboi/gen1recomp'
|
|
&& (github.event_name != 'pull_request'
|
|
|| github.event.pull_request.head.repo.full_name == github.repository)
|
|
runs-on: ["self-hosted", "macOS"]
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Build Switch fused NRO
|
|
run: |
|
|
set -euo pipefail
|
|
VER="$(printf '%s' "$GITHUB_SHA" | cut -c1-7)"
|
|
scripts/build_switch.sh --fetch --fused --version "$VER"
|
|
echo "SWITCH_VER=$VER" >> "$GITHUB_ENV"
|
|
- name: upload Switch NRO artifact
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: gen1recomp-switch-nro
|
|
path: |
|
|
dist/switch/gen1recomp-${{ env.SWITCH_VER }}-switch.nro
|
|
dist/switch/gen1recomp-${{ env.SWITCH_VER }}-switch.nro.sha256
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
xbox-uwp-changes:
|
|
name: detect Xbox UWP changes
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
changed: ${{ steps.paths.outputs.changed }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- id: paths
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
HEAD_SHA: ${{ github.sha }}
|
|
run: |
|
|
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -Eq '^(ports/uwp/|scripts/build_xbox_uwp\.sh$|scripts/xbox-uwp/|scripts/pack_love\.sh$|\.github/workflows/(ci|release)\.yml$|src/core/Platform\.lua$|src/import/(CacheFs|RomImporter)\.lua$|src/update/Check\.lua$|tests/engine/(platform_nx|uwp_native_picker)_test\.lua$|tests/rom_importer_double_pick_test\.lua$)'; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
xbox-uwp-selftest:
|
|
name: Xbox UWP offline selftest
|
|
needs: xbox-uwp-changes
|
|
if: needs.xbox-uwp-changes.outputs.changed == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Xbox UWP offline selftest
|
|
run: bash scripts/xbox-uwp/selftest_build_xbox_uwp.sh
|
|
- name: Build shared payload
|
|
run: |
|
|
scripts/pack_love.sh \
|
|
--output .bazinga/work/ci-game.love \
|
|
--listing .bazinga/work/ci-love-listing.txt \
|
|
--version 0.0.0
|
|
- name: Upload shared payload
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: gen1recomp-xbox-uwp-payload
|
|
path: .bazinga/work/ci-game.love
|
|
if-no-files-found: error
|
|
retention-days: 1
|
|
|
|
xbox-uwp-build:
|
|
name: Xbox UWP build
|
|
needs: [xbox-uwp-changes, xbox-uwp-selftest]
|
|
if: |
|
|
always()
|
|
&& needs.xbox-uwp-changes.outputs.changed == 'true'
|
|
&& needs.xbox-uwp-selftest.result == 'success'
|
|
runs-on: windows-2022
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Download shared payload
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: gen1recomp-xbox-uwp-payload
|
|
path: .bazinga/work
|
|
- name: Build Xbox UWP package
|
|
shell: bash
|
|
run: |
|
|
bash scripts/build_xbox_uwp.sh \
|
|
--release \
|
|
--version 0.0.0 \
|
|
--game-love .bazinga/work/ci-game.love
|
|
- name: Upload Xbox UWP package
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: gen1recomp-xbox-uwp
|
|
path: |
|
|
dist/xbox-uwp/gen1recomp-0.0.0-xbox-uwp.zip
|
|
dist/xbox-uwp/gen1recomp-0.0.0-xbox-uwp.zip.sha256
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
linux-arm64-changes:
|
|
name: detect Linux arm64 changes
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
changed: ${{ steps.paths.outputs.changed }}
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
with:
|
|
fetch-depth: 0
|
|
- id: paths
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
|
|
HEAD_SHA: ${{ github.sha }}
|
|
run: |
|
|
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "0000000000000000000000000000000000000000" ]; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" | grep -Eq '^(scripts/build_linux_arm64\.sh$|scripts/linux-arm64/|scripts/pack_love\.sh$|docs/linux-arm64-build\.md$|\.github/workflows/(ci|release)\.yml$)'; then
|
|
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
fi
|
|
|
|
linux-arm64-selftest:
|
|
name: Linux arm64 offline selftest
|
|
needs: linux-arm64-changes
|
|
if: needs.linux-arm64-changes.outputs.changed == 'true'
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
# Deliberately on x86_64: everything this gate checks (pins, the
|
|
# host-arch guard, the dependency exclude list, the AppRun fusion
|
|
# contract) is answerable without a container or an aarch64 machine,
|
|
# so the slow native job below only ever starts on a sane tree.
|
|
- name: Linux arm64 offline selftest
|
|
run: bash scripts/linux-arm64/selftest_build_linux_arm64.sh
|
|
|
|
linux-arm64-build:
|
|
name: Linux arm64 AppImage build
|
|
needs: [linux-arm64-changes, linux-arm64-selftest]
|
|
if: |
|
|
always()
|
|
&& needs.linux-arm64-changes.outputs.changed == 'true'
|
|
&& needs.linux-arm64-selftest.result == 'success'
|
|
# No fork restriction, unlike switch-build: this needs no secrets and no
|
|
# self-hosted hardware, just GitHub's free arm64 runner for public repos,
|
|
# so contributors get the same coverage on their own PRs.
|
|
runs-on: ubuntu-24.04-arm
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- name: Build the aarch64 AppImage
|
|
run: |
|
|
set -euo pipefail
|
|
scripts/build_linux_arm64.sh --version 0.0.0
|
|
- name: Verify the AppImage is self-contained and bullseye-compatible
|
|
run: |
|
|
set -euo pipefail
|
|
image="dist/linux-arm64/gen1recomp-0.0.0-linux-arm64.AppImage"
|
|
|
|
# --appimage-extract needs no FUSE, so this works on a runner
|
|
# without /dev/fuse and still exercises the real payload.
|
|
"$image" --appimage-extract >/dev/null
|
|
for required in AppRun bin/love game.love lib/liblove-11.5.so; do
|
|
[ -e "squashfs-root/$required" ] \
|
|
|| { echo "::error::AppImage is missing $required"; exit 1; }
|
|
done
|
|
|
|
# Every bundled object must resolve once AppRun's LD_LIBRARY_PATH is
|
|
# applied; an unresolved soname here is a user-visible launch crash.
|
|
#
|
|
# This runs on a HEADLESS runner on purpose, and that is the point.
|
|
# The first version of this build bundled Debian's SDL2, which
|
|
# hard-links libpulse/libasound/libX11/libwayland, so it only ever
|
|
# started on a full desktop -- a bare runner is what exposed it.
|
|
missing="$(LD_LIBRARY_PATH="$PWD/squashfs-root/lib" \
|
|
ldd squashfs-root/bin/love squashfs-root/lib/*.so* 2>/dev/null \
|
|
| grep 'not found' || true)"
|
|
[ -z "$missing" ] || { echo "::error::unresolved deps:"; echo "$missing"; exit 1; }
|
|
|
|
# Nothing may hard-link a driver, session or audio-stack library:
|
|
# those must be reached through dlopen so the AppImage runs on a box
|
|
# with only ALSA, only Wayland, or only KMSDRM.
|
|
linked="$(for f in squashfs-root/bin/love squashfs-root/lib/*.so*; do
|
|
objdump -p "$f" 2>/dev/null | awk '/NEEDED/{print $2}'
|
|
done | sort -u | grep -E '^lib(pulse|asound|X11|wayland|GL|EGL|drm|gbm|xcb|cairo|sndio|dbus)' || true)"
|
|
[ -z "$linked" ] \
|
|
|| { echo "::error::these must be dlopened, not linked:"; echo "$linked"; exit 1; }
|
|
|
|
# The whole point of compiling on bullseye. If a future change moves
|
|
# the builder to a newer base, the glibc floor silently rises and
|
|
# every user on an older distro gets "GLIBC_2.xx not found" -- catch
|
|
# it here instead of in a release.
|
|
floor="$(objdump -T squashfs-root/bin/love squashfs-root/lib/*.so* 2>/dev/null \
|
|
| grep -o 'GLIBC_[0-9.]*' | sort -V | tail -1)"
|
|
echo "highest required glibc symbol version: $floor"
|
|
[ -n "$floor" ] \
|
|
|| { echo "::error::found no versioned glibc symbols -- objdump read nothing"; exit 1; }
|
|
highest="$(printf '%s\n' "$floor" "GLIBC_2.31" | sort -V | tail -1)"
|
|
[ "$highest" = "GLIBC_2.31" ] \
|
|
|| { echo "::error::AppImage requires $floor, above the bullseye 2.31 floor"; exit 1; }
|
|
- name: Upload the AppImage
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: gen1recomp-linux-arm64
|
|
path: |
|
|
dist/linux-arm64/gen1recomp-0.0.0-linux-arm64.AppImage
|
|
dist/linux-arm64/gen1recomp-0.0.0-linux-arm64.AppImage.sha256
|
|
if-no-files-found: error
|
|
retention-days: 7
|
|
|
|
headless:
|
|
name: headless suites (no ROM)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
# LuaJIT, not lua5.4: LOVE 11.x embeds LuaJIT 2.1 and the engine is
|
|
# written to Lua 5.1 semantics, so CI must run the interpreter the
|
|
# game actually ships with or it would green-light 5.4-only syntax.
|
|
- name: install luajit
|
|
run: sudo apt-get update && sudo apt-get install -y luajit
|
|
|
|
- name: interpreter version
|
|
run: luajit -v
|
|
|
|
- name: run every ROM-free tier
|
|
run: ./scripts/test.sh
|
|
|
|
fixture-dataset:
|
|
name: fixture dataset integrity
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: sudo apt-get update && sudo apt-get install -y luajit
|
|
- run: python3 -m pip install --upgrade pillow
|
|
|
|
# the fixture PNGs are committed (they are 8x8 placeholders, not
|
|
# ROM-derived); assert they are still readable 4-shade images rather
|
|
# than regenerating them, so a corrupted commit is caught
|
|
- name: fixture assets are valid PNGs
|
|
run: |
|
|
python3 - <<'PY'
|
|
import glob, sys
|
|
from PIL import Image
|
|
paths = sorted(glob.glob("tests/fixture_data/assets/*.png"))
|
|
if not paths:
|
|
sys.exit("no fixture assets found")
|
|
for path in paths:
|
|
with Image.open(path) as image:
|
|
image.load()
|
|
print(f"ok {path} {image.size} {image.mode}")
|
|
print(f"\n{len(paths)} fixture assets valid")
|
|
PY
|
|
|
|
# the fingerprint golden is the parity tripwire; prove it still
|
|
# matches the dataset on a clean checkout
|
|
- name: fingerprint gate
|
|
run: luajit tests/engine/gate_fingerprint.lua
|
|
|
|
- name: parity-guarantee meta-test
|
|
run: luajit tests/engine/gate_meta_coverage.lua
|
|
|
|
# Only the differ is under test here, and the job is named for that. The
|
|
# capture half of the golden pipeline does not exist: a POKEPORT_DRIVER
|
|
# chunk runs after main.lua has already booted the game, and
|
|
# src/core/Data.lua has no POKEPORT_DATA_DIR branch, so no LOVE process
|
|
# can be pointed at tests/fixture_data. There is deliberately no step
|
|
# here that runs scripts/test.sh with WITH_SHOTS: it would have nothing
|
|
# to capture and nothing to diff, and a job that green-lights on skipped
|
|
# work is worse than an absent one.
|
|
shot-differ:
|
|
name: screenshot differ (capture not yet wired)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
- run: python3 -m pip install --upgrade pillow
|
|
|
|
# 21-testing-and-ci §"Testing & acceptance criteria": compare_shots
|
|
# flags a deliberately corrupted golden and passes the clean one.
|
|
# That is the half of the pipeline this repo can actually prove.
|
|
- name: compare_shots self-test
|
|
run: |
|
|
set -e
|
|
python3 - <<'PY'
|
|
import os
|
|
from PIL import Image
|
|
os.makedirs("/tmp/g", exist_ok=True)
|
|
os.makedirs("/tmp/s", exist_ok=True)
|
|
base = Image.new("RGB", (160, 144), (255, 255, 255))
|
|
for x in range(0, 160, 8):
|
|
for y in range(0, 144, 8):
|
|
base.putpixel((x, y), (0, 0, 0))
|
|
base.save("/tmp/g/clean.png")
|
|
base.save("/tmp/s/clean.png")
|
|
base.save("/tmp/g/broken.png")
|
|
bad = base.copy()
|
|
for x in range(40, 60):
|
|
for y in range(40, 60):
|
|
bad.putpixel((x, y), (255, 0, 0))
|
|
bad.save("/tmp/s/broken.png")
|
|
PY
|
|
if python3 tools/compare_shots.py /tmp/g /tmp/s; then
|
|
echo "compare_shots passed a corrupted golden -- differ is broken"
|
|
exit 1
|
|
fi
|
|
rm /tmp/g/broken.png /tmp/s/broken.png
|
|
python3 tools/compare_shots.py /tmp/g /tmp/s
|
|
|
|
# an empty golden directory must not read as success
|
|
- name: differ refuses to pass vacuously
|
|
run: |
|
|
set -e
|
|
mkdir -p /tmp/empty-goldens /tmp/empty-shots
|
|
if python3 tools/compare_shots.py /tmp/empty-goldens /tmp/empty-shots; then
|
|
echo "compare_shots passed with no goldens"
|
|
exit 1
|
|
fi
|
|
|
|
lint:
|
|
name: mod lint (no ROM-derived content)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v7
|
|
|
|
# the MK305 dump check key-diffs shipped tables through luajit, and
|
|
# modkit treats a missing interpreter as a fatal MK100 -- without
|
|
# this install the gate would fail instead of failing open
|
|
- run: sudo apt-get update && sudo apt-get install -y luajit
|
|
- run: python3 -m pip install --upgrade pillow
|
|
|
|
# constraint 1, enforced automatically: a committed mod that ships
|
|
# ROM-derived bytes fails the build. `lint` is the no-ROM-content
|
|
# check; `validate` is deliberately not run here because it resolves
|
|
# a mod against the fixture dataset, and a Red-content mod such as
|
|
# example_mew_starter legitimately does not resolve against it.
|
|
- name: lint every committed mod
|
|
run: |
|
|
set -e
|
|
found=0
|
|
for mod in mods/*/; do
|
|
[ -f "$mod/manifest.json" ] || continue
|
|
found=1
|
|
echo "== $mod"
|
|
python3 tools/modkit.py lint "${mod%/}"
|
|
done
|
|
if [ "$found" = "0" ]; then
|
|
echo "no committed mods to lint"
|
|
fi
|