From bdcc8f0fb6aec72114b70766aed59ef585853c4e Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Wed, 21 Oct 2026 05:32:50 +0200 Subject: [PATCH] fix: lower the pipeline floor to Python 3.9 The 3.12 guard rested on generate_readme f-strings that have since been rewritten. Every script compiles under CPython 3.9.25, 3.10 and 3.11, and only generate_pack's parenthesized context managers fail under 3.8. Under 3.9 the offline pipeline (--skip-packs) completed with no change to the committed artifacts, the 15 manifests matched 3.12 byte for byte, and the RomM pack was identical. CI now compiles every script under a real 3.9. --- .github/workflows/validate.yml | 6 ++++-- README.md | 4 ++-- pyproject.toml | 5 ++--- scripts/generate_readme.py | 2 +- scripts/pipeline.py | 12 ++++++----- tests/test_audit_regressions.py | 36 ++++++++++++++++++++++++++++++++- wiki/installer.md | 2 +- wiki/testing-guide.md | 2 +- wiki/tools.md | 2 +- 9 files changed, 54 insertions(+), 17 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 94b4c64f..50a8aaf8 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -165,12 +165,14 @@ jobs: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 # install.py stays on Python 3.8: the suite runs it end to end under a - # real 3.8 interpreter, which import-only and grammar checks miss. The - # last version listed is the default python. + # real 3.8 interpreter, which import-only and grammar checks miss. 3.9 + # is the floor pipeline.py states for the build scripts, compiled under + # a real 3.9. The last version listed is the default python. - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 with: python-version: | 3.8 + 3.9 3.12 # markdown: the site validation test renders a wiki line to prove the diff --git a/README.md b/README.md index 37696e62..a0163372 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,7 @@ python scripts/verify.py --emulator flycast python scripts/verify.py --platform retroarch --verbose # emulator ground truth ``` -Requires Python 3.12 and `pyyaml`. The one-line installer alone runs on Python 3.8. +Requires Python 3.9 or newer and `pyyaml`. The one-line installer alone runs on Python 3.8. ## Documentation site @@ -180,4 +180,4 @@ The scripts and tooling are released under the [MIT License](LICENSE). The BIOS and firmware files are not covered by that license: they are third-party system software, preserved and provided for personal backup, archival, and interoperability with emulation software. [NOTICE](NOTICE) sets out their status and how to ask for a file to be removed. The reasoning, and where it is weakest, is in the [FAQ](https://abdess.github.io/retrobios/wiki/faq/#is-this-legal). -*Auto-generated on 2026-10-20T20:53:07Z* +*Auto-generated on 2026-10-21T03:32:18Z* diff --git a/pyproject.toml b/pyproject.toml index 6e588335..ac501527 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,9 +4,8 @@ version = "1.0.0" description = "BIOS collection manager for retrogaming platforms" # install.py is the only entry point a user runs before cloning, and both # bootstraps accept 3.8, so the floor stays where the bootstraps put it. That -# floor covers install.py alone: the build scripts under scripts/ use syntax -# CPython 3.12 introduced, which is the version CI pins and the version a -# contributor needs to run the pipeline or the test suite. +# floor covers install.py alone: the build scripts under scripts/ need 3.9 +# (pipeline.py checks it), and CI runs the test suite on 3.12. requires-python = ">=3.8" dependencies = [ "pyyaml", diff --git a/scripts/generate_readme.py b/scripts/generate_readme.py index 52256e85..d9be6cdb 100644 --- a/scripts/generate_readme.py +++ b/scripts/generate_readme.py @@ -582,7 +582,7 @@ def generate_readme(db: dict, platforms_dir: str) -> str: "python scripts/verify.py --platform retroarch --verbose # emulator ground truth", "```", "", - "Requires Python 3.12 and `pyyaml`. The one-line installer alone runs on Python 3.8.", + "Requires Python 3.9 or newer and `pyyaml`. The one-line installer alone runs on Python 3.8.", "", "## Documentation site", "", diff --git a/scripts/pipeline.py b/scripts/pipeline.py index aa815ed4..8245dd24 100644 --- a/scripts/pipeline.py +++ b/scripts/pipeline.py @@ -51,11 +51,13 @@ import sys import time from pathlib import Path -# The build scripts use syntax CPython 3.12 introduced (generate_readme's -# f-strings, generate_pack's parenthesized context managers). On an older -# interpreter the run got through the packs and failed hours in, at step 7. -if sys.version_info < (3, 12): - sys.exit(f"pipeline.py needs Python 3.12 or newer, this is {sys.version.split()[0]}") +# generate_pack's parenthesized context managers do not compile before +# CPython 3.9, which builds the same database, manifests and packs as 3.12. +# On an older interpreter the run got through the first steps and failed in +# the packs, so it stops before step 0. A test compiles every script under +# this floor. +if sys.version_info < (3, 9): + sys.exit(f"pipeline.py needs Python 3.9 or newer, this is {sys.version.split()[0]}") sys.path.insert(0, str(Path(__file__).parent)) from common import ( diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index 4d4ae2eb..0b7a948e 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -108,7 +108,41 @@ class ReadmeRegressions(unittest.TestCase): cwd=ROOT, capture_output=True, text=True, timeout=60, check=False, ) self.assertEqual(proc.returncode, 1) - self.assertIn("needs Python 3.12", proc.stderr) + self.assertIn("needs Python 3.9", proc.stderr) + + def test_the_floor_interpreter_compiles_every_script(self): + """The floor said 3.12 for a reason the code no longer carried: every + script compiled under 3.9, and only the guard refused it. Checked + with a real interpreter of the floor version, as the installer's 3.8 + floor is.""" + import shutil # noqa: PLC0415 + + floor = re.search( + r"sys\.version_info < \((\d+), (\d+)\)", + (ROOT / "scripts" / "pipeline.py").read_text(encoding="utf-8"), + ) + name = f"python{floor.group(1)}.{floor.group(2)}" + interpreter = shutil.which(name) + if interpreter is None: + if os.environ.get("CI"): + self.fail(f"CI must provide {name}") + self.skipTest(f"no {name} on PATH") + check = ( + "import pathlib, sys\n" + "bad = []\n" + "for p in sorted(pathlib.Path('scripts').rglob('*.py')):\n" + " try:\n" + " compile(p.read_text(encoding='utf-8'), str(p), 'exec')\n" + " except SyntaxError as e:\n" + " bad.append(f'{p}:{e.lineno}')\n" + "print(bad)\n" + "sys.exit(1 if bad else 0)\n" + ) + proc = subprocess.run( + [interpreter, "-I", "-c", check], cwd=ROOT, capture_output=True, + text=True, timeout=300, check=False, + ) + self.assertEqual(proc.returncode, 0, proc.stdout + proc.stderr) class ReleaseSigningRegressions(unittest.TestCase): diff --git a/wiki/installer.md b/wiki/installer.md index d646ce5c..574faff4 100644 --- a/wiki/installer.md +++ b/wiki/installer.md @@ -471,7 +471,7 @@ followed. RetroPie image, so it stays on Python 3.8 and the standard library alone. 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. +The build scripts need 3.9 and are not bound by this. Every change to `install.py` recomputes the SHA-256 pinned in `install.sh` and `install.ps1` in the same commit. The tests compare the pins with the file. diff --git a/wiki/testing-guide.md b/wiki/testing-guide.md index bf4c3c1d..ca71dd6c 100644 --- a/wiki/testing-guide.md +++ b/wiki/testing-guide.md @@ -24,7 +24,7 @@ python -m unittest tests.test_install tests.test_install_cores -v The table below says which module covers what. -Running the build needs Python 3.12 and `pyyaml` alone, and so does most of the suite. The +Running the build needs Python 3.9 or newer and `pyyaml` alone. CI runs the suite on 3.12. The schema contract tests also need `jsonschema[format-nongpl]`, and one site test renders a wiki line with `markdown`. The CI installs both: without them those tests are reported as skipped, so install them before a pull request diff --git a/wiki/tools.md b/wiki/tools.md index 06731d05..0027be20 100644 --- a/wiki/tools.md +++ b/wiki/tools.md @@ -1,6 +1,6 @@ # Tools -All tools are Python scripts in `scripts/`. They need Python 3.12 and `pyyaml`, +All tools are Python scripts in `scripts/`. They need Python 3.9 or newer and `pyyaml`, and only `validate_schemas.py` also needs `jsonschema[format-nongpl]`. `install.py` alone runs on Python 3.8.