Add the pinned libretro core build recipe - #18
Conversation
Implements the D-185 boundary as a runnable recipe: no emulator code enters this repository, and the operator fetches exactly the pinned bytes, verifies them, builds, and collects the cores with their licence text. scripts/pi/build-retro-cores.sh carries the provenance for the three selected cores -- repository, revision, archive SHA-256, byte length -- fetches each archive, refuses to continue on any digest or size mismatch, builds in a scratch directory that is removed on exit, and writes each core beside a concatenated THIRD_PARTY_NOTICES.txt containing every upstream licence in full. Verified: - Native build produces all three cores: fceumm, snes9x, genesis_plus_gx. - Cross build with --cross-aarch64 produces real ARM aarch64 shared objects for fceumm and genesis-plus-gx. - A tampered pin is refused before any build runs. - All three licences are captured, 90 KB of notice text. snes9x needs zlib. It compiles -DUNZIP_SUPPORT, bundles no zlib of its own, and links -lz. The recipe probes for this by compiling and linking a tiny program against the toolchain that will actually build, rather than looking for /usr/include/zlib.h. That distinction matters: the first version of this check looked at the host header, which is present on this machine, so a cross build passed the check and then failed at the link step with `cannot find -lz`. The probe now reports the real reason and skips that core instead. Consequently the cross build covers two of three cores. A cross build of snes9x needs the arm64 zlib through dpkg multi-arch; a native build on the Pi needs only zlib1g-dev, which is the deployment path anyway. Deliberately not wired into continuous integration. The recipe depends on GitHub serving byte-identical archives for pinned revisions, which is an external dependency this repository does not control, and a required check that can fail for upstream reasons is the flake pattern D-187 was written to avoid. It can be added later as an opt-in workflow if drift detection is wanted. Nothing here is qualified. A built core is not evidence of playable emulation, audio correctness, controller behaviour, save durability, frame pacing, or thermal headroom on any target, and no core has been built on or run on a Raspberry Pi. RetroArch itself, signed package assembly, and the per-system game manifests remain unimplemented. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughChangesRetro core build
Estimated code review effort: 4 (Complex) | ~45 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/pi/build-retro-cores.sh`:
- Around line 111-113: Validate the `only` argument against the IDs defined in
`CORES` before entering the `for entry in "${CORES[@]}"` loop. If a non-empty
`only` value matches no core ID, print an error and exit nonzero; preserve the
existing filtering and build behavior for valid IDs and when `only` is unset.
- Around line 118-136: Update the dependency-check block around
have_zlib_for_target to dispatch based on the declared deps value, invoking the
zlib probe and zlib-specific guidance only when deps identifies zlib. Reject
unknown dependency names explicitly instead of applying the zlib check or
message to every non-empty dependency, while preserving skipped-count handling
for unavailable known dependencies.
- Around line 79-81: Extend the prerequisite loop in build-retro-cores.sh to
validate nproc, stat, and file alongside curl, tar, make, and sha256sum. Keep
the existing missing-prerequisite message and immediate exit behavior so the
script stops before using these commands.
- Around line 105-109: Update the notices-file handling around the loop counters
`built` and `skipped`: create and append to `THIRD_PARTY_NOTICES.txt` in a
scratch location rather than truncating the published file in `out_dir`, then
move it into `out_dir` only after the build loop completes successfully.
Preserve the existing failure exits so partial builds never expose a truncated
or incomplete notices file.
- Around line 138-149: Update the pinned archive fetch in the build flow to use
a Release asset or immutable mirror, while retaining strict SHA-256 and
byte-length checks for D-185. If the generated archive endpoint remains, extract
it and validate the tree and file modes against gitTree, reporting whether
upstream content or archive encoding changed. Add appropriate transfer limits to
the curl invocation.
- Around line 169-178: Update the build closure’s licence metadata to represent
licenceFile as a list of paths, including every component licence such as
FCEUmm’s src/ntsc/license.txt and Genesis Plus GX’s separate files. In the
notice-generation block around notices and source_root, iterate over all listed
licence paths and append each file’s contents, failing the build if any listed
path is missing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e42163ce-6ac8-4a0e-9f14-1cec5ec3b46c
📒 Files selected for processing (1)
scripts/pi/build-retro-cores.sh
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
- GitHub Check: node / ubuntu-latest
- GitHub Check: node / windows-latest
- GitHub Check: e2e / ubuntu-latest
- GitHub Check: native / windows-latest
- GitHub Check: native / aarch64 cross
🔇 Additional comments (5)
scripts/pi/build-retro-cores.sh (5)
27-45: LGTM!
86-95: LGTM!
97-103: LGTM!
1-23: LGTM!Also applies to: 182-189
156-167: 🗄️ Data Integrity & IntegrationDo not add this architecture check.
The pinned Linux build paths honor the exported cross compiler. The
CC = gccassignments are limited to SunOS branches, and Genesis Plus GX usesCC ?= gcc. The pinned source archives contain no duplicate artifacts. This failure mode is not present here.> Likely an incorrect or invalid review comment.
| for tool in curl tar make sha256sum; do | ||
| command -v "$tool" >/dev/null 2>&1 || { echo "Missing prerequisite: $tool" >&2; exit 1; } | ||
| done |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Add nproc, stat, and file to the prerequisite check.
Line 156 calls make -j"$(nproc)". If nproc is missing, the substitution is empty and make -j runs with no job limit. set -e does not stop this, because the subshell is the left operand of ||. On a small Pi this can exhaust memory during the build. stat -c%s (line 143) is required for the byte-length check, and file (line 167) provides the artifact type record.
🛡️ Proposed fix
-for tool in curl tar make sha256sum; do
+for tool in curl tar make sha256sum stat nproc file find; do
command -v "$tool" >/dev/null 2>&1 || { echo "Missing prerequisite: $tool" >&2; exit 1; }
done📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for tool in curl tar make sha256sum; do | |
| command -v "$tool" >/dev/null 2>&1 || { echo "Missing prerequisite: $tool" >&2; exit 1; } | |
| done | |
| for tool in curl tar make sha256sum stat nproc file find; do | |
| command -v "$tool" >/dev/null 2>&1 || { echo "Missing prerequisite: $tool" >&2; exit 1; } | |
| done |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/pi/build-retro-cores.sh` around lines 79 - 81, Extend the
prerequisite loop in build-retro-cores.sh to validate nproc, stat, and file
alongside curl, tar, make, and sha256sum. Keep the existing missing-prerequisite
message and immediate exit behavior so the script stops before using these
commands.
| mkdir -p "${out_dir}" | ||
| notices="${out_dir}/THIRD_PARTY_NOTICES.txt" | ||
| : > "${notices}" | ||
| built=0 | ||
| skipped=0 |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win
Publish the notices file only after the loop completes.
Line 107 truncates THIRD_PARTY_NOTICES.txt before the first build. Any exit 1 inside the loop, at lines 148, 160, or 164, leaves already-copied cores in out_dir beside a truncated or partial notices file. A consumer then reads a core with no licence text. Build the notices file in scratch, and move it into out_dir after the loop.
♻️ Proposed refactor
mkdir -p "${out_dir}"
-notices="${out_dir}/THIRD_PARTY_NOTICES.txt"
+notices="${scratch}/THIRD_PARTY_NOTICES.txt"
+final_notices="${out_dir}/THIRD_PARTY_NOTICES.txt"
: > "${notices}"Then after the loop:
+mv "${notices}" "${final_notices}"
+notices="${final_notices}"
+
echo
echo "built ${built} core(s), skipped ${skipped}, into ${out_dir}"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| mkdir -p "${out_dir}" | |
| notices="${out_dir}/THIRD_PARTY_NOTICES.txt" | |
| : > "${notices}" | |
| built=0 | |
| skipped=0 | |
| mkdir -p "${out_dir}" | |
| notices="${scratch}/THIRD_PARTY_NOTICES.txt" | |
| final_notices="${out_dir}/THIRD_PARTY_NOTICES.txt" | |
| : > "${notices}" | |
| built=0 | |
| skipped=0 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/pi/build-retro-cores.sh` around lines 105 - 109, Update the
notices-file handling around the loop counters `built` and `skipped`: create and
append to `THIRD_PARTY_NOTICES.txt` in a scratch location rather than truncating
the published file in `out_dir`, then move it into `out_dir` only after the
build loop completes successfully. Preserve the existing failure exits so
partial builds never expose a truncated or incomplete notices file.
| for entry in "${CORES[@]}"; do | ||
| IFS='|' read -r id repo rev sha bytes subdir makefile artifact licence deps <<<"${entry}" | ||
| [ -n "${only}" ] && [ "${only}" != "${id}" ] && continue |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Validate --only against the core ids.
If --only receives an id that is absent from CORES, the loop skips every entry. The script then prints "built 0 core(s), skipped 0" and exits 0. A typo is indistinguishable from success. Fail before the loop when the id is unknown.
🛡️ Proposed fix
+if [ -n "${only}" ]; then
+ match=0
+ for entry in "${CORES[@]}"; do
+ [ "${entry%%|*}" = "${only}" ] && match=1
+ done
+ [ "${match}" -eq 1 ] || { echo "unknown core id: ${only}" >&2; exit 2; }
+fi
+
for entry in "${CORES[@]}"; do📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for entry in "${CORES[@]}"; do | |
| IFS='|' read -r id repo rev sha bytes subdir makefile artifact licence deps <<<"${entry}" | |
| [ -n "${only}" ] && [ "${only}" != "${id}" ] && continue | |
| if [ -n "${only}" ]; then | |
| match=0 | |
| for entry in "${CORES[@]}"; do | |
| [ "${entry%%|*}" = "${only}" ] && match=1 | |
| done | |
| [ "${match}" -eq 1 ] || { echo "unknown core id: ${only}" >&2; exit 2; } | |
| fi | |
| for entry in "${CORES[@]}"; do | |
| IFS='|' read -r id repo rev sha bytes subdir makefile artifact licence deps <<<"${entry}" | |
| [ -n "${only}" ] && [ "${only}" != "${id}" ] && continue |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/pi/build-retro-cores.sh` around lines 111 - 113, Validate the `only`
argument against the IDs defined in `CORES` before entering the `for entry in
"${CORES[@]}"` loop. If a non-empty `only` value matches no core ID, print an
error and exit nonzero; preserve the existing filtering and build behavior for
valid IDs and when `only` is unset.
| if [ -n "${deps}" ] && ! have_zlib_for_target; then | ||
| # A missing development library is a declared dependency, not a build | ||
| # defect: snes9x compiles -DUNZIP_SUPPORT, bundles no zlib, and links -lz. | ||
| # | ||
| # This is probed by compiling and linking against the active toolchain | ||
| # rather than by looking for /usr/include/zlib.h. The host header can be | ||
| # present while the aarch64 target library is absent, which fails much | ||
| # later and far less clearly, at the link step. | ||
| if [ "${cross}" -eq 1 ]; then | ||
| echo " SKIP: ${deps} is not available for the aarch64 target." | ||
| echo " Cross builds need the arm64 zlib: dpkg --add-architecture arm64," | ||
| echo " then install zlib1g-dev:arm64. A native build on the Pi needs" | ||
| echo " only zlib1g-dev." | ||
| else | ||
| echo " SKIP: needs ${deps}. Install it and rerun." | ||
| fi | ||
| skipped=$((skipped + 1)) | ||
| continue | ||
| fi |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Bind the dependency probe to the declared dependency name.
The systemDeps column holds an arbitrary package name, but line 118 runs have_zlib_for_target for any non-empty value. The skip text at lines 127-132 also names zlib only. A future entry with a different dependency reports an incorrect diagnosis. Dispatch on the value instead, and reject unknown values.
♻️ Proposed refactor
- if [ -n "${deps}" ] && ! have_zlib_for_target; then
+ dep_ok=1
+ case "${deps}" in
+ "") ;;
+ zlib1g-dev) have_zlib_for_target || dep_ok=0 ;;
+ *) echo " unhandled declared dependency: ${deps}" >&2; exit 1 ;;
+ esac
+ if [ "${dep_ok}" -eq 0 ]; then📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if [ -n "${deps}" ] && ! have_zlib_for_target; then | |
| # A missing development library is a declared dependency, not a build | |
| # defect: snes9x compiles -DUNZIP_SUPPORT, bundles no zlib, and links -lz. | |
| # | |
| # This is probed by compiling and linking against the active toolchain | |
| # rather than by looking for /usr/include/zlib.h. The host header can be | |
| # present while the aarch64 target library is absent, which fails much | |
| # later and far less clearly, at the link step. | |
| if [ "${cross}" -eq 1 ]; then | |
| echo " SKIP: ${deps} is not available for the aarch64 target." | |
| echo " Cross builds need the arm64 zlib: dpkg --add-architecture arm64," | |
| echo " then install zlib1g-dev:arm64. A native build on the Pi needs" | |
| echo " only zlib1g-dev." | |
| else | |
| echo " SKIP: needs ${deps}. Install it and rerun." | |
| fi | |
| skipped=$((skipped + 1)) | |
| continue | |
| fi | |
| dep_ok=1 | |
| case "${deps}" in | |
| "") ;; | |
| zlib1g-dev) have_zlib_for_target || dep_ok=0 ;; | |
| *) echo " unhandled declared dependency: ${deps}" >&2; exit 1 ;; | |
| esac | |
| if [ "${dep_ok}" -eq 0 ]; then | |
| # A missing development library is a declared dependency, not a build | |
| # defect: snes9x compiles -DUNZIP_SUPPORT, bundles no zlib, and links -lz. | |
| # | |
| # This is probed by compiling and linking against the active toolchain | |
| # rather than by looking for /usr/include/zlib.h. The host header can be | |
| # present while the aarch64 target library is absent, which fails much | |
| # later and far less clearly, at the link step. | |
| if [ "${cross}" -eq 1 ]; then | |
| echo " SKIP: ${deps} is not available for the aarch64 target." | |
| echo " Cross builds need the arm64 zlib: dpkg --add-architecture arm64," | |
| echo " then install zlib1g-dev:arm64. A native build on the Pi needs" | |
| echo " only zlib1g-dev." | |
| else | |
| echo " SKIP: needs ${deps}. Install it and rerun." | |
| fi | |
| skipped=$((skipped + 1)) | |
| continue | |
| fi |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/pi/build-retro-cores.sh` around lines 118 - 136, Update the
dependency-check block around have_zlib_for_target to dispatch based on the
declared deps value, invoking the zlib probe and zlib-specific guidance only
when deps identifies zlib. Reject unknown dependency names explicitly instead of
applying the zlib check or message to every non-empty dependency, while
preserving skipped-count handling for unavailable known dependencies.
| archive="${scratch}/${id}.tar.gz" | ||
| echo " fetching pinned revision ${rev}" | ||
| curl -fsSL "${repo}/archive/${rev}.tar.gz" -o "${archive}" | ||
|
|
||
| actual_sha="$(sha256sum "${archive}" | cut -d' ' -f1)" | ||
| actual_bytes="$(stat -c%s "${archive}")" | ||
| if [ "${actual_sha}" != "${sha}" ] || [ "${actual_bytes}" != "${bytes}" ]; then | ||
| echo " REFUSING TO BUILD: pinned archive digest does not match." >&2 | ||
| echo " expected ${sha} (${bytes} bytes)" >&2 | ||
| echo " actual ${actual_sha} (${actual_bytes} bytes)" >&2 | ||
| exit 1 | ||
| fi |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🌐 Web query:
Does GitHub guarantee stable checksums for /archive/<commit>.tar.gz source archives, and what is the recommended way to pin a source archive digest?
💡 Result:
No, GitHub does not guarantee stable checksums for its automatically generated source archives (e.g.,.tar.gz or.zip files downloaded via /archive/ endpoints) [1][2]. These archives are generated on demand using the git archive command [2][3]. Because GitHub may update the underlying Git version or its configuration (such as compression settings), the byte layout of these archives can change even when the repository contents remain identical [4][2]. Consequently, the cryptographic hash (e.g., SHA256) of these archives is not guaranteed to remain stable over time [1][4]. Recommended ways to ensure reproducibility and security: 1. Use GitHub Releases for stable archives: The recommended method for projects requiring stable, reproducible checksums is to upload pre-generated source archives to GitHub Releases as release assets [1][2]. These files are stored as-is and are guaranteed to remain byte-for-byte identical [1][5]. 2. Use commit IDs for internal consistency: If you must use automatically generated archives and only require that the file contents inside the archive remain identical (rather than the entire compressed file's checksum), use a specific commit ID in the request [2][3]. GitHub guarantees that an archive generated for a specific commit ID will always contain the same files, regardless of tag or branch movement [2][6]. 3. Mirroring: For build systems or package managers (such as Bazel) that strictly require pinned checksums, it is common practice to mirror these archives in a location where the owner can guarantee immutability, rather than relying on GitHub's on-demand generation [7][8].
Citations:
- 1: https://github.blog/changelog/2023-01-30-git-archive-checksums-may-change/
- 2: https://github.com/github/docs/blob/main/content/repositories/working-with-files/using-files/downloading-source-code-archives.md
- 3: https://docs.github.com/en/repositories/working-with-files/using-files/downloading-source-code-archives
- 4: https://github.blog/open-source/git/update-on-the-future-stability-of-source-code-archives-and-hashes/
- 5: Clarification about reproducibility of release tarballs github/docs#31141
- 6: https://help.github.com/en/enterprise-server@3.11/repositories/working-with-files/using-files/downloading-source-code-archives
- 7: Warn user when using unstable GitHub archive URL bazelbuild/bazel#15128
- 8: https://blog.bazel.build/2023/02/15/github-archive-checksum.html
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- target script ---'
cat -n scripts/pi/build-retro-cores.sh | sed -n '110,165p'
printf '%s\n' '--- compliance record ---'
wc -l compliance/retro-cores/libretro-2048.candidate.json
cat -n compliance/retro-cores/libretro-2048.candidate.json | sed -n '1,220p'
printf '%s\n' '--- related archive and digest logic ---'
rg -n -C 3 'gitTree|git-blob-oid-and-tar-mode-comparison|sha256sum|actual_bytes|curl .*archive|D-185' scripts complianceRepository: Randroids-Dojo/VCG-Console
Length of output: 19788
🏁 Script executed:
#!/bin/bash
set -e
printf '%s\n' '--- script header and core pins ---'
cat -n scripts/pi/build-retro-cores.sh | sed -n '1,110p'
printf '%s\n' '--- repository documentation for D-185 and source provenance ---'
rg -n -C 4 'D-185|archive SHA|sourceArchiveSha256|sourceArchiveByteLength|gitTree|matched-pinned-git-tree|source candidate' README.md docs compliance scripts .github 2>/dev/null || true
printf '%s\n' '--- all compliance source records ---'
rg -l '"gitTree"|"sourceArchiveSha256"' compliance/retro-cores | sortRepository: Randroids-Dojo/VCG-Console
Length of output: 44049
Pin an immutable source archive
GitHub does not guarantee stable checksums for /archive/<rev>.tar.gz. A future encoding change can make all three core builds fail even when the commit contents are unchanged. This failure does not prove tampering.
Use a Release asset or immutable mirror, and keep the archive SHA-256 and byte length strict for D-185. If the generated archive endpoint remains, verify the extracted tree and modes against gitTree, and report either changed upstream content or changed archive encoding. Add transfer limits to curl.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/pi/build-retro-cores.sh` around lines 138 - 149, Update the pinned
archive fetch in the build flow to use a Release asset or immutable mirror,
while retaining strict SHA-256 and byte-length checks for D-185. If the
generated archive endpoint remains, extract it and validate the tree and file
modes against gitTree, reporting whether upstream content or archive encoding
changed. Add appropriate transfer limits to the curl invocation.
| { | ||
| echo "===============================================================================" | ||
| echo "${id} -- ${repo}" | ||
| echo "pinned revision: ${rev}" | ||
| echo "source archive SHA-256: ${sha}" | ||
| echo "===============================================================================" | ||
| echo | ||
| cat "${source_root}/${licence}" | ||
| echo | ||
| } >> "${notices}" |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: List licence-bearing files in each pinned libretro core tree to find licences the recipe does not collect.
set -uo pipefail
list() { # owner/repo rev
echo "### $1 @ $2"
curl -fsSL "https://api.github.com/repos/$1/git/trees/$2?recursive=1" \
| jq -r '.tree[].path' \
| rg -i '(^|/)(copying|licen[sc]e|licence|notice|authors|copyright)([^/]*)$' \
| sort
echo
}
list libretro/libretro-fceumm b5e3566515c27dc66c9c20572171673126532e06
list libretro/snes9x 21a66f3975388e15d5495d52422415f52c040f86
list libretro/Genesis-Plus-GX ca93fec870378f3bff65931bcd828d5e756cce75Repository: Randroids-Dojo/VCG-Console
Length of output: 521
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- script structure and relevant ranges ---'
sed -n '1,190p' scripts/pi/build-retro-cores.sh
printf '%s\n' '--- compliance record ---'
cat compliance/retro-cores/libretro-2048.candidate.json
printf '%s\n' '--- related licence metadata ---'
rg -n -i 'fceumm|licen[cs]|noticeObligation|non-commercial|attribution|Copying' \
scripts compliance README.md .github 2>/dev/null | head -200Repository: Randroids-Dojo/VCG-Console
Length of output: 32570
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- repository files related to retro cores ---'
git ls-files | rg '(^|/)(build-retro-cores|retro-cores|fceumm|snes9x|Genesis)' || true
printf '%s\n' '--- all references to the pinned revisions and notice output ---'
rg -n 'b5e3566515c27dc66c9c20572171673126532e06|21a66f3975388e15d5495d52422415f52c040f86|ca93fec870378f3bff65931bcd828d5e756cce75|notices|licence' . --glob '!*.lock' | head -300Repository: Randroids-Dojo/VCG-Console
Length of output: 25597
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import json, pathlib
p = pathlib.Path("compliance/retro-cores/libretro-2048.candidate.json")
data = json.loads(p.read_text())
def walk(x, path=""):
if isinstance(x, dict):
for k, v in x.items():
if any(term in k.lower() for term in ("license", "notice", "attribution", "component", "copyright")):
print(f"{path}/{k}: {json.dumps(v, ensure_ascii=False)}")
walk(v, f"{path}/{k}")
elif isinstance(x, list):
for i, v in enumerate(x):
walk(v, f"{path}[{i}]")
walk(data)
PY
printf '%s\n' '--- script diff/status ---'
git diff --stat
git status --shortRepository: Randroids-Dojo/VCG-Console
Length of output: 791
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- remote metadata through git ls-remote (read-only) ---'
for spec in \
'libretro/libretro-fceumm b5e3566515c27dc66c9c20572171673126532e06' \
'libretro/snes9x 21a66f3975388e15d5495d52422415f52c040f86' \
'libretro/Genesis-Plus-GX ca93fec870378f3bff65931bcd828d5e756cce75'
do
set -- $spec
printf '### %s @ %s\n' "$1" "$2"
git ls-remote "https://github.com/$1.git" "$2" || true
done
printf '%s\n' '--- public raw-file probes for likely licence paths ---'
for url in \
'https://raw.githubusercontent.com/libretro/libretro-fceumm/b5e3566515c27dc66c9c20572171673126532e06/Copying' \
'https://raw.githubusercontent.com/libretro/libretro-fceumm/b5e3566515c27dc66c9c20572171673126532e06/LICENSE' \
'https://raw.githubusercontent.com/libretro/libretro-fceumm/b5e3566515c27dc66c9c20572171673126532e06/licence.txt'
do
code=$(curl -L -sS -o /tmp/probe -w '%{http_code}' "$url")
printf '%s %s\n' "$code" "$url"
doneRepository: Randroids-Dojo/VCG-Console
Length of output: 825
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- script lines around setup, table, and notice generation ---'
nl -ba scripts/pi/build-retro-cores.sh | sed -n '1,125p;145,185p'
printf '%s\n' '--- candidate file ---'
nl -ba compliance/retro-cores/libretro-2048.candidate.json | sed -n '1,220p'Repository: Randroids-Dojo/VCG-Console
Length of output: 268
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- exact component and licence-path references ---'
rg -n -i -C 3 'JMA|snes_ntsc|xBRZ|Nuked|libchdr|libFLAC|minimp3|Tremor|LZMA|NTSC filter|LGPL|GPLv3|Copying|LICENSE\.txt' \
docs scripts packages compliance --glob '!compliance/vcg-console.cdx.json' | head -300
printf '%s\n' '--- decision excerpts ---'
sed -n '188,200p' docs/DECISIONS.mdRepository: Randroids-Dojo/VCG-Console
Length of output: 50382
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import hashlib, io, re, tarfile, urllib.request
cores = [
("fceumm", "https://github.com/libretro/libretro-fceumm/archive/b5e3566515c27dc66c9c20572171673126532e06.tar.gz",
"1ef34c9ed324f91856d6eca7d923e4f0d33ce85acb4b6a9b62d4a56c46e5ddc1", "Copying"),
("snes9x", "https://github.com/libretro/snes9x/archive/21a66f3975388e15d5495d52422415f52c040f86.tar.gz",
"5146b4fbdb7062a3d93d5ced27c72d7c811f7278228353786619c0d591b73634", "LICENSE"),
("genesis-plus-gx", "https://github.com/libretro/Genesis-Plus-GX/archive/ca93fec870378f3bff65931bcd828d5e756cce75.tar.gz",
"9b00f9b29abb4e9fbe7be01b54e45bb8910935b62810c1d2f4a2b2138237650b", "LICENSE.txt"),
]
pat = re.compile(r"(^|/)(copying|license|licence|notice|copyright|authors)([^/]*)$", re.I)
for name, url, expected, configured in cores:
print(f"### {name}")
data = urllib.request.urlopen(url, timeout=30).read()
print("archive_sha256:", hashlib.sha256(data).hexdigest())
print("archive_bytes:", len(data))
with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as tf:
names = [m.name for m in tf.getmembers() if m.isfile()]
matches = sorted(n for n in names if pat.search(n))
print("configured:", configured)
print("licence_like_paths:")
for n in matches:
print(" ", n)
print("configured_present:", any(n.rsplit("/", 1)[-1] == configured for n in names))
print()
PYRepository: Randroids-Dojo/VCG-Console
Length of output: 2462
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
import hashlib, io, re, subprocess, tarfile
cores = [
("fceumm", "https://github.com/libretro/libretro-fceumm/archive/b5e3566515c27dc66c9c20572171673126532e06.tar.gz",
"1ef34c9ed324f91856d6eca7d923e4f0d33ce85acb4b6a9b62d4a56c46e5ddc1", "Copying"),
("snes9x", "https://github.com/libretro/snes9x/archive/21a66f3975388e15d5495d52422415f52c040f86.tar.gz",
"5146b4fbdb7062a3d93d5ced27c72d7c811f7278228353786619c0d591b73634", "LICENSE"),
("genesis-plus-gx", "https://github.com/libretro/Genesis-Plus-GX/archive/ca93fec870378f3bff65931bcd828d5e756cce75.tar.gz",
"9b00f9b29abb4e9fbe7be01b54e45bb8910935b62810c1d2f4a2b2138237650b", "LICENSE.txt"),
]
pat = re.compile(r"(^|/)(copying|license|licence|notice|copyright|authors)([^/]*)$", re.I)
for name, url, expected, configured in cores:
path = f"/tmp/{name}.tar.gz"
subprocess.run(["curl", "-k", "-fsSL", url, "-o", path], check=True)
data = open(path, "rb").read()
print(f"### {name}")
print("sha256:", hashlib.sha256(data).hexdigest(), "expected:", expected)
print("bytes:", len(data))
with tarfile.open(path, "r:gz") as tf:
files = [m.name for m in tf.getmembers() if m.isfile()]
matches = sorted(n for n in files if pat.search(n))
print("configured:", configured)
print("licence_like_paths:")
for n in matches:
print(" ", n)
print("configured_present:", any(n.rsplit("/", 1)[-1] == configured for n in files))
for member in matches:
if member.rsplit("/", 1)[-1].lower() in {"copying", "license", "license.txt"}:
text = tf.extractfile(member).read().decode("utf-8", "replace")
terms = [x for x in ("LGPL", "GPL", "Nuked", "snes_ntsc", "xBRZ", "libchdr", "libFLAC", "minimp3", "Tremor", "LZMA") if x.lower() in text.lower()]
print(" ", member, "component_terms:", terms)
print()
PYRepository: Randroids-Dojo/VCG-Console
Length of output: 3953
Collect all licence files for the built components.
The pinned FCEUmm archive contains src/ntsc/license.txt in addition to Copying, but line 176 appends only Copying. The Genesis Plus GX archive also contains separate component licence files. Represent licenceFile as a list for each build closure, and fail when a listed path is absent.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/pi/build-retro-cores.sh` around lines 169 - 178, Update the build
closure’s licence metadata to represent licenceFile as a list of paths,
including every component licence such as FCEUmm’s src/ntsc/license.txt and
Genesis Plus GX’s separate files. In the notice-generation block around notices
and source_root, iterate over all listed licence paths and append each file’s
contents, failing the build if any listed path is missing.
Implements the D-185 boundary as a runnable recipe. No emulator code enters this repository — the operator fetches exactly the pinned bytes, verifies them, builds, and collects the cores with their licence text.
scripts/pi/build-retro-cores.shcarries provenance for all three selected cores, refuses to continue on any digest or size mismatch, builds in a scratch directory removed on exit, and writes each core beside a concatenatedTHIRD_PARTY_NOTICES.txtwith every upstream licence in full.Verified
fceumm,snes9x,genesis_plus_gx--cross-aarch64ELF … ARM aarch64shared objectsThe zlib finding, and a bug I had to fix
snes9x compiles
-DUNZIP_SUPPORT, bundles no zlib, and links-lz.My first version of the dependency check looked for
/usr/include/zlib.h. That host header is present on this machine, so the cross build sailed past the check and then died at the link step withcannot find -lz— the wrong error, at the wrong time, blaming the wrong thing.The check now compiles and links a tiny program against the toolchain that will actually build, so it reports the real reason and skips cleanly:
So cross coverage is 2 of 3 by dependency, not by defect. A native Pi build needs only
zlib1g-dev— and native is the deployment path anyway.Not wired into CI, on purpose
The recipe depends on GitHub serving byte-identical archives for pinned revisions — an external dependency this repo does not control. A required check that can fail for upstream reasons is exactly the flake pattern D-187 was written to avoid. It can be added later as an opt-in
workflow_dispatchjob if drift detection is wanted.Scope
Nothing here is qualified. A built core is not evidence of playable emulation, audio, controller behaviour, save durability, frame pacing, or thermals — and nothing has been built on or run on a Raspberry Pi. RetroArch itself, signed package assembly, and per-system game manifests remain unimplemented.
🤖 Generated with Claude Code