From dc79363aef03d8849e389af171e16d7ce36f3de4 Mon Sep 17 00:00:00 2001 From: AdamZ Date: Sun, 6 Sep 2026 16:36:28 -0700 Subject: [PATCH] Prove PoB Codes feed with the existing build generator --- .github/workflows/pob-codes-feed-proof.yml | 32 +++++++ spec/FetchTestBuilds.py | 92 +++++++++++++++++++ spec/GenerateBuilds.lua | 18 +++- spec/POB_CODES_FEED_PROOF.md | 38 ++++++++ tests/test_pob_codes_feed.py | 101 +++++++++++++++++++++ 5 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pob-codes-feed-proof.yml create mode 100644 spec/FetchTestBuilds.py create mode 100644 spec/POB_CODES_FEED_PROOF.md create mode 100644 tests/test_pob_codes_feed.py diff --git a/.github/workflows/pob-codes-feed-proof.yml b/.github/workflows/pob-codes-feed-proof.yml new file mode 100644 index 00000000000..aa97ba3a168 --- /dev/null +++ b/.github/workflows/pob-codes-feed-proof.yml @@ -0,0 +1,32 @@ +name: PoB Codes feed integration proof +on: + pull_request: + branches: [tests-branch, dev] + paths: + - spec/FetchTestBuilds.py + - spec/GenerateBuilds.lua + - spec/DiffOutput.lua + - spec/TestBuilds/** + - tests/test_pob_codes_feed.py + - .github/workflows/pob-codes-feed-proof.yml + workflow_dispatch: +permissions: + contents: read +jobs: + existing_generator: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Prove adapter, existing generator, and existing comparator + env: + POB_FEED_DOCKER_TEST: '1' + run: python -m unittest discover -s tests -p test_pob_codes_feed.py -v + - name: Validate and decode the live monthly batch (manual only) + if: github.event_name == 'workflow_dispatch' + run: python spec/FetchTestBuilds.py --output "$RUNNER_TEMP/pob-codes-inputs" diff --git a/spec/FetchTestBuilds.py b/spec/FetchTestBuilds.py new file mode 100644 index 00000000000..cdbecaf6454 --- /dev/null +++ b/spec/FetchTestBuilds.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python3 +"""Decode one PoB Codes batch for the existing GenerateBuilds.lua input path.""" +import argparse +import base64 +import hashlib +import json +from pathlib import Path +import re +import tempfile +import urllib.request +import zlib +from xml.etree import ElementTree + +FEED = "https://api.pob.codes/test-builds" +MAX_BATCH, MAX_CODE, MAX_XML = 16 * 1024 * 1024, 150 * 1024, 4 * 1024 * 1024 + + +def decode_batch(data): + if len(data) > MAX_BATCH: + raise ValueError("batch too large") + batch = json.loads(data) + if batch.get("schemaVersion") != 1 or batch.get("requestedCount") != 100: + raise ValueError("unsupported feed schema") + builds = batch.get("builds") + if not isinstance(builds, list) or not 1 <= len(builds) <= 100 or batch.get("count") != len(builds): + raise ValueError("invalid build count") + decoded = {} + for entry in builds: + code, key = entry["code"], entry["sha256"] + if not isinstance(code, str) or not 0 < len(code) <= MAX_CODE or not re.fullmatch(r"[A-Za-z0-9_+/=-]+", code): + raise ValueError("invalid build code") + if hashlib.sha256(code.encode("ascii")).hexdigest() != key or key + ".xml" in decoded: + raise ValueError("hash mismatch or duplicate build") + packed = base64.b64decode(code + "=" * (-len(code) % 4), altchars=b"-_", validate=True) + xml = None + for window in (zlib.MAX_WBITS, -zlib.MAX_WBITS): + try: + stream = zlib.decompressobj(window) + xml = stream.decompress(packed, MAX_XML + 1) + except zlib.error: + continue + if len(xml) > MAX_XML or stream.unconsumed_tail or not stream.eof or stream.unused_data: + raise ValueError("oversized, incomplete, or trailing compressed data") + break + if xml is None: + raise ValueError("invalid compressed build") + text = xml.decode("utf-8") + if "\0" in text or re.search(r"'), + batch_for(b"x" * (MAX_XML + 1))): + with self.subTest(batch=batch.get("schemaVersion")), tempfile.TemporaryDirectory() as tmp: + output = Path(tmp) / "inputs" + with self.assertRaises((ValueError, ElementTree.ParseError)): + write_inputs(json.dumps(batch), output) + self.assertFalse(output.exists()) + + def test_existing_inputs_are_never_overwritten(self): + with tempfile.TemporaryDirectory() as tmp: + output = Path(tmp) / "inputs" + self.assertEqual(write_inputs(json.dumps(self.batch), output), 1) + before = {p.name: p.read_bytes() for p in output.iterdir()} + with self.assertRaises(ValueError): + write_inputs(json.dumps(self.batch), output) + self.assertEqual({p.name: p.read_bytes() for p in output.iterdir()}, before) + + @unittest.skipUnless(os.environ.get("POB_FEED_DOCKER_TEST") == "1", "opt-in existing Docker generator proof") + def test_existing_generator_and_comparator(self): + with tempfile.TemporaryDirectory() as tmp: + tmp = Path(tmp) + write_inputs(json.dumps(self.batch), tmp / "inputs") + for name in ("first", "second", "bad-output"): + (tmp / name).mkdir() + command = ["docker", "run", "--rm", "--network", "none", + "--mount", "type=bind,source=%s,target=/workdir,readonly" % ROOT, + "--mount", "type=bind,source=%s,target=/proof" % tmp, + "-w", "/workdir", "-e", "HOME=/tmp"] + + def docker(arguments, environment=()): + return subprocess.run(command + list(environment) + + ["ghcr.io/paliak/busted-tests:latest"] + arguments, timeout=330, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True) + + def generate(inputs, output): + return docker(["timeout", "300", "busted", "--lua=luajit", "-r", "generate"], + ["-e", "BUILDINPUTDIR=/proof/" + inputs, "-e", "BUILDCACHEPREFIX=/proof/" + output]) + + for output in ("first", "second"): + result = generate("inputs", output) + self.assertEqual(result.returncode, 0, result.stdout) + self.assertEqual(len(list((tmp / output).glob("*.build"))), 1) + name = next((tmp / "first").glob("*.build")).name + args = ["luajit", "spec/DiffOutput.lua", "/proof/first/" + name, "/proof/second/" + name] + self.assertEqual(docker(args).returncode, 0) + saved = (tmp / "second" / name).read_text() + changed, count = re.subn(r'(123456789', saved, count=1) + self.assertEqual(count, 1, "generator must save calculated stats") + (tmp / "second" / name).write_text(changed) + self.assertEqual(docker(args).returncode, 1, "existing comparator must detect changed stats") + bad = re.sub(br'targetVersion="[^"]+"', b'targetVersion="unsupported"', self.xml, count=1) + write_inputs(json.dumps(batch_for(bad)), tmp / "bad-input") + result = generate("bad-input", "bad-output") + self.assertNotEqual(result.returncode, 0, result.stdout) + self.assertFalse(list((tmp / "bad-output").glob("*.build"))) + + +if __name__ == "__main__": + unittest.main()