mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-11 12:33:53 -06:00
docs: tell agents how to drive the installer
llms.txt now gives the package commands, the plan as JSON events from --dry-run --events, the exit codes and the plan, consent, act pattern with --yes. Tests hold each fact to install.py and to the exit code table of the installer page.
This commit is contained in:
1 parent
92546794e2
commit
4c0c480bdd
2 files changed
+84
No files matched your search
@@ -19,6 +19,8 @@ import json
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
|
||||
from siteguides import PACKAGE_COMMAND
|
||||
|
||||
SITE = "https://abdess.github.io/retrobios/"
|
||||
PUBLISHER = "abdess.github.io"
|
||||
SH = "curl -fsSL https://abdess.github.io/retrobios/i | sh -s --"
|
||||
@@ -31,6 +33,22 @@ QUOTING = (f"A folder goes in single quotes, attached to its option. One that ho
|
||||
"command. On a page of the site, the `device_setup_command` WebMCP tool writes the "
|
||||
"command and refuses such a folder.")
|
||||
|
||||
UVX = PACKAGE_COMMAND
|
||||
# EVENT_SCHEMA of install.py, and the schema generate_event_schema.py writes
|
||||
# from EVENT_KEYS, as the site publishes it. tests/test_site_agents.py holds
|
||||
# both to install.py.
|
||||
EVENT_SCHEMA = 1
|
||||
EVENTS_SCHEMA_PATH = "api/v1/schemas/install-events.schema.json"
|
||||
# Every code of the Exit status table of wiki/installer.md, in short.
|
||||
EXIT_CODES = (
|
||||
(0, "everything asked for is installed or already in place"),
|
||||
(1, "a failure: a download, a file list or a folder that cannot be used"),
|
||||
(2, "nothing was done: an option refused, every file failed, or the user quit"),
|
||||
(3, "`--check` found files missing or wrong, nothing having failed"),
|
||||
(130, "stopped with Ctrl+C"),
|
||||
(141, "the output went to a reader that closed early"),
|
||||
)
|
||||
|
||||
START_PAGES = (
|
||||
("Which pack is needed", "which-pack/", "pick a device or setup, get the folder its BIOS files go in, "
|
||||
"one command that installs and checks them, or a ZIP pack"),
|
||||
@@ -108,6 +126,23 @@ def llms_txt(platforms: list[tuple[str, str]]) -> str:
|
||||
"- A game the installer cannot identify gets every BIOS of its system, and the run names it.",
|
||||
f"- The same steps as an Agent Skill: [SKILL.md]({SITE}{SKILL_PATH}).",
|
||||
"",
|
||||
"## Driving the installer for a user",
|
||||
"",
|
||||
f"With Python 3.8 or newer, the installer is also a package: `{UVX} --for-games "
|
||||
f"--required-only --dry-run`, or `pipx run retrobios` with the same options.",
|
||||
"",
|
||||
"1. Plan: add `--dry-run --events`. Nothing is written, and standard output carries "
|
||||
f"one JSON object per line, event schema {EVENT_SCHEMA} "
|
||||
f"([JSON Schema]({SITE}{EVENTS_SCHEMA_PATH})): a `file` event per file with its "
|
||||
"`state`, `check` with the totals and `bytes_needed`, then `summary` with `pending`.",
|
||||
"2. Consent: show the user what will be installed, where, and the size, and ask.",
|
||||
"3. Act, only on a yes: the same command with `--yes` in place of `--dry-run`.",
|
||||
"",
|
||||
"- `--yes` asks nothing: use it in a run without a terminal. A run that needs an "
|
||||
"answer (two setups found, none found) stops and names the option to give.",
|
||||
"- The exit code, also the `code` of the last `end` event:",
|
||||
*(f" - `{code}`: {meaning}" for code, meaning in EXIT_CODES),
|
||||
"",
|
||||
"## Start",
|
||||
"",
|
||||
*(f"- [{title}]({SITE}{path}): {what}" for title, path, what in START_PAGES),
|
||||
|
||||
@@ -50,6 +50,55 @@ class TheGuideLeadsToTheFewestFiles(unittest.TestCase):
|
||||
self.assertTrue(target.is_file() or (target / "index.html").is_file(), url)
|
||||
|
||||
|
||||
class TheGuideDrivesTheInstaller(unittest.TestCase):
|
||||
"""What an agent needs to run the installer for a user: the package
|
||||
command, the plan as JSON, the exit codes, and plan, consent, act. Each
|
||||
fact is held to install.py, so the guide cannot drift from it."""
|
||||
|
||||
def setUp(self) -> None:
|
||||
import generate_installer_doc # noqa: PLC0415
|
||||
|
||||
self.doc = generate_installer_doc
|
||||
self.text = siteagents.llms_txt([("retroarch", "RetroArch")])
|
||||
self.section = self.text.split("\n## Driving the installer for a user\n", 1)[1].split("\n## ", 1)[0]
|
||||
|
||||
def test_the_exit_codes_are_the_installer_page_table(self) -> None:
|
||||
page = (REPO_ROOT / "wiki" / "installer.md").read_text(encoding="utf-8")
|
||||
table = page.split("<!-- generated: exit-codes -->", 1)[1].split("<!-- /generated: exit-codes -->")[0]
|
||||
in_table = [int(code) for code in re.findall(r"^\| `(\d+)` \|", table, re.M)]
|
||||
self.assertEqual(in_table, self.doc.exit_codes())
|
||||
self.assertEqual([code for code, _ in siteagents.EXIT_CODES], in_table)
|
||||
listed = [int(code) for code in re.findall(r"^ - `(\d+)`: ", self.section, re.M)]
|
||||
self.assertEqual(listed, in_table)
|
||||
|
||||
def test_the_event_schema_is_the_installers(self) -> None:
|
||||
import json # noqa: PLC0415
|
||||
|
||||
self.assertEqual(siteagents.EVENT_SCHEMA, self.doc._install.EVENT_SCHEMA)
|
||||
schema = json.loads((REPO_ROOT / "schemas" / "install-events.schema.json").read_text(encoding="utf-8"))
|
||||
self.assertEqual(schema["$id"], siteagents.SITE + siteagents.EVENTS_SCHEMA_PATH)
|
||||
self.assertIn(f"event schema {siteagents.EVENT_SCHEMA} ", self.section)
|
||||
|
||||
def test_every_option_and_event_named_exists(self) -> None:
|
||||
options = {flag for action in self.doc._install.build_parser()._actions
|
||||
for flag in action.option_strings}
|
||||
for flag in set(re.findall(r"(?<![\w-])(--[a-z][a-z-]*)", self.section)):
|
||||
with self.subTest(flag=flag):
|
||||
self.assertIn(flag, options)
|
||||
keys = self.doc._install.EVENT_KEYS
|
||||
self.assertIn("state", keys["file"])
|
||||
self.assertIn("bytes_needed", keys["check"])
|
||||
self.assertIn("pending", keys["summary"])
|
||||
self.assertEqual(tuple(keys["end"]), ("code",))
|
||||
|
||||
def test_the_package_commands_and_the_pattern(self) -> None:
|
||||
self.assertIn("`uvx retrobios --for-games --required-only --dry-run`", self.section)
|
||||
self.assertIn("`pipx run retrobios`", self.section)
|
||||
self.assertIn("`--dry-run --events`", self.section)
|
||||
steps = re.findall(r"^\d\. (\w+)", self.section, re.M)
|
||||
self.assertEqual(steps, ["Plan", "Consent", "Act"])
|
||||
|
||||
|
||||
class TheSkillIsAnAgentSkill(unittest.TestCase):
|
||||
"""ARD reserves text/markdown for agent skills (Lighthouse warns on a
|
||||
plain Markdown entry): the setup steps are published as one."""
|
||||
|
||||
Reference in new issue
Block a user