mirror of
https://github.com/Abdess/retroarch_system.git
synced 2026-10-18 17:33:30 -05:00
A count of assets misses a failed upload beside a stray file and a part re-uploaded from an older build. check_release.py holds each asset's GitHub sha256 against SHA256SUMS.txt and release.json, and the release process publishes the draft only on its clean exit.
133 lines
4.5 KiB
Python
133 lines
4.5 KiB
Python
"""A dated release is checked against what was built before it is published.
|
|
|
|
The upload loop has no `set -e`: a part that failed to upload, or a part
|
|
re-uploaded from an older build, left a release whose count looked right and
|
|
whose checksums every user's `sha256sum --check` rejected.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "scripts"))
|
|
|
|
from check_release import SIGNATURE, SUMS, Asset, compare_release, parse_sums
|
|
|
|
A = "a" * 64
|
|
B = "b" * 64
|
|
C = "c" * 64
|
|
S = "d" * 64
|
|
|
|
RECORD = {
|
|
"tag": "v2026.10.10",
|
|
"packs": {
|
|
"Big_BIOS_Pack.zip": {
|
|
"download_size": 300,
|
|
"assets": ["Big_BIOS_Pack.part1of2.zip", "Big_BIOS_Pack.part2of2.zip"],
|
|
},
|
|
"Small_BIOS_Pack.zip": {"download_size": 50, "assets": ["Small_BIOS_Pack.zip"]},
|
|
},
|
|
}
|
|
SUMS_LIST = {
|
|
"Big_BIOS_Pack.part1of2.zip": A,
|
|
"Big_BIOS_Pack.part2of2.zip": B,
|
|
"Small_BIOS_Pack.zip": C,
|
|
}
|
|
|
|
|
|
def _assets() -> dict[str, Asset]:
|
|
return {
|
|
"Big_BIOS_Pack.part1of2.zip": Asset(200, A),
|
|
"Big_BIOS_Pack.part2of2.zip": Asset(100, B),
|
|
"Small_BIOS_Pack.zip": Asset(50, C),
|
|
SUMS: Asset(300, S),
|
|
SIGNATURE: Asset(10, "e" * 64),
|
|
}
|
|
|
|
|
|
def _kinds(findings) -> set[tuple[str, str]]:
|
|
return {(f.kind, f.name) for f in findings}
|
|
|
|
|
|
class ReleaseMatchesWhatWasBuilt(unittest.TestCase):
|
|
def test_a_complete_release_passes(self):
|
|
self.assertEqual(
|
|
compare_release(RECORD, SUMS_LIST, _assets(), "v2026.10.10", {SUMS: S}), []
|
|
)
|
|
|
|
def test_a_part_that_failed_to_upload_is_missing(self):
|
|
assets = _assets()
|
|
del assets["Big_BIOS_Pack.part2of2.zip"]
|
|
self.assertIn(
|
|
("missing", "Big_BIOS_Pack.part2of2.zip"),
|
|
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
|
|
)
|
|
|
|
def test_a_part_from_an_older_build_is_caught_at_the_same_count(self):
|
|
assets = _assets()
|
|
assets["Big_BIOS_Pack.part1of2.zip"] = Asset(200, "f" * 64)
|
|
self.assertEqual(
|
|
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
|
|
{("content", "Big_BIOS_Pack.part1of2.zip")},
|
|
)
|
|
|
|
def test_a_checksum_list_other_than_the_local_one_is_caught(self):
|
|
self.assertEqual(
|
|
_kinds(compare_release(RECORD, SUMS_LIST, _assets(), "v2026.10.10", {SUMS: "0" * 64})),
|
|
{("content", SUMS)},
|
|
)
|
|
|
|
def test_an_asset_nobody_recorded_is_extra(self):
|
|
assets = _assets()
|
|
assets["Big_BIOS_Pack.zip.001"] = Asset(1, "1" * 64)
|
|
self.assertEqual(
|
|
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
|
|
{("extra", "Big_BIOS_Pack.zip.001")},
|
|
)
|
|
|
|
def test_record_and_list_must_name_the_same_files(self):
|
|
sums = {k: v for k, v in SUMS_LIST.items() if k != "Small_BIOS_Pack.zip"}
|
|
sums["Other_BIOS_Pack.zip"] = C
|
|
kinds = _kinds(compare_release(RECORD, sums, _assets(), "v2026.10.10"))
|
|
self.assertIn(("unlisted", "Small_BIOS_Pack.zip"), kinds)
|
|
self.assertIn(("unrecorded", "Other_BIOS_Pack.zip"), kinds)
|
|
self.assertIn(("missing", "Other_BIOS_Pack.zip"), kinds)
|
|
|
|
def test_parts_must_add_up_to_the_printed_download_size(self):
|
|
assets = _assets()
|
|
assets["Big_BIOS_Pack.part2of2.zip"] = Asset(99, B)
|
|
self.assertEqual(
|
|
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
|
|
{("size", "Big_BIOS_Pack.zip")},
|
|
)
|
|
|
|
def test_an_asset_without_a_digest_is_not_taken_on_trust(self):
|
|
assets = _assets()
|
|
assets["Small_BIOS_Pack.zip"] = Asset(50, None)
|
|
self.assertEqual(
|
|
_kinds(compare_release(RECORD, SUMS_LIST, assets, "v2026.10.10")),
|
|
{("unverified", "Small_BIOS_Pack.zip")},
|
|
)
|
|
|
|
def test_another_tag_than_the_record_is_named(self):
|
|
self.assertEqual(
|
|
_kinds(compare_release(RECORD, SUMS_LIST, _assets(), "v2026.10.11")),
|
|
{("tag", "v2026.10.11")},
|
|
)
|
|
|
|
|
|
class SumsParsing(unittest.TestCase):
|
|
def test_text_and_binary_mode_lines(self):
|
|
text = f"{A} Small_BIOS_Pack.zip\n{B} *Big BIOS.zip\n\n"
|
|
self.assertEqual(parse_sums(text), {"Small_BIOS_Pack.zip": A, "Big BIOS.zip": B})
|
|
|
|
def test_a_line_that_is_not_a_checksum_stops(self):
|
|
with self.assertRaises(ValueError):
|
|
parse_sums("<html>Not Found</html>\n")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|