mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-23 20:29:06 -05:00
feat: offer the one-liner where users land
The download page had no Android line and no Windows form with options, and platform and system pages offered only ZIP parts. One generator now writes the Linux, Windows and Android commands for the download page and the platform, emulator and system pages. A system section lists each platform's command under the system id its install manifest records, the only names --system accepts.
This commit is contained in:
1 parent
ccd1ff3b0b
commit
f15fa46d85
2 files changed
+167
-25
No files matched your search
+123
-25
@@ -20,6 +20,7 @@ import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import shutil
|
||||
import sqlite3
|
||||
import sys
|
||||
@@ -2157,6 +2158,7 @@ def generate_platform_page(
|
||||
f"| Contributed by | [@{username}](https://github.com/{username}):"
|
||||
f" {contribution}{pr_link} |"
|
||||
)
|
||||
lines.extend(["", *_platform_install_block(name, display)])
|
||||
lines.extend(
|
||||
[
|
||||
"",
|
||||
@@ -2842,11 +2844,46 @@ def _card_title(text: str, anchor: str) -> str:
|
||||
return f"#### {text} {{ {attributes} }}"
|
||||
|
||||
|
||||
def system_install_choices(coverages: dict) -> dict[str, dict[str, list[tuple[str, str]]]]:
|
||||
"""slug -> console -> the (platform, system id) pairs linked to it.
|
||||
|
||||
Read through the same table as every system link, so a command sits in
|
||||
the section the platform's system links to. Only a system the
|
||||
platform's install manifest records is offered: --system refuses any
|
||||
other name.
|
||||
"""
|
||||
from siterender import _page_by_norm # noqa: PLC0415
|
||||
|
||||
choices: dict[str, dict[str, list[tuple[str, str]]]] = {}
|
||||
for name, cov in sorted(coverages.items()):
|
||||
manifest = _REPO_ROOT / "install" / f"{name}.json"
|
||||
if not manifest.is_file():
|
||||
continue
|
||||
document = json.loads(manifest.read_text(encoding="utf-8"))
|
||||
recorded = {
|
||||
str(system)
|
||||
for entry in (document.get("files") or []) + (document.get("omitted_files") or [])
|
||||
for system in entry.get("systems") or []
|
||||
}
|
||||
for sys_id in sorted(set(cov["config"].get("systems") or {}) & recorded):
|
||||
page = _system_page_map.get(sys_id) or _page_by_norm(sys_id)
|
||||
if page:
|
||||
slug, console = page
|
||||
choices.setdefault(slug, {}).setdefault(console, []).append((name, sys_id))
|
||||
return choices
|
||||
|
||||
|
||||
def generate_system_page(
|
||||
manufacturer: str,
|
||||
consoles: dict[str, list],
|
||||
consumers: FileConsumers,
|
||||
installs: dict[str, list[tuple[str, str]]] | None = None,
|
||||
) -> str:
|
||||
"""One manufacturer's page, a section per console.
|
||||
|
||||
``installs`` gives, per console, the (platform, system id) pairs whose
|
||||
install command the section offers.
|
||||
"""
|
||||
slug = manufacturer.lower().replace(" ", "-")
|
||||
lines = [
|
||||
f"# {manufacturer}",
|
||||
@@ -2862,6 +2899,7 @@ def generate_system_page(
|
||||
icon_md = system_icon_markdown(manufacturer, console_name)
|
||||
lines.append(f"## {icon_md}{console_name}")
|
||||
lines.append("")
|
||||
lines.extend(_console_install_block(console_name, (installs or {}).get(console_name, [])))
|
||||
main_files = [f for f in files if "/.variants/" not in f["path"]]
|
||||
variant_files = [f for f in files if "/.variants/" in f["path"]]
|
||||
|
||||
@@ -3477,6 +3515,37 @@ def _render_metadata_rows(profile: dict) -> list[str]:
|
||||
return lines
|
||||
|
||||
|
||||
INSTALL_BASE = "https://raw.githubusercontent.com/Abdess/retrobios/main"
|
||||
|
||||
|
||||
def install_command(shell: str, args: str = "") -> str:
|
||||
"""The one-liner for a shell, with installer options when given.
|
||||
|
||||
PowerShell passes options only through a scriptblock: `irm ... | iex`
|
||||
takes none.
|
||||
"""
|
||||
if shell == "sh":
|
||||
return f"curl -fsSL {INSTALL_BASE}/install.sh | sh" + (f" -s -- {args}" if args else "")
|
||||
if args:
|
||||
return f"&([scriptblock]::Create((irm {INSTALL_BASE}/install.ps1))) {args}"
|
||||
return f"irm {INSTALL_BASE}/install.ps1 | iex"
|
||||
|
||||
|
||||
def install_commands(args: str = "", indent: str = "") -> list[str]:
|
||||
"""The same install for each place a user runs it: one generator for the
|
||||
download page and the platform, system and emulator pages, so none of
|
||||
them loses the Android or Windows form."""
|
||||
lines = []
|
||||
for label, shell in (
|
||||
("Linux, macOS, Steam Deck:", "sh"),
|
||||
("Windows (PowerShell):", "powershell"),
|
||||
("Android (Termux, after `pkg install python` and `termux-setup-storage`):", "sh"),
|
||||
):
|
||||
lines += [f"{indent}{label}", "", f"{indent}```{shell}",
|
||||
f"{indent}{install_command(shell, args)}", f"{indent}```", ""]
|
||||
return lines
|
||||
|
||||
|
||||
def _install_block(name: str, profile: dict) -> list[str]:
|
||||
"""One command that installs this emulator's files where it reads them.
|
||||
|
||||
@@ -3486,26 +3555,49 @@ def _install_block(name: str, profile: dict) -> list[str]:
|
||||
"""
|
||||
if profile.get("type") in ("alias", "launcher", "test"):
|
||||
return []
|
||||
base = "https://raw.githubusercontent.com/Abdess/retrobios/main"
|
||||
return [
|
||||
'??? tip "Install this emulator\'s files"',
|
||||
"",
|
||||
" The installer finds your platform and its BIOS folder, then downloads "
|
||||
"only the files this emulator reads there and checks each one.",
|
||||
"",
|
||||
" Linux, macOS, Steam Deck:",
|
||||
*install_commands(f"--emulator {name}", " "),
|
||||
]
|
||||
|
||||
|
||||
def _platform_install_block(name: str, display: str) -> list[str]:
|
||||
"""The platform's pack, installed file by file: what the ZIP holds, with
|
||||
resume, verification and only what is missing."""
|
||||
return [
|
||||
'???+ tip "Install with one command"',
|
||||
"",
|
||||
f" The installer downloads the files of the {display} pack that are "
|
||||
"missing or differ from it, checks each one, and resumes where it "
|
||||
"stopped. Add `--system` with the ids of the systems overview below "
|
||||
"to install fewer systems.",
|
||||
"",
|
||||
*install_commands(f"--platform {name}", " "),
|
||||
]
|
||||
|
||||
|
||||
def _console_install_block(console: str, declared: list[tuple[str, str]]) -> list[str]:
|
||||
"""Each platform's command for one console, by the system id it gives it."""
|
||||
if not declared:
|
||||
return []
|
||||
lines = [
|
||||
f'??? tip "Install the {console} files"',
|
||||
"",
|
||||
" Each platform names the system its own way. On Windows, run the "
|
||||
"same options through `&([scriptblock]::Create((irm "
|
||||
f"{INSTALL_BASE}/install.ps1)))`.",
|
||||
"",
|
||||
" ```sh",
|
||||
f" curl -fsSL {base}/install.sh | sh -s -- --emulator {name}",
|
||||
" ```",
|
||||
"",
|
||||
" Windows (PowerShell):",
|
||||
"",
|
||||
" ```powershell",
|
||||
f" &([scriptblock]::Create((irm {base}/install.ps1))) --emulator {name}",
|
||||
" ```",
|
||||
"",
|
||||
]
|
||||
lines += [
|
||||
f" {install_command('sh', f'--platform {platform} --system {shlex.quote(system)}')}"
|
||||
for platform, system in declared
|
||||
]
|
||||
return lines + [" ```", ""]
|
||||
|
||||
|
||||
def _render_platform_details(profile: dict) -> list[str]:
|
||||
@@ -5045,6 +5137,10 @@ def _mkdocs_config(docs: Path, text: str) -> tuple[Path, str]:
|
||||
def generate_which_pack() -> str:
|
||||
"""Generate the 'Which pack?' decision page."""
|
||||
rel = RELEASE_URL
|
||||
quick = "\n".join(install_commands())
|
||||
check_sh, check_ps = install_command("sh", "--check"), install_command("powershell", "--check")
|
||||
sd_card = install_command(
|
||||
"sh", "--platform retroarch --dest /path/to/sdcard/retroarch/cores/system")
|
||||
return _link_pack_cells(f"""\
|
||||
# Download
|
||||
|
||||
@@ -5063,18 +5159,7 @@ incorrect files, verifies their hashes, and installs them atomically. The small
|
||||
bootstrap checks the Python installer against an embedded SHA-256 before running
|
||||
it, and the installer reads its file list from that same revision.
|
||||
|
||||
**Linux / Mac / Steam Deck:**
|
||||
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
```
|
||||
|
||||
**Windows (PowerShell):**
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
{quick}
|
||||
That is the complete default flow. Extra copies into detected standalone
|
||||
emulator directories are deliberately opt-in with `--standalone-copies`, so
|
||||
automatic setup never writes outside the selected platform tree unexpectedly.
|
||||
@@ -5082,7 +5167,19 @@ Adding `--check` reads the BIOS folder, hashes what is there and writes
|
||||
nothing:
|
||||
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --check
|
||||
{check_sh}
|
||||
```
|
||||
|
||||
```powershell
|
||||
{check_ps}
|
||||
```
|
||||
|
||||
An SD card mounted on a PC is installed with `--platform` and `--dest`, the
|
||||
BIOS folder on the card (RetroArch on Switch: `retroarch/cores/system`, on
|
||||
Android: `RetroArch/system`):
|
||||
|
||||
```sh
|
||||
{sd_card}
|
||||
```
|
||||
|
||||
Options, environment overrides, how each platform is detected and what the
|
||||
@@ -5488,11 +5585,12 @@ def _generate(args: argparse.Namespace) -> None:
|
||||
write_if_changed(
|
||||
str(docs / "systems" / "index.md"), generate_systems_index(manufacturers)
|
||||
)
|
||||
console_installs = system_install_choices(coverages)
|
||||
(docs / "files").mkdir(parents=True, exist_ok=True)
|
||||
write_if_changed(str(docs / "files" / "index.md"), generate_files_page())
|
||||
for mfr, consoles in manufacturers.items():
|
||||
slug = mfr.lower().replace(" ", "-")
|
||||
page = generate_system_page(mfr, consoles, consumers)
|
||||
page = generate_system_page(mfr, consoles, consumers, console_installs.get(slug))
|
||||
PAGE_DATA[f"systems/{slug}.md"] = system_structured_data(slug, consoles)
|
||||
write_single_item(
|
||||
docs / "api" / "v1" / "systems" / f"{slug}.json", "systems",
|
||||
|
||||
@@ -882,6 +882,50 @@ class NestedProfileBlocksRenderAsLists(unittest.TestCase):
|
||||
self.assertIn("name: a.bin, size: 2", html)
|
||||
|
||||
|
||||
class TheOneLinerIsWhereUsersLand(unittest.TestCase):
|
||||
"""The download page had no Android line and no Windows form with
|
||||
options, and the platform and system pages offered only ZIP parts."""
|
||||
|
||||
def test_download_page(self):
|
||||
import generate_site # noqa: PLC0415
|
||||
|
||||
page = generate_site.generate_which_pack()
|
||||
self.assertIn("Android (Termux", page)
|
||||
self.assertIn("install.ps1))) --check", page)
|
||||
self.assertIn("--platform retroarch --dest", page)
|
||||
|
||||
def test_platform_and_system_pages(self):
|
||||
import generate_site # noqa: PLC0415
|
||||
from cross_reference import FileConsumers # noqa: PLC0415
|
||||
|
||||
cov = {"platform": "Batocera", "mode": "md5", "present": 0, "total": 0,
|
||||
"verified": 0, "missing": 0, "details": [], "config": {"systems": {}}}
|
||||
page = generate_site.generate_platform_page("batocera", cov)
|
||||
self.assertIn("install.sh | sh -s -- --platform batocera", page)
|
||||
self.assertIn("install.ps1))) --platform batocera", page)
|
||||
self.assertLess(page.index("--platform batocera"), page.index("md-button"))
|
||||
|
||||
entry = {"name": "a.bin", "sha1": "a" * 40, "md5": "b" * 32, "size": 3,
|
||||
"path": "bios/Recalbox/MSX/a.bin"}
|
||||
page = generate_site.generate_system_page(
|
||||
"Microsoft", {"MSX": [entry]}, FileConsumers({"files": {}}),
|
||||
{"MSX": [("recalbox", "msx1,msx2,msxturbor"), ("batocera", "msx1")]})
|
||||
self.assertIn("--platform recalbox --system msx1,msx2,msxturbor\n", page)
|
||||
self.assertIn("--platform batocera --system msx1", page)
|
||||
|
||||
def test_a_system_is_offered_only_where_the_manifest_records_it(self):
|
||||
import generate_site # noqa: PLC0415
|
||||
import siterender # noqa: PLC0415
|
||||
|
||||
saved = dict(siterender._system_page_map)
|
||||
self.addCleanup(siterender.set_system_page_map, saved)
|
||||
siterender.set_system_page_map({"sony-playstation": ("sony", "PlayStation"),
|
||||
"nowhere": ("sony", "PlayStation")})
|
||||
cov = {"retroarch": {"config": {"systems": {"sony-playstation": {}, "nowhere": {}}}}}
|
||||
choices = generate_site.system_install_choices(cov)
|
||||
self.assertEqual(choices["sony"]["PlayStation"], [("retroarch", "sony-playstation")])
|
||||
|
||||
|
||||
class TheGapPageOpensOnCounts(unittest.TestCase):
|
||||
"""The page opened on a slogan and on tiles reading "Verified (100%)"
|
||||
for 5,437 of 5,438 files and "Missing 0", above a table of red cells."""
|
||||
|
||||
Reference in new issue
Block a user