From 6e7047b3fcd441a12d02c416fdb4e0726a55f010 Mon Sep 17 00:00:00 2001 From: Abdessamad Derraz <3028866+Abdess@users.noreply.github.com> Date: Fri, 23 Oct 2026 18:48:02 +0200 Subject: [PATCH] feat: attach the installer to each release --- scripts/check_release.py | 23 ++++++++++++++++++++--- wiki/release-process.md | 11 +++++++++-- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/scripts/check_release.py b/scripts/check_release.py index 92dda869..e53e5b56 100644 --- a/scripts/check_release.py +++ b/scripts/check_release.py @@ -7,7 +7,9 @@ SHA256SUMS.txt lists for it, as must each handheld launcher (RetroBIOS.zip and the others build_port.py writes) the list names, the parts of a pack must add up to the download size the README prints, and nothing else may be attached. The record holds one pack for each platform the registry marks release_pack, and no other -pack. An upload that +pack. The installer (install.py, install.sh, install.ps1) and every file +list of install/ are listed and attached too, so a package can pin one +release. An upload that failed in the loop, or a part re-uploaded from an older build, otherwise publishes a release that `sha256sum --check` rejects for every user. @@ -37,6 +39,12 @@ from release_record import RECORD, load_record SUMS = "SHA256SUMS.txt" SIGNATURE = SUMS + ".sig" +INSTALLER = ("install.py", "install.sh", "install.ps1") + + +def installer_files(install_dir: str | Path = "install") -> list[str]: + """The release files a package pins: the installer and each file list.""" + return [*INSTALLER, *sorted(p.name for p in Path(install_dir).glob("*.json"))] @dataclass(frozen=True) @@ -73,6 +81,7 @@ def compare_release( tag: str, local_files: dict[str, str] | None = None, released: set[str] | None = None, + installer: list[str] | None = None, ) -> list[Finding]: """Every way the release can differ from what was built. @@ -80,6 +89,7 @@ def compare_release( signature) to its local sha256: the copy on the release must be that one. *released* names the platforms the registry marks release_pack: each one has a recorded pack, and no recorded pack serves only other platforms. + *installer* names the installer files each release lists and carries. """ findings: list[Finding] = [] if record.get("tag") != tag: @@ -112,9 +122,11 @@ def compare_release( findings.append(Finding("extra", name, "attached but neither recorded nor listed")) for name in sorted(set(recorded) - set(sums)): findings.append(Finding("unlisted", name, f"absent from {SUMS}")) + for name in sorted(set(installer or ()) - set(sums)): + findings.append(Finding("no-installer", name, f"absent from {SUMS}")) # The handheld launchers are release files but no packs: release.json # records packs, and the checksum list alone vouches for the launchers. - for name in sorted(set(sums) - set(recorded) - set(ARCHIVES)): + for name in sorted(set(sums) - set(recorded) - set(ARCHIVES) - set(installer or ())): findings.append(Finding("unrecorded", name, f"listed in {SUMS}, absent from release.json")) wanted = {**sums, **(local_files or {})} @@ -190,6 +202,10 @@ def main() -> int: "--platforms-dir", default="platforms", help="registry whose release_pack platforms each need a pack (default: %(default)s)", ) + parser.add_argument( + "--install-dir", default="install", + help="file lists the release must carry beside the installer (default: %(default)s)", + ) parser.add_argument("--json", action="store_true", help="JSON output") args = parser.parse_args() @@ -223,7 +239,8 @@ def main() -> int: return 2 released = set(list_release_pack_platforms(args.platforms_dir, include_archived=True)) - findings = compare_release(record, sums, assets, tag, local_files, released) + findings = compare_release(record, sums, assets, tag, local_files, released, + installer_files(args.install_dir)) if args.json: print(json.dumps( {"tag": tag, "draft": draft, "assets": len(assets), diff --git a/wiki/release-process.md b/wiki/release-process.md index 923da624..f3c73dd9 100644 --- a/wiki/release-process.md +++ b/wiki/release-process.md @@ -227,11 +227,17 @@ python scripts/split_pack.py dist/ # check_release.py compares each asset with its line there. python scripts/build_port.py +# 2c. The installer and the file lists step 1 wrote, so a package can pin +# one release (installer page, "For packagers"). check_release.py +# refuses a release that lacks one of them. +cp install.py install.sh install.ps1 install/*.json dist/ + # 3. Checksums of the files as published, then sign the list. The checksums # answer corruption, and the signature answers a rewritten release, which is # the one thing a checksum published beside its own artifacts cannot # answer. -(cd dist && sha256sum *.zip RetroBIOS.muxapp > SHA256SUMS.txt) +(cd dist && sha256sum *.zip RetroBIOS.muxapp install.py install.sh install.ps1 \ + $(cd ../install && ls *.json) > SHA256SUMS.txt) ssh-keygen -Y sign -f ~/.ssh/retrobios_signing -n file dist/SHA256SUMS.txt # 4. Record what each pack holds, read from the archives just checked: @@ -253,7 +259,8 @@ python scripts/release_notes.py --whats-new tmp/whats-new.md --output tmp/notes. # A public release with half its assets is a broken download for everyone # during the whole upload. gh release create "v${DATE}" --draft --title "BIOS Pack v${DATE}" --notes-file tmp/notes.md -for f in dist/SHA256SUMS.txt dist/SHA256SUMS.txt.sig dist/*.zip dist/RetroBIOS.muxapp; do +for f in dist/SHA256SUMS.txt dist/SHA256SUMS.txt.sig dist/*.zip dist/RetroBIOS.muxapp \ + dist/install.py dist/install.sh dist/install.ps1 $(ls install/*.json | sed 's|^install/|dist/|'); do [ -f "$f" ] && gh release upload "v${DATE}" "$f#$(basename "$f")" --clobber done # A count does not see a failed upload beside a stray file, nor a part