mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-28 14:47:21 -05:00
docs: refresh installer and release pages
The installer output example showed 5865 files and no Not installable line, while the RetroArch manifest gives 8069 and omits 11. Exit code 2 also comes from every option argparse refuses. RETROBIOS_REF holds a file list only until the next release deletes its tag, and large files always come from large-files. The release commit now carries the manifests the pipeline regenerates. A RetroPie 4.8 image ships Python 3.7.3, below the bootstrap's floor, and the pages say what to use.
This commit is contained in:
1 parent
32bda05be3
commit
9edf785513
5 files changed
+139
-24
No files matched your search
@@ -78,6 +78,79 @@ class TestSkeletonReadsTheResultShape(unittest.TestCase):
|
||||
self.assertIn(key.value, result)
|
||||
|
||||
|
||||
class InstallerOutputExample(unittest.TestCase):
|
||||
"""The page showed a run with no `Not installable:` line while the
|
||||
RetroArch manifest left eleven entries out, and a count that the menu
|
||||
example below it no longer shared."""
|
||||
|
||||
def test_run_and_menu_show_one_dated_run_of_the_manifest(self):
|
||||
import install
|
||||
|
||||
text = _page("installer.md")
|
||||
intro, run = re.search(r"(?s)\n([^\n]*)\n\n```\n(Fetching file index.*?)```", text).groups()
|
||||
self.assertRegex(intro, r"\d{4}-\d{2}-\d{2}", "an output example says when it was taken")
|
||||
manifest = json.loads((ROOT / "install" / "retroarch.json").read_text(encoding="utf-8"))
|
||||
omitted = re.search(r"Not installable: \d+ files? \(\d+ required, (.*)\)\.", run)
|
||||
if manifest.get("omitted_files"):
|
||||
self.assertIsNotNone(omitted, "the manifest omits files and the run shows no line for it")
|
||||
if omitted:
|
||||
for part in omitted.group(1).split(", "):
|
||||
self.assertIn(part.rsplit(":", 1)[0], install.OMISSION_REASONS.values())
|
||||
count, size = re.search(r"\n (\d+) files \(([\d.]+ [KMG]B)\)", run).groups()
|
||||
self.assertIn(f"0/{count} present", run)
|
||||
menu = re.search(r"Retroarch: (\d+) files, ([\d.]+ [KMG]B)\.", text)
|
||||
self.assertEqual(menu.groups(), (count, size))
|
||||
|
||||
|
||||
class ReleaseRefPromise(unittest.TestCase):
|
||||
"""RETROBIOS_REF was said to pin a reproducible file set, while every
|
||||
release deletes the previous tags and large files come from one fixed
|
||||
release whatever the ref."""
|
||||
|
||||
def test_the_page_says_what_a_tag_does_not_pin(self):
|
||||
import install
|
||||
|
||||
self.assertIn("/large-files/", install.RELEASE_URL)
|
||||
self.assertIn("--cleanup-tag", _page("release-process.md"))
|
||||
row = next(
|
||||
line for line in _page("installer.md").splitlines()
|
||||
if line.startswith("| `RETROBIOS_REF`")
|
||||
)
|
||||
self.assertIn("`large-files`", row)
|
||||
self.assertIn("next release", row)
|
||||
for page in PUBLISHED:
|
||||
for paragraph in page.read_text(encoding="utf-8").split("\n\n"):
|
||||
if "RETROBIOS_REF" in paragraph:
|
||||
self.assertNotIn("reproducible", paragraph, page.name)
|
||||
|
||||
|
||||
class ReleaseCommitCarriesTheManifests(unittest.TestCase):
|
||||
"""The documented release commit left out install/*.json, which the
|
||||
release pipeline regenerates: v2026.10.10 added three by hand."""
|
||||
|
||||
def test_the_commit_step_adds_what_the_pipeline_rewrites(self):
|
||||
pipeline = (ROOT / "scripts" / "pipeline.py").read_text(encoding="utf-8")
|
||||
self.assertIn('"--manifest"', pipeline)
|
||||
block = _section(_page("release-process.md"), "Cutting a release")
|
||||
add = next(line for line in block.splitlines() if line.startswith("git add "))
|
||||
self.assertIn("install/*.json", add.split())
|
||||
|
||||
|
||||
class PageOpenings(unittest.TestCase):
|
||||
"""Four pages opened by announcing what they cover."""
|
||||
|
||||
META = re.compile(r"(?i)\bthis (page|guide) (covers|documents|explains|describes|picks)\b|^Diagnosis guide")
|
||||
|
||||
def test_no_page_announces_itself(self):
|
||||
offenders = [
|
||||
f"{page.name}: {line[:80]}"
|
||||
for page in PUBLISHED
|
||||
for line in FENCED.sub("", page.read_text(encoding="utf-8")).splitlines()
|
||||
if self.META.search(line)
|
||||
]
|
||||
self.assertEqual(offenders, [], "\n".join(offenders))
|
||||
|
||||
|
||||
class NoHedgedRules(unittest.TestCase):
|
||||
"""The profiling guide and the verification discipline turned rules into
|
||||
wishes ("it's worth", "Ideally", "something to look into")."""
|
||||
@@ -97,6 +170,22 @@ class NoHedgedRules(unittest.TestCase):
|
||||
self.assertEqual(offenders, [], "\n".join(offenders))
|
||||
|
||||
|
||||
class ExitStatusTable(unittest.TestCase):
|
||||
"""Code 2 was documented as 'every file failed' only, while argparse
|
||||
exits 2 on every refused option, and the 141 example named an option
|
||||
that does not exist."""
|
||||
|
||||
def test_codes_and_options_are_the_installers(self):
|
||||
source = (ROOT / "install.py").read_text(encoding="utf-8")
|
||||
section = _section(_page("installer.md"), "Exit status")
|
||||
documented = {int(row[0].strip("`")) for row in _table_rows(section)}
|
||||
raised = {int(code) for code in re.findall(r"sys\.exit\((\d+)\)", source)}
|
||||
self.assertEqual(documented, raised | {2})
|
||||
self.assertIn("install.py: error:", section)
|
||||
for option in set(re.findall(r"(--[a-z][a-z-]+)", section)):
|
||||
self.assertIn(f'"{option}"', source, option)
|
||||
|
||||
|
||||
class ProfileFieldsRequired(unittest.TestCase):
|
||||
"""The table marked source, profiled_date, core_version and systems as
|
||||
required and called the schema authoritative, which requires none."""
|
||||
@@ -115,5 +204,19 @@ class ProfileFieldsRequired(unittest.TestCase):
|
||||
self.assertEqual(marked, required)
|
||||
|
||||
|
||||
class PythonFloorOnRetroPie(unittest.TestCase):
|
||||
"""The page offered `apt install python3` to a RetroPie 4.8 image, where
|
||||
python3 is 3.7.3 and apt has nothing newer."""
|
||||
|
||||
def test_both_pages_name_the_case(self):
|
||||
install_sh = (ROOT / "install.sh").read_text(encoding="utf-8")
|
||||
self.assertIn("(3, 8)", install_sh)
|
||||
first = _section(_page("first-install.md"), "Error: Python 3.8 or newer is required.")
|
||||
self.assertIn("RetroPie 4.8", first)
|
||||
self.assertIn("3.7.3", first)
|
||||
changing = _section(_page("installer.md"), "Changing the installer")
|
||||
self.assertIn("3.7.3", changing)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,8 +1,7 @@
|
||||
# First install
|
||||
|
||||
One path, from an empty BIOS folder to files the emulator can read. Every step
|
||||
shows what the screen looks like when it worked, so there is never a moment of
|
||||
wondering.
|
||||
shows what the screen looks like when it worked.
|
||||
|
||||
If something on this page does not match what happens, the last section covers
|
||||
the cases that come up.
|
||||
@@ -154,11 +153,17 @@ its BIOS folder, see [Getting started](getting-started.md).
|
||||
|
||||
### Error: Python 3.8 or newer is required.
|
||||
|
||||
Python is missing from the machine. The message names the command that installs
|
||||
it, `sudo apt install python3` on Debian and Ubuntu. On a handheld running Batocera or Recalbox it is already there, so
|
||||
No Python 3.8 or newer was found: Python is missing, or older. The message
|
||||
names the command that installs it, `sudo apt install python3` on Debian and
|
||||
Ubuntu. On a handheld running Batocera or Recalbox it is already there, so
|
||||
this message usually means the command ran on the computer rather than on the
|
||||
console.
|
||||
|
||||
A stock RetroPie 4.8 image is Debian Buster, where `python3` is 3.7.3 and apt
|
||||
has nothing newer, so the same command changes nothing. The RetroPie pack from
|
||||
the [releases page](https://github.com/Abdess/retrobios/releases/latest),
|
||||
extracted into `~/RetroPie/BIOS/`, installs the same files.
|
||||
|
||||
### Error: the downloaded installer does not match its expected fingerprint.
|
||||
|
||||
Nothing was run and nothing was written. The download did not match the
|
||||
|
||||
+15
-10
@@ -100,14 +100,15 @@ macOS.
|
||||
rest.
|
||||
7. Copies into standalone emulator directories, only with `--standalone-copies`.
|
||||
|
||||
A run looks like this:
|
||||
A RetroArch run into an empty folder printed this on 2026-10-21:
|
||||
|
||||
```
|
||||
Fetching file index for retroarch...
|
||||
5865 files (5.7 GB)
|
||||
8069 files (6.5 GB)
|
||||
Not installable: 11 files (1 required, not found: 11).
|
||||
|
||||
Checking existing files...
|
||||
0/5865 present (0 verified, 0 wrong hash)
|
||||
0/8069 present (0 verified, 0 wrong hash)
|
||||
```
|
||||
|
||||
When the manifest leaves entries out (a file the collection cannot supply, or
|
||||
@@ -122,9 +123,9 @@ By default the installer fetches everything the platform runs. At a terminal,
|
||||
after detection, it asks:
|
||||
|
||||
```
|
||||
Retroarch: 5865 files, 5.7 GB.
|
||||
Retroarch: 8069 files, 6.5 GB.
|
||||
Enter) install everything
|
||||
i) only what the installed cores read: 102 files, 44.5 MB
|
||||
i) only what the installed cores read: 100 files, 44.2 MB
|
||||
c) choose systems, cores and regions
|
||||
q) quit
|
||||
```
|
||||
@@ -246,7 +247,7 @@ Files the list does not name are never read, moved or deleted.
|
||||
|
||||
| Variable | Effect |
|
||||
|----------|--------|
|
||||
| `RETROBIOS_REF` | Branch or tag the file list and the payloads are read from. Defaults to `main`, and a release tag pins a reproducible file set |
|
||||
| `RETROBIOS_REF` | Branch or tag the file list and the files committed to the repository are read from. Defaults to `main`. Files the manifest marks `release_asset` always come from the `large-files` release, whatever the ref, and that release replaces an asset in place when it is rebuilt. Each release deletes the previous release tags ([release process](release-process.md#cutting-a-release)), so a tag holds a file list until the next release |
|
||||
| `RETROBIOS_BASE_URL` | Base URL for the manifest and the files it declares. HTTPS only, plain HTTP allowed to loopback for end-to-end tests. Whoever controls this base controls the expected hashes, hence the check |
|
||||
| `RETROBIOS_OS` | Names the host outright (`linux`, `wsl`, `windows`, `darwin`, `android`) instead of detecting it |
|
||||
| `EXTERNAL_STORAGE` | Android only, the shared storage root RetroArch builds its default directories from. Defaults to `/storage/emulated/0` |
|
||||
@@ -467,8 +468,12 @@ followed.
|
||||
|
||||
## Changing the installer
|
||||
|
||||
`install.py` runs on the user's machine, often an older handheld or a stock
|
||||
RetroPie image, so it stays on Python 3.8 and the standard library alone.
|
||||
`install.py` runs on the user's machine, often an older handheld, so it stays
|
||||
on Python 3.8 and the standard library alone. The RetroPie 4.8 images
|
||||
(`retropie-buster-4.8-*.img.gz`) are Debian Buster, whose `python3` is 3.7.3:
|
||||
there the bootstrap stops before running anything, and the RetroPie pack from
|
||||
the releases page is the way to install
|
||||
([first install](first-install.md#error-python-38-or-newer-is-required)).
|
||||
Tests refuse any 3.9 syntax or API, and one runs the installer end to end under
|
||||
a real 3.8 interpreter, which CI installs beside 3.12 so the check cannot skip.
|
||||
The build scripts need 3.12 and are not bound by this.
|
||||
@@ -487,9 +492,9 @@ fields, the installer refuses to narrow rather than install everything.
|
||||
|------|---------|
|
||||
| `0` | everything asked for is installed or already up to date |
|
||||
| `1` | a failure: the one-liner could not download or verify `install.py`, or found no Python 3.8 (nothing was run), an unusable base URL, an unknown platform or target, `--installed-cores` with no core found, a manifest that fails validation, a fetch that never succeeded, no platform to install for in a non-interactive run, some files failed while others installed, or `--check` found files to fetch |
|
||||
| `2` | nothing was installed: every file the run tried failed (a BIOS folder that cannot be written, for one) |
|
||||
| `2` | nothing was installed: every file the run tried failed (a BIOS folder that cannot be written, for one). Also an option the installer refused before any download: an unknown option, `--jobs` outside 1 to 32, `--target` with `--every-board`. The message then starts with `install.py: error:` |
|
||||
| `130` | stopped with Ctrl+C. Run the same command again to carry on |
|
||||
| `141` | the output was piped to a reader that closed early (`--list \| head`) |
|
||||
| `141` | the output was piped to a reader that closed early (`--list-platforms \| head`) |
|
||||
|
||||
`install.ps1` passes the installer's code on unchanged: run as a file it exits
|
||||
with it, run through `irm \| iex` it sets `$LASTEXITCODE` (an `exit` there
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Release process
|
||||
|
||||
This page documents the CI/CD pipeline: what each workflow does, how releases
|
||||
are built, and how to run the process manually.
|
||||
Two GitHub workflows check pull requests and publish the site. Releases are
|
||||
built, checked and uploaded from the maintainer's machine.
|
||||
|
||||
## CI workflows overview
|
||||
|
||||
The project uses 2 GitHub Actions workflows. All use only official GitHub
|
||||
Both workflows use only official GitHub
|
||||
actions (`actions/checkout`, `actions/setup-python`, `actions/upload-pages-artifact`,
|
||||
`actions/deploy-pages`). No third-party actions.
|
||||
|
||||
@@ -237,8 +237,12 @@ python scripts/check_release.py --tag "v${DATE}" --sums dist/SHA256SUMS.txt \
|
||||
&& gh release edit "v${DATE}" --draft=false --latest
|
||||
|
||||
# The record and the pages it feeds go to main with the release, so the
|
||||
# table never describes a pack other than the one the link serves.
|
||||
git add release.json README.md mkdocs.yml
|
||||
# table never describes a pack other than the one the link serves. The
|
||||
# install manifests go too: step 1 regenerated them, and their pack_files
|
||||
# and pack_size describe the packs just built. git status names anything
|
||||
# else the run rewrote.
|
||||
git status --short
|
||||
git add release.json README.md mkdocs.yml install/*.json install/targets/*.json
|
||||
git commit -m "chore: record release v${DATE}" && git push
|
||||
|
||||
# 6. Keep only the new release plus large-files: an older pack carries hashes
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# Troubleshooting
|
||||
|
||||
Diagnosis guide organized by symptom. Each section describes what to check and how to fix it.
|
||||
|
||||
## Game won't start / black screen
|
||||
|
||||
Most launch failures are caused by a missing or incorrect BIOS file.
|
||||
@@ -280,9 +278,9 @@ The one-line bootstrap verifies the downloaded installer before running it, and
|
||||
the installer reads its manifests from the same revision. A platform name is
|
||||
checked before anything is downloaded, so a "No file list" message comes from
|
||||
the revision: `RETROBIOS_REF` naming a tag that does not exist, or one older
|
||||
than the platform. Unset it, or pick a tag from the releases page.
|
||||
`RETROBIOS_REF` pins an installation to a published tag when a reproducible
|
||||
file set matters more than the newest one.
|
||||
than the platform. Unset it, or pick a tag from the releases page. What a tag
|
||||
holds and for how long is under
|
||||
[environment overrides](installer.md#environment-overrides).
|
||||
|
||||
### Manual pack not found in release
|
||||
|
||||
|
||||
Reference in new issue
Block a user