mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-11-06 10:03:49 -06:00
feat: pin_installer writes the installer pins
Every change to install.py meant a sha256sum pasted by hand into install.sh and install.ps1, and the tests that caught a stale pin failed on two bare hashes. scripts/pin_installer.py writes both pins (--check only reports), the pin tests name it, check_freshness reads the pins through it and refresh_stale runs it for a stale pin row.
This commit is contained in:
1 parent
01ce92232d
commit
ada7738fa7
7 files changed
+133
-14
No files matched your search
@@ -32,7 +32,6 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import hashlib
|
||||
import io
|
||||
import json
|
||||
import re
|
||||
@@ -51,6 +50,7 @@ sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
||||
|
||||
import export_native # noqa: E402
|
||||
import pin_installer # noqa: E402
|
||||
import refresh_data_dirs # noqa: E402
|
||||
import upstream # noqa: E402
|
||||
from common import ( # noqa: E402
|
||||
@@ -83,7 +83,6 @@ PYPI_URL = "https://pypi.org/pypi/{name}/json"
|
||||
NPM_URL = "https://registry.npmjs.org/{name}/latest"
|
||||
# The site's npm dependencies, pinned to exact versions and built by Deploy Site.
|
||||
SITE_PACKAGE = REPO_ROOT / "website" / "package.json"
|
||||
INSTALLERS = ("install.sh", "install.ps1")
|
||||
|
||||
SHOWN_ITEMS = 4 # items named in a diff summary before ", ..."
|
||||
SHOWN_NAMES = 12 # core names named in an unresolved row before ", ..."
|
||||
@@ -1465,18 +1464,16 @@ def check_ci(cache_dir: str, offline: bool) -> list[Finding]:
|
||||
|
||||
def _check_installer_pins() -> Finding:
|
||||
"""The bootstraps pin install.py by SHA-256, so a stale pin refuses every install."""
|
||||
digest = hashlib.sha256((REPO_ROOT / "install.py").read_bytes()).hexdigest()
|
||||
stale = []
|
||||
for name in INSTALLERS:
|
||||
text = (REPO_ROOT / name).read_text(encoding="utf-8", errors="replace")
|
||||
if digest not in text:
|
||||
stale.append(name)
|
||||
stale = pin_installer.stale_pins(REPO_ROOT)
|
||||
return Finding(
|
||||
"ci",
|
||||
"install.py pin",
|
||||
STALE if stale else OK,
|
||||
local=digest[:12],
|
||||
detail=("pin outdated in " + ", ".join(stale)) if stale else "install.sh and install.ps1 match",
|
||||
local=pin_installer.installer_sha256(REPO_ROOT)[:12],
|
||||
detail=(
|
||||
f"pin outdated in {', '.join(stale)}, {pin_installer.COMMAND} writes it"
|
||||
if stale else "install.sh and install.ps1 match"
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Write the SHA-256 of install.py into the bootstraps that pin it.
|
||||
|
||||
install.sh and install.ps1 refuse an installer whose hash is not the one
|
||||
they carry, so every change to install.py moves both pins in the same
|
||||
commit. --check writes nothing and exits 1 naming each stale pin.
|
||||
|
||||
Usage:
|
||||
python scripts/pin_installer.py
|
||||
python scripts/pin_installer.py --check
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
INSTALLER = "install.py"
|
||||
# Each bootstrap and its one pin line, the value between the two groups.
|
||||
PINS: dict[str, re.Pattern[str]] = {
|
||||
"install.sh": re.compile(r'^(DEFAULT_INSTALL_SHA256=")[0-9a-f]{64}(")', re.M),
|
||||
"install.ps1": re.compile(r'^(\s*\$defaultInstallSha256 = ")[0-9a-f]{64}(")', re.M),
|
||||
}
|
||||
COMMAND = "python scripts/pin_installer.py"
|
||||
|
||||
|
||||
def installer_sha256(root: Path = ROOT) -> str:
|
||||
return hashlib.sha256((root / INSTALLER).read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def stale_pins(root: Path = ROOT) -> list[str]:
|
||||
"""The bootstraps whose pin is not the SHA-256 of install.py."""
|
||||
digest = installer_sha256(root)
|
||||
stale = []
|
||||
for name, pin in PINS.items():
|
||||
found = pin.search((root / name).read_bytes().decode("utf-8"))
|
||||
if found is None:
|
||||
raise ValueError(f"{name}: no installer pin line")
|
||||
if found.group(0) != f"{found.group(1)}{digest}{found.group(2)}":
|
||||
stale.append(name)
|
||||
return stale
|
||||
|
||||
|
||||
def write_pins(root: Path = ROOT) -> list[str]:
|
||||
"""Rewrite each stale pin. Returns the bootstraps written."""
|
||||
digest = installer_sha256(root)
|
||||
written = []
|
||||
for name in stale_pins(root):
|
||||
path = root / name
|
||||
text = path.read_bytes().decode("utf-8")
|
||||
new, count = PINS[name].subn(rf"\g<1>{digest}\g<2>", text)
|
||||
if count != 1:
|
||||
raise ValueError(f"{name}: {count} installer pin lines, expected 1")
|
||||
path.write_bytes(new.encode("utf-8"))
|
||||
written.append(name)
|
||||
return written
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(allow_abbrev=False, description=__doc__.split("\n\n")[0])
|
||||
parser.add_argument("--check", action="store_true",
|
||||
help="write nothing, exit 1 if a pin is stale")
|
||||
args = parser.parse_args()
|
||||
if args.check:
|
||||
stale = stale_pins()
|
||||
for name in stale:
|
||||
print(f"{name}: pin is not the SHA-256 of {INSTALLER}, {COMMAND} writes it",
|
||||
file=sys.stderr)
|
||||
return 1 if stale else 0
|
||||
for name in write_pins():
|
||||
print(f"{name}: pinned {installer_sha256()}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from common import run_cli
|
||||
|
||||
run_cli(main)
|
||||
@@ -34,7 +34,8 @@ has ended, and its export is rewritten even when no row named it.
|
||||
|
||||
Surfaced only (no refresh): data rows that are not a _data_dirs.yml key
|
||||
(the registry itself, buildbot assets it does not track), coreinfo gaps, redump/no-intro/tosec packs,
|
||||
CI pins (install.py SHA-256, PyPI, pinned actions), profile_sync, rom-folder
|
||||
CI pins (PyPI, pinned actions; the install.py SHA-256 pin is written by
|
||||
scripts/pin_installer.py), profile_sync, rom-folder
|
||||
pins (moved by hand, then python -m scripts.scraper.romfolders --setup NAME
|
||||
reads them again). Each needs
|
||||
a human read (new .info file, manual DAT download, PyPI version bump).
|
||||
@@ -401,6 +402,10 @@ def _command_for(
|
||||
"--force",
|
||||
]
|
||||
|
||||
if area == "ci" and subject == "install.py pin":
|
||||
# The pin is the hash of the committed install.py, nothing to judge.
|
||||
return [sys.executable, "scripts/pin_installer.py"]
|
||||
|
||||
if area == "catalogs":
|
||||
if subject == "mame recipes":
|
||||
return [
|
||||
|
||||
Reference in new issue
Block a user