"""The package managers' entries for a release of `wai`, as wai/quickstart/formulae.py writes them, held to the archive build.sh packs: python3 -I wai/quickstart/test/formulae_test.py It runs formulae.py against a stand-in for the forge's release API on 127.0.0.1 (a release with every platform's archive and its .sha256 attached), and checks what it writes: - the Homebrew formula installs every binary build.sh packs but wai-quantum, and depends on wai-quantum's own formula, which wai/wai-quantum-cli/release/formulae.py writes into the same tap: installed by both, the one command would belong to two formulae, and the second's link would fail; - the Scoop manifest names the same binaries, each in the zip the Windows job packs, and depends on wai-quantum's own manifest, so the two never share a shim; - the archive's wai-quantum is built with the features wai-quantum's own release builds it with on each platform (metal on macOS), so the program install.sh installs is the one the package managers do; - with ruby on PATH, the formula parses as Ruby. Exit 0 when each holds, 1 naming each that does not. Standard library only. """ import hashlib import http.server import json import pathlib import re import shutil import subprocess import sys import tempfile import threading ROOT = pathlib.Path(__file__).resolve().parents[3] QUICKSTART = ROOT / "wai/quickstart" failures = [] def check(ok, what): print(("ok " if ok else "FAIL ") + what) if not ok: failures.append(what) build_sh = (QUICKSTART / "build.sh").read_text() # `need "" `: what the archive's bin/ holds. packed = re.findall(r'^need "[^"]+" (\S+)', build_sh, re.M) check("wai" in packed and "wai-quantum" in packed and len(packed) == len(set(packed)), f"build.sh packs wai and wai-quantum, each once ({packed})") installed = [b for b in packed if b != "wai-quantum"] # The forge, as formulae.py reads it: the release by its tag, and each archive's .sha256. VERSION, TAG, REPO = "9.9.9", "wai-v9.9.9", "Transaction-Science/open-standards" TRIPLES = ["aarch64-apple-darwin", "x86_64-apple-darwin", "aarch64-unknown-linux-musl", "x86_64-unknown-linux-musl"] archives = [f"wai-{VERSION}-{t}.tar.gz" for t in TRIPLES] + [f"wai-{VERSION}-x86_64-pc-windows-msvc.zip"] files = {} for a in archives: files[a] = a.encode() files[a + ".sha256"] = f"{hashlib.sha256(a.encode()).hexdigest()} {a}\n".encode() class Forge(http.server.BaseHTTPRequestHandler): def do_GET(self): if self.path == f"/api/v1/repos/{REPO}/releases/tags/{TAG}": body = json.dumps({"tag_name": TAG, "assets": [{"name": n} for n in files]}).encode() elif self.path.startswith(f"/{REPO}/releases/download/{TAG}/") and self.path.rsplit("/", 1)[1] in files: body = files[self.path.rsplit("/", 1)[1]] else: self.send_error(404) return self.send_response(200) self.send_header("Content-Length", str(len(body))) self.end_headers() self.wfile.write(body) def log_message(self, *args): pass server = http.server.ThreadingHTTPServer(("127.0.0.1", 0), Forge) threading.Thread(target=server.serve_forever, daemon=True).start() api = f"http://127.0.0.1:{server.server_address[1]}/api/v1" with tempfile.TemporaryDirectory() as out: run = subprocess.run( [sys.executable, "-I", str(QUICKSTART / "formulae.py"), VERSION, TAG, api, REPO, "https://forge.example/" + REPO, out], capture_output=True, text=True, timeout=120, ) server.shutdown() check(run.returncode == 0, f"formulae.py writes the entries ({run.stderr.strip() or run.stdout.strip()})") rb = pathlib.Path(out, "wai.rb").read_text() if run.returncode == 0 else "" manifest = json.loads(pathlib.Path(out, "wai.json").read_text()) if run.returncode == 0 else {} # The formula. lines = [l.strip() for l in rb.splitlines() if l.strip().startswith("bin.install")] check(len(lines) == 1, f"the formula installs bin/ in one line ({lines})") names = re.findall(r'"bin/([^"]+)"', lines[0]) if lines else [] check(names == installed, f"the formula installs each binary build.sh packs but wai-quantum: {names}, not {installed}") check('Dir["bin/*"]' not in rb and '"bin/wai-quantum"' not in rb, "the formula does not install bin/wai-quantum, by name or by a glob") check('depends_on "transaction-science/tap/wai-quantum"' in rb, "the formula depends on wai-quantum's own formula") for t in TRIPLES: check(f"wai-{VERSION}-{t}.tar.gz" in rb and hashlib.sha256(f"wai-{VERSION}-{t}.tar.gz".encode()).hexdigest() in rb, f"the formula names the {t} archive and its digest") if shutil.which("ruby"): with tempfile.NamedTemporaryFile("w", suffix=".rb") as f: f.write(rb) f.flush() r = subprocess.run(["ruby", "-c", f.name], capture_output=True, text=True) check(r.returncode == 0, f"the formula parses as Ruby ({r.stdout.strip() or r.stderr.strip()})") else: print("note no ruby on PATH; the formula's syntax is not checked") # The manifest. check(manifest.get("bin") == [f"bin\\{b}.exe" for b in installed], f"the manifest names each binary but wai-quantum: {manifest.get('bin')}") check(manifest.get("depends") == "wai-quantum", f"the manifest depends on wai-quantum's own manifest ({manifest.get('depends')!r})") # The Windows job packs the binaries build.sh does, so each the manifest names is in the zip. release = (ROOT / ".github/workflows/wai-release.yml").read_text() windows = re.findall(r"bin = '([^']+)'", release) check(sorted(windows) == sorted(packed), f"the Windows job packs build.sh's binaries ({windows})") # wai-quantum's own entries are where the dependency names them: in this tap and this bucket, # as wai-quantum. own = (ROOT / "wai/wai-quantum-cli/release/formulae.py").read_text() check("class WaiQuantum < Formula" in own and 'pathlib.Path(out, "wai-quantum.rb")' in own and 'pathlib.Path(out, "wai-quantum.json")' in own, "wai-quantum's release writes the formula wai-quantum and the manifest wai-quantum") # One program by either path: the archive's wai-quantum has the features of wai-quantum's own # release, on macOS (its macOS job) and elsewhere (none). quantum = (ROOT / ".github/workflows/wai-quantum-cli-release.yml").read_text() mac_job = quantum.split("\n macos:", 1)[1].split("\n linux:", 1)[0] mac_features = re.findall(r"cargo build [^\n]*--features (\S+)", mac_job) other = quantum.split("\n linux:", 1)[1] check(re.findall(r"cargo build [^\n]*--features (\S+)", other) == [], "wai-quantum's own Linux and Windows builds take no feature") m = re.search(r"\*apple-darwin\) QUANTUM=\(--features (\S+)\)", build_sh) check(m is not None and mac_features == [m.group(1)], f"the archive's macOS wai-quantum has the features of its own release's: {m and m.group(1)}, {mac_features}") check(re.search(r'^need "\$ROOT/wai/wai-quantum-cli" wai-quantum --bin wai-quantum \$\{QUANTUM\[@\]', build_sh, re.M) is not None, "build.sh builds wai-quantum with those features") if failures: print(f"formulae_test: {len(failures)} check(s) failed") sys.exit(1) print("formulae_test: every check held")