mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-01 00:27:20 -05:00
chore: merge the short one-liner
This commit is contained in:
commit
825863b8f9
37 files changed
+486
-110
No files matched your search
@@ -45,6 +45,7 @@ on:
|
||||
- "scripts/restore_large_files.py"
|
||||
- "scripts/safeparse.py"
|
||||
- "scripts/sect233r1.py"
|
||||
- "scripts/sitebootstrap.py"
|
||||
- "scripts/sitediagrams.py"
|
||||
- "scripts/siteentries.py"
|
||||
- "scripts/siteguides.py"
|
||||
@@ -69,7 +70,11 @@ on:
|
||||
- "schemas/**"
|
||||
- "docs_assets/**"
|
||||
- "install/**"
|
||||
# The site serves install.sh and install.ps1 pinned to install.py
|
||||
# of the commit it is built from (scripts/sitebootstrap.py).
|
||||
- "install.py"
|
||||
- "install.sh"
|
||||
- "install.ps1"
|
||||
- "database.json"
|
||||
- "release.json"
|
||||
- "derivable.json"
|
||||
|
||||
@@ -19,21 +19,21 @@ Copy one command into your terminal:
|
||||
|
||||
```bash
|
||||
# Linux / macOS / Steam Deck
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
|
||||
# Windows (PowerShell)
|
||||
irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1 | iex
|
||||
irm https://abdess.github.io/retrobios/i.ps1 | iex
|
||||
|
||||
# Android (Termux, after pkg install python and termux-setup-storage)
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
|
||||
# SD card mounted on a PC: a muOS, KNULLI, ROCKNIX, Onion, spruceOS or MinUI card
|
||||
# is found by itself. For another card, --dest is its BIOS folder
|
||||
# (RetroArch on Switch: retroarch/cores/system, on Android: RetroArch/system)
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --platform retroarch --dest /path/to/sdcard/retroarch/cores/system
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --platform retroarch --dest /path/to/sdcard/retroarch/cores/system
|
||||
|
||||
# Options in PowerShell: anything after | iex goes to iex, so the script runs as a block
|
||||
&([scriptblock]::Create((irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1))) --check
|
||||
&([scriptblock]::Create((irm https://abdess.github.io/retrobios/i.ps1))) --check
|
||||
```
|
||||
|
||||
The script opens a full-screen installer, driven with the arrow keys and Enter, or with a controller on a handheld. It finds your setup and its BIOS folder, downloads only the missing files and checks each one. It can also copy the BIOS files from your old SD card.
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
$defaultInstallUrl = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
$defaultInstallSha256 = "7029f605591f09c020aafcd521d8a7134ace78cc566435d8e76be33c42a9f6dc"
|
||||
$defaultInstallSha256 = "6467b259f1fff162581df002d95f17a2af8b2484e4524dd094003e6ee62217c7"
|
||||
$maximumInstallerBytes = 2MB
|
||||
$installerExit = 0
|
||||
$installer = if ($PSScriptRoot) { Join-Path $PSScriptRoot "install.py" } else { $null }
|
||||
|
||||
+4
-2
@@ -243,7 +243,9 @@ CARD_PLATFORMS = frozenset(name for name, _file, _prefix in _CARD_RULES)
|
||||
SITE_URL = "https://abdess.github.io/retrobios/"
|
||||
INSTALLER_PAGE = f"{SITE_URL}wiki/installer/"
|
||||
TROUBLESHOOTING_PAGE = f"{SITE_URL}wiki/troubleshooting/"
|
||||
BOOTSTRAP_URL = "https://raw.githubusercontent.com/Abdess/retrobios/main/install"
|
||||
# The one-line bootstraps the site serves: BOOTSTRAP_URL for sh, .ps1 after it
|
||||
# for PowerShell (scripts/sitebootstrap.py).
|
||||
BOOTSTRAP_URL = f"{SITE_URL}i"
|
||||
|
||||
# Fallback BIOS destination per platform when --platform is forced
|
||||
# but auto-detection finds nothing on the machine.
|
||||
@@ -5747,7 +5749,7 @@ class Invocation:
|
||||
if bootstrap == "ps1":
|
||||
return f"&([scriptblock]::Create((irm {BOOTSTRAP_URL}.ps1)))"
|
||||
if bootstrap == "sh":
|
||||
return f"curl -fsSL {BOOTSTRAP_URL}.sh | {'sudo ' if elevated else ''}sh -s --"
|
||||
return f"curl -fsSL {BOOTSTRAP_URL} | {'sudo ' if elevated else ''}sh -s --"
|
||||
python = "python" if self.os_type == "windows" else "python3"
|
||||
return f"{'sudo ' if elevated else ''}{python} install.py"
|
||||
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ esac
|
||||
TEMP_INSTALLER=""
|
||||
TEMP_DIRECTORY=""
|
||||
DEFAULT_INSTALL_URL="https://raw.githubusercontent.com/Abdess/retrobios/main/install.py"
|
||||
DEFAULT_INSTALL_SHA256="7029f605591f09c020aafcd521d8a7134ace78cc566435d8e76be33c42a9f6dc"
|
||||
DEFAULT_INSTALL_SHA256="6467b259f1fff162581df002d95f17a2af8b2484e4524dd094003e6ee62217c7"
|
||||
MAX_INSTALLER_BYTES=2097152
|
||||
|
||||
cleanup() {
|
||||
|
||||
@@ -27,8 +27,8 @@
|
||||
{
|
||||
"dest": "roms/ports/retrobios/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 881442,
|
||||
"sha256": "7029f605591f09c020aafcd521d8a7134ace78cc566435d8e76be33c42a9f6dc",
|
||||
"size": 881512,
|
||||
"sha256": "6467b259f1fff162581df002d95f17a2af8b2484e4524dd094003e6ee62217c7",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
@@ -71,8 +71,8 @@
|
||||
"muos": {
|
||||
"archive": {
|
||||
"dest": "ARCHIVE/RetroBIOS.muxapp",
|
||||
"size": 934234,
|
||||
"sha256": "56675adcad36deb03879b7f5961203f6c65cc2f4f13170f6d38b37ed1fe5b367"
|
||||
"size": 934304,
|
||||
"sha256": "2d94d307ff407a842f4a7c8bbd98bc4cfcee27713e6ee88c94339b689dd7dbe0"
|
||||
},
|
||||
"members": [
|
||||
{
|
||||
@@ -92,8 +92,8 @@
|
||||
{
|
||||
"name": "RetroBIOS/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 881442,
|
||||
"sha256": "7029f605591f09c020aafcd521d8a7134ace78cc566435d8e76be33c42a9f6dc",
|
||||
"size": 881512,
|
||||
"sha256": "6467b259f1fff162581df002d95f17a2af8b2484e4524dd094003e6ee62217c7",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
@@ -166,8 +166,8 @@
|
||||
{
|
||||
"dest": ".config/modules/retrobios/install.py",
|
||||
"repo_path": "install.py",
|
||||
"size": 881442,
|
||||
"sha256": "7029f605591f09c020aafcd521d8a7134ace78cc566435d8e76be33c42a9f6dc",
|
||||
"size": 881512,
|
||||
"sha256": "6467b259f1fff162581df002d95f17a2af8b2484e4524dd094003e6ee62217c7",
|
||||
"executable": false
|
||||
},
|
||||
{
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
# The registry keeps what tools read (device_group, extract_to, install);
|
||||
# this file keeps what people read.
|
||||
|
||||
install_base: https://raw.githubusercontent.com/Abdess/retrobios/main
|
||||
# The site serving the one-line bootstraps (scripts/sitebootstrap.py).
|
||||
install_base: https://abdess.github.io/retrobios
|
||||
|
||||
# Is it safe: shown on the home page and under every command on a guide page.
|
||||
# Where it writes follows install.py: the BIOS folder, the frontend's save
|
||||
|
||||
+1
-2
@@ -89,8 +89,7 @@ class Pack:
|
||||
# are served file by file by the installer.
|
||||
NO_PACK_HINT = (
|
||||
"A setup without a pack is served by the installer:\n"
|
||||
" curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh"
|
||||
" | sh -s -- --platform <name>"
|
||||
" curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --platform <name>"
|
||||
)
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -116,7 +116,7 @@ EOF
|
||||
echo "Available platforms:"
|
||||
platform_names "$release_json"
|
||||
echo "A setup without a pack is served by the installer:"
|
||||
echo " curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --platform <name>"
|
||||
echo " curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --platform <name>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ from common import (
|
||||
write_if_changed,
|
||||
)
|
||||
import release_record
|
||||
from siteguides import install_command
|
||||
from verify import verify_platform
|
||||
|
||||
|
||||
@@ -557,21 +558,21 @@ def generate_readme(db: dict, platforms_dir: str, db_path: str = "database.json"
|
||||
"",
|
||||
"```bash",
|
||||
"# Linux / macOS / Steam Deck",
|
||||
"curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh",
|
||||
install_command("sh"),
|
||||
"",
|
||||
"# Windows (PowerShell)",
|
||||
"irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1 | iex",
|
||||
install_command("powershell"),
|
||||
"",
|
||||
"# Android (Termux, after pkg install python and termux-setup-storage)",
|
||||
"curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh",
|
||||
install_command("sh"),
|
||||
"",
|
||||
"# SD card mounted on a PC: a muOS, KNULLI, ROCKNIX, Onion, spruceOS or MinUI card",
|
||||
"# is found by itself. For another card, --dest is its BIOS folder",
|
||||
"# (RetroArch on Switch: retroarch/cores/system, on Android: RetroArch/system)",
|
||||
"curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --platform retroarch --dest /path/to/sdcard/retroarch/cores/system",
|
||||
install_command("sh", "--platform retroarch --dest /path/to/sdcard/retroarch/cores/system"),
|
||||
"",
|
||||
"# Options in PowerShell: anything after | iex goes to iex, so the script runs as a block",
|
||||
"&([scriptblock]::Create((irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1))) --check",
|
||||
install_command("powershell", "--check"),
|
||||
"```",
|
||||
"",
|
||||
"The script opens a full-screen installer, driven with the arrow keys and Enter, or with a"
|
||||
|
||||
@@ -99,6 +99,8 @@ from siteguides import (
|
||||
HandheldSystem,
|
||||
FENCE_TITLES,
|
||||
INSTALL_BASE,
|
||||
INSTALL_PS1,
|
||||
INSTALL_SH,
|
||||
TERMINALS,
|
||||
TRUST_ITEMS,
|
||||
detected_systems as _detected_systems,
|
||||
@@ -111,6 +113,7 @@ from siteguides import (
|
||||
trust_box,
|
||||
)
|
||||
import release_record
|
||||
import sitebootstrap
|
||||
import sitemeasurements
|
||||
|
||||
# The Starlight project, and where its pages go inside it.
|
||||
@@ -6101,6 +6104,23 @@ SYSTEM_PAGE_BUDGET = 300_000
|
||||
CARD_HTML_RATIO = 1.7
|
||||
INSTALL_HTML_RATIO = 11
|
||||
SYSTEM_SUMMARY_HTML = 500_000
|
||||
# The ratio was fitted while each command named the raw.githubusercontent.com
|
||||
# bootstrap, 66 characters (67 for PowerShell). A box is weighed with its
|
||||
# address at that length, so the shorter site address moves no console off
|
||||
# or onto a summary and no card changes address.
|
||||
FITTED_ADDRESS_CHARS = {INSTALL_PS1: 67, INSTALL_SH: 66}
|
||||
|
||||
|
||||
def _install_box_weight(lines: list[str]) -> int:
|
||||
weight = 0
|
||||
for line in lines:
|
||||
weight += len(line) + 1
|
||||
for address, fitted in FITTED_ADDRESS_CHARS.items():
|
||||
found = line.count(address)
|
||||
line = line.replace(address, "")
|
||||
weight += found * (fitted - len(address))
|
||||
return weight
|
||||
|
||||
|
||||
# A system card: whether it is a variant, its lines, its sha1.
|
||||
Card = tuple[bool, list[str], str]
|
||||
@@ -7252,8 +7272,7 @@ def system_pages(
|
||||
# page, which the card weights never counted. A page is also cut when
|
||||
# its estimate as it renders passes the summary's.
|
||||
with markup.using("mkdocs"):
|
||||
boxes = {console: sum(len(line) + 1 for line in install_block(console))
|
||||
for console in consoles}
|
||||
boxes = {console: _install_box_weight(install_block(console)) for console in consoles}
|
||||
rendered = {console: CARD_HTML_RATIO * weights[console] + INSTALL_HTML_RATIO * boxes[console]
|
||||
for console in weights}
|
||||
if (not boards and len(measured_text) <= budget
|
||||
@@ -11763,6 +11782,8 @@ def _generate(args: argparse.Namespace) -> None:
|
||||
# this project site lives under /retrobios/. Every page's head points at
|
||||
# the sitemap instead (Starlight's head links it).
|
||||
(public / "robots.txt").unlink(missing_ok=True)
|
||||
# The one-line commands' bootstraps, at the site's root.
|
||||
sitebootstrap.write_bootstraps(public, _REPO_ROOT)
|
||||
|
||||
images_dest = public / "assets" / "images"
|
||||
images_dest.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -5,10 +5,7 @@ goes, and which ones the user has to rename."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from siteguides import INSTALL_BASE, pack_manual
|
||||
|
||||
INSTALL_SH = f"{INSTALL_BASE}/install.sh"
|
||||
INSTALL_PS1 = f"{INSTALL_BASE}/install.ps1"
|
||||
from siteguides import INSTALL_PS1, INSTALL_SH, pack_manual
|
||||
|
||||
def _detect_systems(detect: list[dict]) -> list[str]:
|
||||
"""The operating systems the registry's detection rules cover, in order."""
|
||||
|
||||
@@ -28,9 +28,9 @@ from common import (
|
||||
load_platform_registry,
|
||||
write_text_atomic,
|
||||
)
|
||||
from siteguides import install_command
|
||||
|
||||
REPO = "Abdess/retrobios"
|
||||
RAW = f"https://raw.githubusercontent.com/{REPO}/main"
|
||||
SITE = "https://abdess.github.io/retrobios/"
|
||||
|
||||
|
||||
@@ -112,12 +112,12 @@ def render(record: dict, platforms_dir: str, whats_new: str) -> str:
|
||||
"",
|
||||
"**Linux, macOS, Steam Deck**",
|
||||
"```sh",
|
||||
f"curl -fsSL {RAW}/install.sh | sh",
|
||||
install_command("sh"),
|
||||
"```",
|
||||
"",
|
||||
"**Windows (PowerShell)**",
|
||||
"```powershell",
|
||||
f"irm {RAW}/install.ps1 | iex",
|
||||
install_command("powershell"),
|
||||
"```",
|
||||
"",
|
||||
"### Download a pack",
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
"""The one-line bootstraps the site serves at its own address.
|
||||
|
||||
`curl -fsSL https://abdess.github.io/retrobios/i | sh` and
|
||||
`irm https://abdess.github.io/retrobios/i.ps1 | iex` fetch copies of
|
||||
install.sh and install.ps1 written when the site is built. Each copy
|
||||
downloads install.py from the commit the site is built from, the commit
|
||||
whose bootstrap pins that installer's SHA-256. The site deploys minutes
|
||||
after a push: until then it serves the previous commit's pair, never the
|
||||
bootstrap of one commit with the installer of the next.
|
||||
|
||||
GitHub Pages serves both names as application/octet-stream (no type in
|
||||
mime-db for .ps1 or for a bare name). curl ignores the type, and
|
||||
Invoke-RestMethod reads any type that is neither JSON nor XML as text.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from artifacts import write_if_changed
|
||||
|
||||
REPO_RAW = "https://raw.githubusercontent.com/Abdess/retrobios"
|
||||
SH_NAME = "i"
|
||||
PS1_NAME = "i.ps1"
|
||||
|
||||
# Served name: the bootstrap it copies and the one line naming the
|
||||
# installer's address there.
|
||||
SOURCES: dict[str, tuple[str, re.Pattern[str]]] = {
|
||||
SH_NAME: ("install.sh", re.compile(r'^(DEFAULT_INSTALL_URL=")[^"]*(")', re.M)),
|
||||
PS1_NAME: ("install.ps1", re.compile(r'^(\s*\$defaultInstallUrl = ")[^"]*(")', re.M)),
|
||||
}
|
||||
|
||||
|
||||
def build_commit(root: Path) -> str:
|
||||
"""The commit the site is built from: GITHUB_SHA in CI, HEAD locally."""
|
||||
commit = os.environ.get("GITHUB_SHA", "").strip()
|
||||
if not commit:
|
||||
commit = subprocess.run(
|
||||
["git", "rev-parse", "HEAD"], cwd=root, check=True,
|
||||
capture_output=True, text=True,
|
||||
).stdout.strip()
|
||||
if not re.fullmatch(r"[0-9a-f]{40}", commit):
|
||||
raise ValueError(f"not a commit id: {commit!r}")
|
||||
return commit
|
||||
|
||||
|
||||
def installer_url(commit: str) -> str:
|
||||
return f"{REPO_RAW}/{commit}/install.py"
|
||||
|
||||
|
||||
def served_text(name: str, root: Path, commit: str) -> str:
|
||||
"""The bootstrap served as name, its installer read from commit."""
|
||||
source, line = SOURCES[name]
|
||||
text = (root / source).read_text(encoding="utf-8")
|
||||
served, count = line.subn(
|
||||
lambda m: f"{m.group(1)}{installer_url(commit)}{m.group(2)}", text
|
||||
)
|
||||
if count != 1:
|
||||
raise ValueError(f"{source}: {count} installer address lines, expected one")
|
||||
return served
|
||||
|
||||
|
||||
def write_bootstraps(public: Path, root: Path, commit: str | None = None) -> list[Path]:
|
||||
"""Write every served bootstrap at the root of the site's public folder."""
|
||||
commit = commit or build_commit(root)
|
||||
public.mkdir(parents=True, exist_ok=True)
|
||||
written = []
|
||||
for name in SOURCES:
|
||||
path = public / name
|
||||
write_if_changed(str(path), served_text(name, root, commit))
|
||||
written.append(path)
|
||||
return written
|
||||
@@ -750,10 +750,10 @@ FENCE_TITLES = {"sh": "Terminal", "powershell": "PowerShell"}
|
||||
|
||||
def _installer_args(body: str) -> str:
|
||||
"""The options of a one-line install command, continuation lines joined."""
|
||||
from siteguides import INSTALL_BASE
|
||||
from siteguides import INSTALL_SH
|
||||
|
||||
line = " ".join(part.strip().rstrip("\\").strip() for part in body.splitlines())
|
||||
match = re.fullmatch(rf"curl -fsSL {re.escape(INSTALL_BASE)}/install\.sh \| sh(?: -s -- (.+))?", line)
|
||||
match = re.fullmatch(rf"curl -fsSL {re.escape(INSTALL_SH)} \| sh(?: -s -- (.+))?", line)
|
||||
if not match:
|
||||
raise ValueError(f"an os-tabs block holds no install command: {line!r}")
|
||||
return match.group(1) or ""
|
||||
|
||||
@@ -18,6 +18,7 @@ from pathlib import Path
|
||||
|
||||
import sitemarkup as markup
|
||||
from safeparse import require_yaml
|
||||
from sitebootstrap import PS1_NAME, SH_NAME
|
||||
|
||||
PLATFORMS_DIR = Path(__file__).resolve().parent.parent / "platforms"
|
||||
GUIDES_FILE = PLATFORMS_DIR / "_guides.yml"
|
||||
@@ -380,6 +381,9 @@ def load_guides(path: Path = GUIDES_FILE, registry: dict | None = None) -> dict:
|
||||
|
||||
_LOADED = load_guides()
|
||||
INSTALL_BASE: str = _LOADED["install_base"]
|
||||
# The bootstraps the site serves (sitebootstrap.py writes them).
|
||||
INSTALL_SH: str = f"{INSTALL_BASE}/{SH_NAME}"
|
||||
INSTALL_PS1: str = f"{INSTALL_BASE}/{PS1_NAME}"
|
||||
DEVICE_GROUPS: dict[str, tuple[str, str]] = _LOADED["device_groups"]
|
||||
GROUP_ANCHORS: dict[str, str] = _LOADED["group_anchors"]
|
||||
ALSO_RUNS: dict[str, tuple[tuple[str, str, str, str, str], ...]] = _LOADED["also_runs"]
|
||||
@@ -435,10 +439,10 @@ def install_command(shell: str, args: str = "") -> str:
|
||||
takes none.
|
||||
"""
|
||||
if shell == "sh":
|
||||
return f"curl -fsSL {INSTALL_BASE}/install.sh | sh" + (f" -s -- {args}" if args else "")
|
||||
return f"curl -fsSL {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"
|
||||
return f"&([scriptblock]::Create((irm {INSTALL_PS1}))) {args}"
|
||||
return f"irm {INSTALL_PS1} | iex"
|
||||
|
||||
|
||||
# The title over each command block, by the shell it is written for.
|
||||
|
||||
@@ -404,6 +404,7 @@ def _check_get_pages(
|
||||
"""
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
import release_record # noqa: PLC0415
|
||||
from siteguides import INSTALL_SH # noqa: PLC0415
|
||||
from common import list_registered_platforms, list_release_pack_platforms # noqa: PLC0415
|
||||
|
||||
if not (platforms_dir / "_registry.yml").is_file():
|
||||
@@ -428,7 +429,7 @@ def _check_get_pages(
|
||||
packs = [a for a in assets if "_BIOS_Pack" in a]
|
||||
if packs:
|
||||
issues.append(f"{rel}: links a pack for a setup without one: {packs[0]}")
|
||||
if "install.sh" not in (site / rel).read_text(encoding="utf-8"):
|
||||
if f"{INSTALL_SH} | sh" not in (site / rel).read_text(encoding="utf-8"):
|
||||
issues.append(f"{rel}: gives no install command")
|
||||
return issues
|
||||
|
||||
|
||||
@@ -59,14 +59,22 @@ class ReadmeRegressions(unittest.TestCase):
|
||||
)[0]
|
||||
|
||||
def test_advertised_bootstrap_urls_are_the_shipped_ones(self):
|
||||
import sitebootstrap
|
||||
from siteguides import INSTALL_BASE
|
||||
|
||||
quick_install = self._quick_install()
|
||||
for script in ("install.sh", "install.ps1"):
|
||||
url = f"https://raw.githubusercontent.com/Abdess/retrobios/main/{script}"
|
||||
self.assertIn(url, quick_install, f"{script} bootstrap URL missing")
|
||||
for name in sitebootstrap.SOURCES:
|
||||
url = f"{INSTALL_BASE}/{name}"
|
||||
self.assertIn(f"{url} |", quick_install, f"{name} bootstrap URL missing")
|
||||
# The site serves each bootstrap with install.py read from the
|
||||
# commit it was built from, the raw form from main.
|
||||
commit = "0" * 40
|
||||
for name in sitebootstrap.SOURCES:
|
||||
self.assertIn(sitebootstrap.installer_url(commit),
|
||||
sitebootstrap.served_text(name, ROOT, commit))
|
||||
self.assertIn(
|
||||
"https://raw.githubusercontent.com/Abdess/retrobios/main/install.py",
|
||||
(ROOT / "install.sh").read_text(encoding="utf-8"),
|
||||
"install.sh must fetch install.py from the ref the README advertises",
|
||||
)
|
||||
|
||||
def test_advertised_installer_flags_exist(self):
|
||||
@@ -690,9 +698,11 @@ class PackReadmesSayWhatTheInstallerDoes(unittest.TestCase):
|
||||
return text[text.index("INSTALLATION"):text.index("TROUBLESHOOTING")]
|
||||
|
||||
def test_retroarch_offers_the_windows_installer(self):
|
||||
from siteguides import INSTALL_PS1, INSTALL_SH
|
||||
|
||||
guide = self._guide("retroarch")
|
||||
self.assertIn("install.sh | sh", guide)
|
||||
self.assertIn("install.ps1 | iex", guide)
|
||||
self.assertIn(f"{INSTALL_SH} | sh", guide)
|
||||
self.assertIn(f"{INSTALL_PS1} | iex", guide)
|
||||
|
||||
def test_emudeck_keys_match_the_registry_and_the_flag(self):
|
||||
import re
|
||||
@@ -715,8 +725,10 @@ class PackReadmesSayWhatTheInstallerDoes(unittest.TestCase):
|
||||
|
||||
def test_retropie_never_sends_a_pc_to_the_linux_partition(self):
|
||||
guide = self._guide("retropie")
|
||||
from siteguides import INSTALL_SH
|
||||
|
||||
self.assertNotIn("Put the SD card in your PC", guide)
|
||||
self.assertIn("install.sh | sh", guide)
|
||||
self.assertIn(f"{INSTALL_SH} | sh", guide)
|
||||
|
||||
|
||||
class AutoFetchReadsListsAndPlacesWithoutClobbering(unittest.TestCase):
|
||||
|
||||
@@ -663,12 +663,14 @@ class ReadmeInstallerOptions(unittest.TestCase):
|
||||
import generate_readme
|
||||
|
||||
source = inspect.getsource(generate_readme.generate_readme)
|
||||
self.assertIn("&([scriptblock]::Create((irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1)))", source)
|
||||
self.assertIn('install_command("powershell", "--check")', source)
|
||||
self.assertIn("wiki/installer/#passing-options", source)
|
||||
self.assertIn("## Passing options", _page("installer.md"))
|
||||
readme = (ROOT / "README.md").read_text(encoding="utf-8")
|
||||
quick = _section(readme, "Quick install")
|
||||
self.assertIn("[scriptblock]::Create", quick)
|
||||
from siteguides import INSTALL_PS1
|
||||
|
||||
self.assertIn(f"&([scriptblock]::Create((irm {INSTALL_PS1}))) --check", quick)
|
||||
self.assertIn("wiki/installer/#passing-options", quick)
|
||||
self.assertNotRegex(readme, r"(?m)^irm \S+ \| iex \S")
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ class EndUsersCheckTheirOwnFolder(unittest.TestCase):
|
||||
def test_the_download_page_gives_the_one_liner_check(self):
|
||||
page = _which_pack()
|
||||
self.assertNotIn("from a checkout", page)
|
||||
self.assertIn("install.sh | sh -s -- --check", page)
|
||||
self.assertIn("/i | sh -s -- --check", page)
|
||||
|
||||
def test_the_readme_does_not_call_verify_a_check_of_your_bios(self):
|
||||
self.assertNotIn("Verify your BIOS collection", _read("README.md"))
|
||||
|
||||
@@ -1911,7 +1911,7 @@ class TestManualPlatformPrompt(unittest.TestCase):
|
||||
with unittest.mock.patch.dict(os.environ, {"RETROBIOS_BOOTSTRAP": "ps1"}):
|
||||
hint = "\n".join(install._manual_usage_hint("windows"))
|
||||
self.assertIn("--platform retroarch --dest", hint)
|
||||
self.assertIn("install.ps1", hint)
|
||||
self.assertIn("/i.ps1", hint)
|
||||
|
||||
|
||||
class TestSteamRetroarchDetection(unittest.TestCase):
|
||||
@@ -3983,7 +3983,7 @@ class HintsGiveTheUsersOwnCommandBack(unittest.TestCase):
|
||||
def test_the_shell_one_liner_is_rebuilt(self):
|
||||
with unittest.mock.patch.dict(os.environ, {"RETROBIOS_BOOTSTRAP": "sh"}):
|
||||
hint = "\n".join(install._manual_usage_hint("linux"))
|
||||
self.assertIn("install.sh | sh -s -- --platform retroarch", hint)
|
||||
self.assertIn("/i | sh -s -- --platform retroarch", hint)
|
||||
self.assertNotIn("python3 install.py", hint)
|
||||
self.assertIn(install.INSTALLER_PAGE, hint)
|
||||
|
||||
@@ -4028,7 +4028,7 @@ class HintsGiveTheUsersOwnCommandBack(unittest.TestCase):
|
||||
False, "retroarch")
|
||||
self.assertNotIn("sudo", advice)
|
||||
self.assertIn("administrator", advice)
|
||||
self.assertIn("install.ps1", advice)
|
||||
self.assertIn("/i.ps1", advice)
|
||||
self.assertIn("--system atari-lynx", advice)
|
||||
|
||||
def test_a_full_drive_keeps_the_platform(self):
|
||||
@@ -4070,7 +4070,7 @@ class HintsGiveTheUsersOwnCommandBack(unittest.TestCase):
|
||||
proc = _install_run(env, "--help")
|
||||
self.assertEqual(proc.returncode, 0)
|
||||
self.assertIn("examples:", proc.stdout)
|
||||
self.assertIn("install.sh | sh -s -- --platform batocera --dest", proc.stdout)
|
||||
self.assertIn("/i | sh -s -- --platform batocera --dest", proc.stdout)
|
||||
self.assertIn(install.INSTALLER_PAGE, proc.stdout)
|
||||
|
||||
|
||||
|
||||
@@ -9930,14 +9930,12 @@ class TheAndroidResultReadsWhole(unittest.TestCase):
|
||||
self.assertIn("--restore", words)
|
||||
self.assertTrue(all(not word.endswith("/") for word in words), words)
|
||||
state = _installed_state(Path("/sdcard/RetroArch/system"))
|
||||
command = install._styled("curl -fsSL https://raw.githubusercontent.com/Abdess/"
|
||||
"retrobios/main/install.sh | sh -s -- --restore "
|
||||
command = install._styled(f"curl -fsSL {install.BOOTSTRAP_URL} | sh -s -- --restore "
|
||||
"'/storage/emulated/0/a b/20261027-223524'", "command")
|
||||
state.stack.append("result")
|
||||
pieces = install._text_lines([command], 38, whole=True)
|
||||
joined = [str(piece).strip() for piece in pieces if piece.style != "cont"]
|
||||
self.assertIn("https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh",
|
||||
joined)
|
||||
self.assertIn(install.BOOTSTRAP_URL, joined)
|
||||
self.assertIn("'/storage/emulated/0/a b/20261027-223524'", joined)
|
||||
|
||||
def test_a_long_line_folds_in_the_terminal(self):
|
||||
@@ -10770,7 +10768,7 @@ class AuditNineteenAndTwelve(_CardFixtures, unittest.TestCase):
|
||||
lines = drawn(state, cols, 30)
|
||||
self.assertFalse(install.tui_wants_mouse(state, lines))
|
||||
if bootstrap:
|
||||
url = "https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh"
|
||||
url = install.BOOTSTRAP_URL
|
||||
words = [word for line in lines for word in str(line).split()]
|
||||
self.assertIn(url, words)
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@ class TheOneLinerFollowsTheRegistry(unittest.TestCase):
|
||||
continue
|
||||
text = _guide(name)
|
||||
with self.subTest(platform=name):
|
||||
self.assertEqual("install.sh | sh" in text, bool(systems - {"windows"}))
|
||||
self.assertEqual("install.ps1 | iex" in text, "windows" in systems)
|
||||
self.assertEqual("/i | sh" in text, bool(systems - {"windows"}))
|
||||
self.assertEqual("/i.ps1 | iex" in text, "windows" in systems)
|
||||
|
||||
def test_rocknix_and_mister_run_it_on_the_device(self):
|
||||
for name in ("rocknix", "misterfpga"):
|
||||
@@ -87,7 +87,7 @@ class TheOneLinerFollowsTheRegistry(unittest.TestCase):
|
||||
text = _guide("emudeck")
|
||||
self.assertNotIn("Steam Deck / Linux", text)
|
||||
self.assertNotIn("Konsole", text)
|
||||
self.assertIn("install.ps1 | iex", text)
|
||||
self.assertIn("/i.ps1 | iex", text)
|
||||
self.assertIn("C:\\Emulation\\bios", text)
|
||||
self.assertIn("--standalone-copies", text)
|
||||
|
||||
@@ -101,12 +101,14 @@ class TheOneLinerFollowsTheRegistry(unittest.TestCase):
|
||||
"""The address comes from install_base in platforms/_guides.yml, the
|
||||
one the site's commands use."""
|
||||
import packreadme
|
||||
import sitebootstrap
|
||||
from siteguides import INSTALL_BASE
|
||||
|
||||
source = (REPO_ROOT / "scripts" / "packreadme.py").read_text(encoding="utf-8")
|
||||
self.assertNotIn("raw.githubusercontent.com", source)
|
||||
self.assertEqual(packreadme.INSTALL_SH, f"{INSTALL_BASE}/install.sh")
|
||||
self.assertEqual(packreadme.INSTALL_PS1, f"{INSTALL_BASE}/install.ps1")
|
||||
self.assertNotIn("abdess.github.io", source)
|
||||
self.assertEqual(packreadme.INSTALL_SH, f"{INSTALL_BASE}/{sitebootstrap.SH_NAME}")
|
||||
self.assertEqual(packreadme.INSTALL_PS1, f"{INSTALL_BASE}/{sitebootstrap.PS1_NAME}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -321,7 +321,7 @@ class EveryPlatformHasAPermanentPage(unittest.TestCase):
|
||||
f"/releases/download/{record['tag']}/{asset}", page)
|
||||
elif name not in RELEASED:
|
||||
self.assertNotIn("_BIOS_Pack", page)
|
||||
self.assertIn(f"install.sh | sh -s -- --platform {name}", page)
|
||||
self.assertIn(f"/i | sh -s -- --platform {name}", page)
|
||||
self.assertIn("No ZIP for ", page)
|
||||
|
||||
def test_the_generator_writes_and_sweeps_them(self):
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
"""The one-line commands the site serves.
|
||||
|
||||
`curl -fsSL https://abdess.github.io/retrobios/i | sh` and the PowerShell
|
||||
`i.ps1` replaced a raw.githubusercontent.com address of 70 characters. The
|
||||
site deploys some eleven minutes after a push: a copy that pinned one
|
||||
commit's installer and downloaded another's would refuse every install for
|
||||
that long, so each copy reads install.py from the commit it was built from.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(REPO_ROOT))
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import install # noqa: E402
|
||||
import sitebootstrap # noqa: E402
|
||||
from siteguides import INSTALL_BASE, INSTALL_PS1, INSTALL_SH, install_command # noqa: E402
|
||||
|
||||
WEBSITE = REPO_ROOT / "website"
|
||||
DOCS = WEBSITE / "src" / "content" / "docs"
|
||||
OLD_SH = "raw.githubusercontent.com/Abdess/retrobios/main/install.sh"
|
||||
OLD_PS1 = "raw.githubusercontent.com/Abdess/retrobios/main/install.ps1"
|
||||
|
||||
|
||||
def _head() -> str:
|
||||
return subprocess.run(["git", "rev-parse", "HEAD"], cwd=REPO_ROOT, check=True,
|
||||
capture_output=True, text=True).stdout.strip()
|
||||
|
||||
|
||||
def _pin(text: str) -> str:
|
||||
match = re.search(r'(?:DEFAULT_INSTALL_SHA256="|\$defaultInstallSha256 = ")([0-9a-f]{64})"',
|
||||
text)
|
||||
assert match, "no installer pin"
|
||||
return match.group(1)
|
||||
|
||||
|
||||
class ServedCopies(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.commit = _head()
|
||||
|
||||
def test_each_copy_is_its_bootstrap_but_for_the_installer_address(self):
|
||||
for name, (source, _line) in sitebootstrap.SOURCES.items():
|
||||
with self.subTest(name=name):
|
||||
original = (REPO_ROOT / source).read_text(encoding="utf-8").splitlines()
|
||||
served = sitebootstrap.served_text(name, REPO_ROOT, self.commit).splitlines()
|
||||
self.assertEqual(len(original), len(served))
|
||||
changed = [(a, b) for a, b in zip(original, served) if a != b]
|
||||
self.assertEqual(len(changed), 1, changed)
|
||||
before, after = changed[0]
|
||||
self.assertIn("https://raw.githubusercontent.com/Abdess/retrobios/main/install.py",
|
||||
before)
|
||||
self.assertEqual(after, before.replace(
|
||||
"https://raw.githubusercontent.com/Abdess/retrobios/main/install.py",
|
||||
sitebootstrap.installer_url(self.commit)))
|
||||
|
||||
def test_the_address_names_the_commit_whose_installer_the_pin_holds(self):
|
||||
installer = hashlib.sha256((REPO_ROOT / "install.py").read_bytes()).hexdigest()
|
||||
url = f"https://raw.githubusercontent.com/Abdess/retrobios/{self.commit}/install.py"
|
||||
for name in sitebootstrap.SOURCES:
|
||||
with self.subTest(name=name):
|
||||
text = sitebootstrap.served_text(name, REPO_ROOT, self.commit)
|
||||
self.assertIn(f'"{url}"', text)
|
||||
self.assertEqual(_pin(text), installer)
|
||||
|
||||
def test_ci_builds_from_the_pushed_commit(self):
|
||||
pushed = "a" * 40
|
||||
with mock.patch.dict(os.environ, {"GITHUB_SHA": pushed}):
|
||||
self.assertEqual(sitebootstrap.build_commit(REPO_ROOT), pushed)
|
||||
with mock.patch.dict(os.environ, {"GITHUB_SHA": "main"}), \
|
||||
self.assertRaises(ValueError):
|
||||
sitebootstrap.build_commit(REPO_ROOT)
|
||||
with mock.patch.dict(os.environ, {"GITHUB_SHA": ""}):
|
||||
self.assertEqual(sitebootstrap.build_commit(REPO_ROOT), self.commit)
|
||||
|
||||
def test_the_copies_land_at_the_root_of_the_public_folder(self):
|
||||
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp" if (REPO_ROOT / "tmp").is_dir()
|
||||
else None) as scratch:
|
||||
public = Path(scratch) / "public"
|
||||
written = sitebootstrap.write_bootstraps(public, REPO_ROOT, self.commit)
|
||||
self.assertEqual(sorted(p.name for p in written), ["i", "i.ps1"])
|
||||
for path in written:
|
||||
self.assertEqual(path.read_text(encoding="utf-8"),
|
||||
sitebootstrap.served_text(path.name, REPO_ROOT, self.commit))
|
||||
|
||||
def test_a_bootstrap_without_its_address_line_stops_the_build(self):
|
||||
with tempfile.TemporaryDirectory(dir=REPO_ROOT / "tmp" if (REPO_ROOT / "tmp").is_dir()
|
||||
else None) as scratch:
|
||||
root = Path(scratch)
|
||||
(root / "install.sh").write_text("#!/bin/sh\n", encoding="utf-8")
|
||||
with self.assertRaises(ValueError):
|
||||
sitebootstrap.served_text("i", root, self.commit)
|
||||
|
||||
|
||||
class DocumentedCommands(unittest.TestCase):
|
||||
def test_the_addresses_are_the_served_paths(self):
|
||||
config = (WEBSITE / "astro.config.mjs").read_text(encoding="utf-8")
|
||||
site = re.search(r"const SITE = '([^']+)'", config).group(1)
|
||||
base = re.search(r"const BASE = '([^']+)'", config).group(1)
|
||||
self.assertEqual(f"{INSTALL_BASE}/", site + base)
|
||||
self.assertEqual(INSTALL_SH, f"{site}{base}{sitebootstrap.SH_NAME}")
|
||||
self.assertEqual(INSTALL_PS1, f"{site}{base}{sitebootstrap.PS1_NAME}")
|
||||
self.assertEqual(install.BOOTSTRAP_URL, INSTALL_SH)
|
||||
self.assertEqual(f"{install.BOOTSTRAP_URL}.ps1", INSTALL_PS1)
|
||||
|
||||
def test_the_installer_hints_give_the_short_command(self):
|
||||
with mock.patch.dict(os.environ, {"RETROBIOS_BOOTSTRAP": "sh"}):
|
||||
self.assertEqual(install.Invocation().launcher(), f"curl -fsSL {INSTALL_SH} | sh -s --")
|
||||
with mock.patch.dict(os.environ, {"RETROBIOS_BOOTSTRAP": "ps1"}):
|
||||
self.assertEqual(install.Invocation().launcher(),
|
||||
f"&([scriptblock]::Create((irm {INSTALL_PS1})))")
|
||||
|
||||
def test_every_page_a_user_copies_from_gives_the_short_command(self):
|
||||
sources = {
|
||||
"wiki/installer.md": [install_command("sh"), install_command("powershell")],
|
||||
"wiki/first-install.md": [install_command("sh"), install_command("powershell")],
|
||||
"wiki/faq.md": [f"curl -fsSL {INSTALL_SH} | sh -s -- --dry-run"],
|
||||
"wiki/troubleshooting.md": [install_command("sh", "--check")],
|
||||
"wiki/tools.md": [install_command("sh"), install_command("powershell")],
|
||||
"website/src/components/FilePicker.astro": [f"'curl -fsSL {INSTALL_SH} | sh -s --'"],
|
||||
"scripts/download.sh": [f"curl -fsSL {INSTALL_SH} | sh -s -- --platform <name>"],
|
||||
"scripts/download.py": [f"curl -fsSL {INSTALL_SH} | sh -s -- --platform <name>"],
|
||||
}
|
||||
for rel, commands in sources.items():
|
||||
text = (REPO_ROOT / rel).read_text(encoding="utf-8")
|
||||
for command in commands:
|
||||
with self.subTest(file=rel, command=command):
|
||||
self.assertIn(command, text)
|
||||
|
||||
def test_the_raw_form_is_written_once_as_the_fallback(self):
|
||||
"""The long address stays on the installer page only, for the day
|
||||
the site is down."""
|
||||
surfaces = [*sorted((REPO_ROOT / "wiki").glob("*.md")), REPO_ROOT / "README.md",
|
||||
REPO_ROOT / "CONTRIBUTING.md", REPO_ROOT / "install.py",
|
||||
*sorted((REPO_ROOT / "scripts").rglob("*.py")),
|
||||
REPO_ROOT / "scripts" / "download.sh",
|
||||
*sorted((WEBSITE / "src" / "components").glob("*.astro"))]
|
||||
found = {
|
||||
str(path.relative_to(REPO_ROOT)): text.count(OLD_SH) + text.count(OLD_PS1)
|
||||
for path in surfaces
|
||||
for text in [path.read_text(encoding="utf-8")]
|
||||
if OLD_SH in text or OLD_PS1 in text
|
||||
}
|
||||
self.assertEqual(found, {"wiki/installer.md": 2})
|
||||
|
||||
def test_typed_by_hand_without_the_scheme(self):
|
||||
page = (REPO_ROOT / "wiki" / "installer.md").read_text(encoding="utf-8")
|
||||
bare = INSTALL_BASE.removeprefix("https://")
|
||||
self.assertIn(f"curl -fL {bare}/{sitebootstrap.SH_NAME} | sh", page)
|
||||
self.assertIn(f"irm {bare}/{sitebootstrap.PS1_NAME} | iex", page)
|
||||
|
||||
|
||||
class NothingElseAtTheseAddresses(unittest.TestCase):
|
||||
"""A page or a folder named i would take the address, or turn it into a
|
||||
redirect to i/ that `curl | sh` reads as HTML."""
|
||||
|
||||
NAMES = tuple(sitebootstrap.SOURCES)
|
||||
|
||||
def test_no_source_page_takes_the_names(self):
|
||||
for folder in (REPO_ROOT / "wiki", WEBSITE / "src" / "pages"):
|
||||
taken = sorted(str(path.relative_to(REPO_ROOT)) for path in folder.glob("i*")
|
||||
if path.name == "i" or path.name.split(".", 1)[0] == "i")
|
||||
with self.subTest(folder=str(folder.relative_to(REPO_ROOT))):
|
||||
self.assertEqual(taken, [])
|
||||
|
||||
@unittest.skipUnless(DOCS.is_dir(), "site not generated")
|
||||
def test_no_generated_page_or_redirect_takes_the_names(self):
|
||||
for name in self.NAMES:
|
||||
stem = name.split(".", 1)[0]
|
||||
with self.subTest(name=name):
|
||||
self.assertFalse((DOCS / stem).exists())
|
||||
self.assertEqual(sorted(DOCS.glob(f"{stem}.md*")), [])
|
||||
redirects = WEBSITE / "src" / "generated" / "redirects.json"
|
||||
if redirects.is_file():
|
||||
sources = {key.strip("/").removeprefix("retrobios/")
|
||||
for key in json.loads(redirects.read_text(encoding="utf-8"))}
|
||||
self.assertEqual(sources & set(self.NAMES), set())
|
||||
|
||||
@unittest.skipUnless((WEBSITE / "dist" / "index.html").is_file(), "site not built")
|
||||
def test_the_built_site_serves_files_at_the_addresses(self):
|
||||
for name in self.NAMES:
|
||||
path = WEBSITE / "dist" / name
|
||||
with self.subTest(name=name):
|
||||
self.assertTrue(path.is_file())
|
||||
text = path.read_text(encoding="utf-8")
|
||||
self.assertRegex(text, r"raw\.githubusercontent\.com/Abdess/retrobios/[0-9a-f]{40}/"
|
||||
r"install\.py")
|
||||
|
||||
|
||||
class GeneratedSurfaces(unittest.TestCase):
|
||||
def test_a_shorter_address_moves_no_section(self):
|
||||
"""The install boxes weigh in the split of a manufacturer's page,
|
||||
fitted on the 66-character raw address: weighed at their new length,
|
||||
Nintendo DS fell back onto the Nintendo summary and lost its page."""
|
||||
import generate_site
|
||||
|
||||
tail = " | sh -s -- --check"
|
||||
self.assertEqual(generate_site._install_box_weight([f"curl -fsSL {INSTALL_SH}{tail}"]),
|
||||
len("curl -fsSL ") + 66 + len(tail) + 1)
|
||||
line = f"&([scriptblock]::Create((irm {INSTALL_PS1}))) --check"
|
||||
self.assertEqual(generate_site._install_box_weight([line]),
|
||||
len(line) - len(INSTALL_PS1) + 67 + 1)
|
||||
|
||||
def test_the_readme_shows_the_short_form(self):
|
||||
readme = (REPO_ROOT / "README.md").read_text(encoding="utf-8")
|
||||
quick = readme.split("## Quick install", 1)[1].split("\n## ", 1)[0]
|
||||
self.assertIn(f"\n{install_command('sh')}\n", quick)
|
||||
self.assertIn(f"\n{install_command('powershell')}\n", quick)
|
||||
self.assertNotIn("raw.githubusercontent.com", quick)
|
||||
|
||||
@unittest.skipUnless(DOCS.is_dir(), "site not generated")
|
||||
def test_the_site_shows_the_short_form(self):
|
||||
pages = {path: path.read_text(encoding="utf-8") for path in DOCS.rglob("*.md*")}
|
||||
self.assertTrue(any(INSTALL_SH in text for text in pages.values()))
|
||||
long_form = sorted(str(path.relative_to(DOCS)) for path, text in pages.items()
|
||||
if OLD_SH in text or OLD_PS1 in text)
|
||||
self.assertEqual(long_form, ["wiki/installer.md"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -207,10 +207,10 @@ class AnInstallBoxShowsOneCommand(unittest.TestCase):
|
||||
listed = self.text[self.text.index('<details class="rb-install-all" open>'):]
|
||||
for platform, _system in PAIRS:
|
||||
self.assertEqual(listed.count(f"--platform {platform} --system"), 2, platform)
|
||||
self.assertIn("install.sh | sh -s -- --platform retrobat --system 'Neo Geo'</code>", listed)
|
||||
self.assertIn("install.ps1))) --platform retrobat --system 'Neo Geo'</code>", listed)
|
||||
self.assertIn("/i | sh -s -- --platform retrobat --system 'Neo Geo'</code>", listed)
|
||||
self.assertIn("/i.ps1))) --platform retrobat --system 'Neo Geo'</code>", listed)
|
||||
# The URL is no link inside the command, and the break adds nothing.
|
||||
self.assertIn("https://<wbr>raw.githubusercontent.com", listed)
|
||||
self.assertIn("https://<wbr>abdess.github.io", listed)
|
||||
|
||||
def test_one_setup_keeps_its_two_blocks(self) -> None:
|
||||
text = "\n".join(gs._console_install_block("PlayStation Portable", PAIRS[:1]))
|
||||
@@ -225,7 +225,7 @@ class AnInstallBoxShowsOneCommand(unittest.TestCase):
|
||||
text = "\n".join(gs._console_install_block("PlayStation Portable", PAIRS))
|
||||
self.assertNotIn("<select", text)
|
||||
self.assertEqual(text.count("# RetroBat"), 2)
|
||||
self.assertEqual(text.count("install.sh | sh"), 3)
|
||||
self.assertEqual(text.count("/i | sh"), 3)
|
||||
|
||||
|
||||
class TheSetupListLooksLikeAList(unittest.TestCase):
|
||||
|
||||
@@ -902,8 +902,8 @@ class AnEmulatorPageOffersItsInstall(unittest.TestCase):
|
||||
|
||||
profile = yaml.safe_load((ROOT / "emulators" / "pcem.yml").read_text(encoding="utf-8"))
|
||||
text = "\n".join(_install_block("pcem", profile))
|
||||
self.assertIn("install.sh | sh -s -- --emulator pcem", text)
|
||||
self.assertIn("install.ps1))) --emulator pcem", text)
|
||||
self.assertIn("/i | sh -s -- --emulator pcem", text)
|
||||
self.assertIn("/i.ps1))) --emulator pcem", text)
|
||||
self.assertEqual(_install_block("x", {"type": "alias"}), [])
|
||||
|
||||
def test_the_installer_takes_the_option(self):
|
||||
@@ -969,7 +969,7 @@ class TheOneLinerIsWhereUsersLand(unittest.TestCase):
|
||||
|
||||
page = generate_site.generate_which_pack()
|
||||
self.assertIn("Android (Termux", page)
|
||||
self.assertIn("install.ps1))) --check", page)
|
||||
self.assertIn("/i.ps1))) --check", page)
|
||||
self.assertIn("--platform retroarch --dest", page)
|
||||
|
||||
def test_platform_and_system_pages(self):
|
||||
@@ -979,14 +979,14 @@ class TheOneLinerIsWhereUsersLand(unittest.TestCase):
|
||||
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("/i | sh -s -- --platform batocera", page)
|
||||
# Batocera runs the command on the device itself, so its page opens on
|
||||
# the computer route. A platform detected on Windows gets the
|
||||
# PowerShell form, and its command comes before the ZIP.
|
||||
cov = dict(cov, platform="RetroArch", mode="existence")
|
||||
page = generate_site.generate_platform_page("retroarch", cov)
|
||||
self.assertIn("install.ps1))) --platform retroarch", page)
|
||||
self.assertIn("install.sh | sh -s -- --platform retroarch", page)
|
||||
self.assertIn("/i.ps1))) --platform retroarch", page)
|
||||
self.assertIn("/i | sh -s -- --platform retroarch", page)
|
||||
self.assertLess(page.index("--platform retroarch"), page.index("rb-button"))
|
||||
|
||||
entry = {"name": "a.bin", "sha1": "a" * 40, "md5": "b" * 32, "size": 3,
|
||||
|
||||
@@ -279,23 +279,23 @@ class CommandBlocks(unittest.TestCase):
|
||||
def test_shell_lines_have_a_powershell_form(self):
|
||||
text = _page("which-pack.md")
|
||||
groups = re.findall(r"<Tabs.*?</Tabs>|<details.*?</details>", text, re.S)
|
||||
commands = [group for group in groups if "install.sh | sh" in group]
|
||||
commands = [group for group in groups if "/i | sh" in group]
|
||||
self.assertGreaterEqual(len(commands), 3)
|
||||
for group in commands:
|
||||
self.assertIn("install.ps1", group)
|
||||
self.assertIn("/i.ps1", group)
|
||||
block = "\n".join(generate_site._console_install_block(
|
||||
"PlayStation", [("batocera", "psx"), ("recalbox", "psx")]))
|
||||
# Every setup's options in both forms: the list of every command,
|
||||
# and the one command under the OS tabs.
|
||||
sh_args = re.findall(r"install\.sh \| sh -s -- ([^<\n]+)", block)
|
||||
ps_args = re.findall(r"install\.ps1\)\)\) ([^<\n]+)", block)
|
||||
sh_args = re.findall(r"/i \| sh -s -- ([^<\n]+)", block)
|
||||
ps_args = re.findall(r"/i\.ps1\)\)\) ([^<\n]+)", block)
|
||||
self.assertEqual(sorted(set(sh_args)), sorted(set(ps_args)))
|
||||
self.assertEqual(len(set(sh_args)), 2)
|
||||
self.assertIn('```powershell title="PowerShell"', block)
|
||||
for key, guide in siteguides.GUIDES.items():
|
||||
if "windows" in guide.run:
|
||||
with self.subTest(platform=key):
|
||||
self.assertIn("install.ps1", _guide_part(_page(f"platforms/{key}.md")))
|
||||
self.assertIn("/i.ps1", _guide_part(_page(f"platforms/{key}.md")))
|
||||
|
||||
|
||||
class ContributingOpensOnTheAction(unittest.TestCase):
|
||||
|
||||
@@ -22,7 +22,7 @@ sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
sys.path.insert(0, str(REPO_ROOT))
|
||||
|
||||
import sitediagrams # noqa: E402
|
||||
from siteguides import INSTALL_BASE # noqa: E402
|
||||
from siteguides import INSTALL_SH # noqa: E402
|
||||
|
||||
from tests import sitepages # noqa: E402
|
||||
|
||||
@@ -108,7 +108,7 @@ class InstallCommandsHaveEveryForm(unittest.TestCase):
|
||||
def test_a_windows_path_replaces_the_unix_one(self):
|
||||
page = (
|
||||
"<!-- os-tabs windows: --platform retroarch --dest E:\\x -->\n```sh\n"
|
||||
f"curl -fsSL {INSTALL_BASE}/install.sh \\\n | sh -s -- --platform retroarch --dest /a/b\n```\n"
|
||||
f"curl -fsSL {INSTALL_SH} \\\n | sh -s -- --platform retroarch --dest /a/b\n```\n"
|
||||
)
|
||||
text = sitediagrams.expand_commands(page)
|
||||
windows, rest = text.split('<TabItem label="macOS">', 1)
|
||||
|
||||
@@ -22,7 +22,7 @@ sys.path.insert(0, str(REPO_ROOT))
|
||||
sys.path.insert(0, str(REPO_ROOT / "scripts"))
|
||||
|
||||
import sitediagrams # noqa: E402
|
||||
from siteguides import INSTALL_BASE # noqa: E402
|
||||
from siteguides import INSTALL_SH # noqa: E402
|
||||
from tests import sitepages # noqa: E402
|
||||
|
||||
WIKI = REPO_ROOT / "wiki"
|
||||
@@ -113,7 +113,7 @@ class OsTabsTakeOptionsPerSystem(unittest.TestCase):
|
||||
"""The marker gives any tab its own options, and refuses words it cannot read."""
|
||||
|
||||
PAGE = ("<!-- os-tabs windows: --dest E:\\x macos: --dest /m steamdeck: --dest /d -->\n"
|
||||
f"```sh\ncurl -fsSL {INSTALL_BASE}/install.sh | sh -s -- --dest /l\n```\n")
|
||||
f"```sh\ncurl -fsSL {INSTALL_SH} | sh -s -- --dest /l\n```\n")
|
||||
|
||||
def test_each_named_tab_uses_its_own(self):
|
||||
parts = re.split(r'<TabItem label="([^"]+)">', sitediagrams.expand_commands(self.PAGE))
|
||||
|
||||
@@ -68,7 +68,7 @@ const packs = Object.fromEntries(
|
||||
|
||||
type Release = { tag: string; packs: Record<string, { files: number; size: number }> };
|
||||
|
||||
const INSTALL = 'curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s --';
|
||||
const INSTALL = 'curl -fsSL https://abdess.github.io/retrobios/i | sh -s --';
|
||||
const MAX_ROWS = 500;
|
||||
// A word that holds a hyphen (--platform, nintendo-64dd) never breaks
|
||||
// inside: the same rule as the command blocks (ec-unbroken-tokens.mjs).
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ terms: ios, iphone, ipad
|
||||
|
||||
<!-- os-tabs -->
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --dry-run
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --dry-run
|
||||
```
|
||||
|
||||
## Do I need every file of a console?
|
||||
@@ -83,7 +83,7 @@ with the card in drive `E:`, run:
|
||||
|
||||
<!-- os-tabs windows: --platform retroarch --dest E:\retroarch\cores\system -->
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh \
|
||||
curl -fsSL https://abdess.github.io/retrobios/i \
|
||||
| sh -s -- --platform retroarch --dest /path/to/sdcard/retroarch/cores/system
|
||||
```
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ line, then press Enter.
|
||||
Open the Start menu. Type **PowerShell** and open it.
|
||||
|
||||
```powershell title="PowerShell"
|
||||
irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1 | iex
|
||||
irm https://abdess.github.io/retrobios/i.ps1 | iex
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -29,7 +29,7 @@ irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1 | iex
|
||||
Open **Terminal**, in Applications, then Utilities.
|
||||
|
||||
```sh title="Terminal"
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -38,7 +38,7 @@ curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh |
|
||||
Open a terminal.
|
||||
|
||||
```sh title="Terminal"
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -47,7 +47,7 @@ curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh |
|
||||
Switch to Desktop Mode and open **Konsole**.
|
||||
|
||||
```sh title="Terminal"
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -58,7 +58,7 @@ Install **Termux**, a terminal app, and run these lines.
|
||||
```sh title="Terminal"
|
||||
pkg install python
|
||||
termux-setup-storage
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
@@ -120,7 +120,7 @@ Add `--check` to the same command. It changes nothing.
|
||||
|
||||
<!-- os-tabs -->
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --check
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --check
|
||||
```
|
||||
|
||||
All good:
|
||||
@@ -154,7 +154,7 @@ $home/RetroPie/BIOS as "bios", and RetroPie docs/Transferring-Roms.md:29 names
|
||||
|
||||
<!-- os-tabs windows: --platform retropie --dest \\RETROPIE\bios\ -->
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --platform retropie --dest <the bios share>
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --platform retropie --dest <the bios share>
|
||||
```
|
||||
|
||||
### Error: the downloaded installer does not match its expected fingerprint.
|
||||
|
||||
+34
-7
@@ -29,11 +29,38 @@ Each links to its row in the [Options](#options) table.
|
||||
|
||||
```bash
|
||||
# Linux / macOS / Steam Deck
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
```
|
||||
|
||||
```powershell
|
||||
# Windows
|
||||
irm https://abdess.github.io/retrobios/i.ps1 | iex
|
||||
```
|
||||
|
||||
Typed by hand, the address works without `https://`:
|
||||
|
||||
```bash
|
||||
curl -fL abdess.github.io/retrobios/i | sh
|
||||
```
|
||||
|
||||
```powershell
|
||||
irm abdess.github.io/retrobios/i.ps1 | iex
|
||||
```
|
||||
|
||||
The `https://` form is safer, because without it the first request goes out
|
||||
in clear text and the redirect it gets back could be replaced on the way.
|
||||
|
||||
`i` and `i.ps1` are `install.sh` and `install.ps1` as of the commit the site
|
||||
was built from, set to download `install.py` from that same commit. The
|
||||
fingerprint they check and the installer they fetch never come from two
|
||||
different versions. If the site is down, the repository serves the same
|
||||
bootstraps:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
```
|
||||
|
||||
```powershell
|
||||
irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1 | iex
|
||||
```
|
||||
|
||||
@@ -68,7 +95,7 @@ What `RETROBIOS_REF` does not pin is listed under
|
||||
The shell bootstrap forwards everything after `-s --`:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh \
|
||||
curl -fsSL https://abdess.github.io/retrobios/i \
|
||||
| sh -s -- --platform retroarch --dest /path/to/sdcard/retroarch/cores/system
|
||||
```
|
||||
|
||||
@@ -84,7 +111,7 @@ rejected with `A positional parameter cannot be found`. Building a script block
|
||||
from the text and calling it passes the arguments through:
|
||||
|
||||
```powershell
|
||||
&([scriptblock]::Create((irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1))) `
|
||||
&([scriptblock]::Create((irm https://abdess.github.io/retrobios/i.ps1))) `
|
||||
--platform retroarch --dest D:\bios
|
||||
```
|
||||
|
||||
@@ -1258,7 +1285,7 @@ Running `termux-setup-storage` once is what makes the shared one reachable:
|
||||
```bash
|
||||
pkg install python
|
||||
termux-setup-storage
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
```
|
||||
|
||||
`EXTERNAL_STORAGE` overrides the storage root for a device that mounts it
|
||||
@@ -1735,12 +1762,12 @@ The installer finds a platform from the config files it knows. When it finds
|
||||
none, name the platform and its BIOS folder:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --platform retroarch --dest ~/.config/retroarch/system
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --platform batocera --dest /userdata/bios
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --platform retroarch --dest ~/.config/retroarch/system
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --platform batocera --dest /userdata/bios
|
||||
```
|
||||
|
||||
```powershell
|
||||
&([scriptblock]::Create((irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1))) `
|
||||
&([scriptblock]::Create((irm https://abdess.github.io/retrobios/i.ps1))) `
|
||||
--platform retroarch --dest C:\RetroArch-Win64\system
|
||||
```
|
||||
|
||||
|
||||
@@ -64,10 +64,7 @@ Every module in `tests/`, by the area it guards.
|
||||
| Pack probe | the cores a platform runs for a system, staging grown to a fixed point, runs mapped to pack and manifest entries, the line and its schema, the probe lock per run, inert run output on the site. One class builds a real pack narrowed to PlayStation and lays it out both ways, from the archive and with `install.py --dest`, and skips without the collection | `test_pack_probe` |
|
||||
| Automation runner, disk cleanup, asset sync and bit-flip repair | fake jobs, git repositories with a bare origin and session worktrees, a committed file with one bit flipped, canned CI runs in place of `gh` | `test_autorun` |
|
||||
| Truth and native export | synthetic + cached originals | `test_export_counts`, `test_export_native_layer`, `test_export_report`, `test_exporters`, `test_truth_data_dir_readers`, `test_truth_diff`, `test_truth_layout` |
|
||||
| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_availability`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_emulator_page`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_picker`, `test_site_platform_hashes`, `test_site_r2_components`, `test_site_r2_css`, `test_site_r2_emulators`, `test_site_r2_entry`, `test_site_r2_gate`, `test_site_r2_nav`, `test_site_r2_p0`, `test_site_r2_reference`, `test_site_r2_systems`, `test_site_r2_wiki`, `test_site_r3_collection`, `test_site_r3_components`, `test_site_r3_css`, `test_site_r3_emulators`, `test_site_r3_entry`, `test_site_r3_nav`, `test_site_r3_p0`, `test_site_r3_reference`, `test_site_r3_systems`, `test_site_r3_wiki`, `test_site_r4_components`, `test_site_r4_css`, `test_site_r4_emulators`, `test_site_r4_entry`, `test_site_r4_merge`, `test_site_r4_p0`, `test_site_r4_reference`, `test_site_r4_search`, `test_site_r4_systems`, `test_site_r4_wiki`, `test_site_r5_components`, `test_site_r5_css`, `test_site_r5_emulators`, `test_site_r5_entry`, `test_site_r5_reference`, `test_site_r5_search`, `test_site_r5_systems`, `test_site_r5_wiki`, `test_site_render`, `test_site_system_links`, `test_site_validation`, `test_site_weight`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_version_basis`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_workflow_paths` |
|
||||
| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_availability`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_emulator_page`, `test_site_entry_tables`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_picker`, `test_site_platform_hashes`, `test_site_r2_components`, `test_site_r2_css`, `test_site_r2_emulators`, `test_site_r2_entry`, `test_site_r2_gate`, `test_site_r2_nav`, `test_site_r2_p0`, `test_site_r2_reference`, `test_site_r2_systems`, `test_site_r2_wiki`, `test_site_r3_collection`, `test_site_r3_components`, `test_site_r3_css`, `test_site_r3_emulators`, `test_site_r3_entry`, `test_site_r3_nav`, `test_site_r3_p0`, `test_site_r3_reference`, `test_site_r3_systems`, `test_site_r3_wiki`, `test_site_r4_components`, `test_site_r4_css`, `test_site_r4_emulators`, `test_site_r4_entry`, `test_site_r4_merge`, `test_site_r4_p0`, `test_site_r4_reference`, `test_site_r4_search`, `test_site_r4_systems`, `test_site_r4_wiki`, `test_site_r5_components`, `test_site_r5_css`, `test_site_r5_emulators`, `test_site_r5_entry`, `test_site_r5_reference`, `test_site_r5_search`, `test_site_r5_systems`, `test_site_r5_wiki`, `test_site_render`, `test_site_system_links`, `test_site_validation`, `test_site_weight`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_workflow_paths` |
|
||||
| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_availability`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_density_cards`, `test_site_emulator_page`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_picker`, `test_site_platform_hashes`, `test_site_r2_components`, `test_site_r2_css`, `test_site_r2_emulators`, `test_site_r2_entry`, `test_site_r2_gate`, `test_site_r2_nav`, `test_site_r2_p0`, `test_site_r2_reference`, `test_site_r2_systems`, `test_site_r2_wiki`, `test_site_r3_collection`, `test_site_r3_components`, `test_site_r3_css`, `test_site_r3_emulators`, `test_site_r3_entry`, `test_site_r3_nav`, `test_site_r3_p0`, `test_site_r3_reference`, `test_site_r3_systems`, `test_site_r3_wiki`, `test_site_r4_components`, `test_site_r4_css`, `test_site_r4_emulators`, `test_site_r4_entry`, `test_site_r4_merge`, `test_site_r4_p0`, `test_site_r4_reference`, `test_site_r4_search`, `test_site_r4_systems`, `test_site_r4_wiki`, `test_site_r5_components`, `test_site_r5_css`, `test_site_r5_emulators`, `test_site_r5_entry`, `test_site_r5_reference`, `test_site_r5_search`, `test_site_r5_systems`, `test_site_r5_wiki`, `test_site_render`, `test_site_system_links`, `test_site_validation`, `test_site_weight`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_version_basis`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_workflow_paths` |
|
||||
| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_availability`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_emulator_page`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_picker`, `test_site_platform_density`, `test_site_platform_hashes`, `test_site_r2_components`, `test_site_r2_css`, `test_site_r2_emulators`, `test_site_r2_entry`, `test_site_r2_gate`, `test_site_r2_nav`, `test_site_r2_p0`, `test_site_r2_reference`, `test_site_r2_systems`, `test_site_r2_wiki`, `test_site_r3_collection`, `test_site_r3_components`, `test_site_r3_css`, `test_site_r3_emulators`, `test_site_r3_entry`, `test_site_r3_nav`, `test_site_r3_p0`, `test_site_r3_reference`, `test_site_r3_systems`, `test_site_r3_wiki`, `test_site_r4_components`, `test_site_r4_css`, `test_site_r4_emulators`, `test_site_r4_entry`, `test_site_r4_merge`, `test_site_r4_p0`, `test_site_r4_reference`, `test_site_r4_search`, `test_site_r4_systems`, `test_site_r4_wiki`, `test_site_r5_components`, `test_site_r5_css`, `test_site_r5_emulators`, `test_site_r5_entry`, `test_site_r5_reference`, `test_site_r5_search`, `test_site_r5_systems`, `test_site_r5_wiki`, `test_site_render`, `test_site_system_links`, `test_site_validation`, `test_site_weight`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_workflow_paths` |
|
||||
| Scrapers, upstream and provenance | inline sources, synthetic | `test_batocera_md5_list`, `test_check_freshness`, `test_contributing_text`, `test_coverage_cache`, `test_data_dir_refs`, `test_dataclass_defaults`, `test_dedup`, `test_doc_pages`, `test_doc_surfaces`, `test_elec_scrapers`, `test_fbneo_parser`, `test_find_pinned_revision`, `test_hash_merge`, `test_launchers`, `test_launchers_emudeck`, `test_launchers_recalbox`, `test_launchers_retrobat`, `test_launchers_retrodeck`, `test_launchers_retropie`, `test_launchers_rocknix`, `test_mame_devices`, `test_mame_parser`, `test_mame_ref_audit`, `test_optional_dependencies`, `test_profile_sync`, `test_prose_style`, `test_provenance`, `test_refresh_data_dirs`, `test_refresh_failures`, `test_refresh_stale`, `test_retrodeck_scraper_errors`, `test_romset_recipes`, `test_schema_semantic_gate`, `test_scraper_contract`, `test_site_availability`, `test_site_bootstrap`, `test_site_content_data`, `test_site_copy`, `test_site_cores`, `test_site_density_cards`, `test_site_emulator_page`, `test_site_entry_tables`, `test_site_exports`, `test_site_funnel`, `test_site_images`, `test_site_picker`, `test_site_platform_density`, `test_site_platform_hashes`, `test_site_r2_components`, `test_site_r2_css`, `test_site_r2_emulators`, `test_site_r2_entry`, `test_site_r2_gate`, `test_site_r2_nav`, `test_site_r2_p0`, `test_site_r2_reference`, `test_site_r2_systems`, `test_site_r2_wiki`, `test_site_r3_collection`, `test_site_r3_components`, `test_site_r3_css`, `test_site_r3_emulators`, `test_site_r3_entry`, `test_site_r3_nav`, `test_site_r3_p0`, `test_site_r3_reference`, `test_site_r3_systems`, `test_site_r3_wiki`, `test_site_r4_components`, `test_site_r4_css`, `test_site_r4_emulators`, `test_site_r4_entry`, `test_site_r4_merge`, `test_site_r4_p0`, `test_site_r4_reference`, `test_site_r4_search`, `test_site_r4_systems`, `test_site_r4_wiki`, `test_site_r5_components`, `test_site_r5_css`, `test_site_r5_emulators`, `test_site_r5_entry`, `test_site_r5_reference`, `test_site_r5_search`, `test_site_r5_systems`, `test_site_r5_wiki`, `test_site_render`, `test_site_system_links`, `test_site_validation`, `test_site_weight`, `test_site_write`, `test_source_ref_targets`, `test_upstream`, `test_upstream_index`, `test_validate_pr`, `test_validate_pr_flow`, `test_validate_pr_hashes`, `test_version_basis`, `test_workflow_paths` |
|
||||
|
||||
## Test architecture
|
||||
|
||||
|
||||
+2
-2
@@ -1320,10 +1320,10 @@ Cross-platform BIOS installer for end users:
|
||||
python install.py
|
||||
|
||||
# One-line automatic install (Linux/macOS)
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh
|
||||
|
||||
# One-line automatic install (Windows PowerShell)
|
||||
irm https://raw.githubusercontent.com/Abdess/retrobios/main/install.ps1 | iex
|
||||
irm https://abdess.github.io/retrobios/i.ps1 | iex
|
||||
```
|
||||
|
||||
`install.py` auto-detects the user's platform by checking config files,
|
||||
|
||||
@@ -22,7 +22,7 @@ missing**? Run the command with `--check`. It changes nothing.
|
||||
|
||||
<!-- os-tabs -->
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --check
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --check
|
||||
```
|
||||
|
||||
Read the count line:
|
||||
@@ -85,7 +85,7 @@ cites them. Steam Deck: the Flatpak build. -->
|
||||
|
||||
<!-- os-tabs windows: --platform retroarch --dest C:\RetroArch-Win64\system macos: --platform retroarch --dest ~/Documents/RetroArch/system steamdeck: --platform retroarch --dest ~/.var/app/org.libretro.RetroArch/config/retroarch/system -->
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/Abdess/retrobios/main/install.sh | sh -s -- --platform retroarch --dest ~/.config/retroarch/system
|
||||
curl -fsSL https://abdess.github.io/retrobios/i | sh -s -- --platform retroarch --dest ~/.config/retroarch/system
|
||||
```
|
||||
|
||||
Other messages: [When something goes wrong](installer.md#when-something-goes-wrong).
|
||||
|
||||
Reference in new issue
Block a user