#!/usr/bin/env python3 """builds native recipe fixtures and inspects the produced .holy artifacts.""" import hashlib import lzma import os from pathlib import Path import shutil import subprocess import sys import tarfile import tempfile binary = str(Path(sys.argv[1]).resolve()) def run(*args, status=0): result = subprocess.run([binary, *map(str, args)], text=True, capture_output=True, errors="replace") assert result.returncode == status, (args, result.returncode, result.stdout, result.stderr) return result.stdout def read_metadata(artifact): contents = subprocess.run(["lz4", "-dc", str(artifact)], check=True, capture_output=True).stdout with tarfile.open(fileobj=__import__("io").BytesIO(contents)) as archive: def read(name): return archive.extractfile(name).read().decode() names = sorted(member.name for member in archive.getmembers()) return {name: read(name) for name in ("HOLY/meta", "HOLY/files", "HOLY/deps", "HOLY/provides", "HOLY/hooks", "HOLY/origin", "HOLY/transform")}, names def write(path, text): path.parent.mkdir(parents=True, exist_ok=True) path.write_text(text) def main(): for tool in ("lz4",): if not shutil.which(tool): print(f"{tool} required for recipe fixture", file=sys.stderr) return 6 with tempfile.TemporaryDirectory() as scratch: root = Path(scratch) out = root / "out" (out / "data").mkdir(parents=True) (out / "data" / "greeting").write_text("hello\n") # noarch data-only recipe, no steps at all write(root / "data.recipe", """format holy-recipe-1 name holy-recipe-data version 1.0 release 1 arch noarch libc nolibc summary Recipe data fixture output holy-recipe-data runtime config etc/holy-recipe-data.conf step package /bin/sh < "$HOLY_DEST/usr/share/holy-recipe-data/greeting" printf 'config\\n' > "$HOLY_DEST/etc/holy-recipe-data.conf" ln -s greeting "$HOLY_DEST/usr/share/holy-recipe-data/current" PACKAGE """) log = run("build", root / "data.recipe", "--output", out / "data-out", "--yes") assert "built holy-recipe-data--noarch--nolibc" in log artifact = out / "data-out" / "holy-recipe-data--noarch--nolibc.holy" metadata, names = read_metadata(artifact) assert 'name "holy-recipe-data"' in metadata["HOLY/meta"] assert 'arch "noarch"' in metadata["HOLY/meta"] and 'libc "nolibc"' in metadata["HOLY/meta"] assert "x-version-family holy" in metadata["HOLY/meta"] assert "installed-size 16" in metadata["HOLY/meta"] assert "format holy-recipe-origin-1" in metadata["HOLY/origin"] assert "greeting" in metadata["HOLY/files"] assert "config" in metadata["HOLY/files"] assert "build-environment recorded" in metadata["HOLY/transform"] assert "HOLY/foreign" not in " ".join(names) # a compiled payload: the scanner derives arch, libc and soname requirements write(root / "lib.recipe", """format holy-recipe-1 name holy-recipe-lib version 2.1 release 3 arch x86_64 libc glibc summary Recipe library fixture build-depend cmd:make depend holy-recipe-data output holy-recipe-lib runtime output holy-recipe-lib-doc docs split holy-recipe-lib-doc usr/share/* step build /bin/sh < lib.c gcc -shared -fPIC -Wl,-soname,libholyrecipe.so.1 -o libholyrecipe.so.1 lib.c BUILD step package /bin/sh < "$HOLY_DEST/usr/share/holy-recipe-lib/README" PACKAGE """) log = run("build", root / "lib.recipe", "--output", out / "lib-out", "--yes") assert "built holy-recipe-lib--x86_64--glibc" in log assert "built holy-recipe-lib-doc--noarch--nolibc" in log library = out / "lib-out" / "holy-recipe-lib--x86_64--glibc.holy" docs = out / "lib-out" / "holy-recipe-lib-doc--noarch--nolibc.holy" library_meta, _ = read_metadata(library) docs_meta, _ = read_metadata(docs) assert 'arch "x86_64"' in library_meta["HOLY/meta"] assert 'libc "glibc"' in library_meta["HOLY/meta"] assert 'provide soname "libholyrecipe.so.1"' in library_meta["HOLY/provides"] assert "libc.so.6" in library_meta["HOLY/deps"] assert '"package" "holy-recipe-data"' in library_meta["HOLY/deps"] assert "libholyrecipe.so.1" in library_meta["HOLY/files"] assert 'symlink "usr/lib/libholyrecipe.so"' in library_meta["HOLY/files"] assert '"libholyrecipe.so.1"' in library_meta["HOLY/files"] assert "usr/lib" in library_meta["HOLY/files"] assert "README" in docs_meta["HOLY/files"] assert "libholyrecipe" not in docs_meta["HOLY/files"] # a runtime hook needs its script in the payload and a recorded digest write(root / "hook.recipe", """format holy-recipe-1 name holy-recipe-hook version 1.0 release 1 arch noarch libc nolibc summary Recipe hook fixture output holy-recipe-hook runtime hook-install /bin/sh usr/share/holy-recipe-hook/hook.sh step package /bin/sh < "$HOLY_DEST/usr/share/holy-recipe-hook/hook.sh" chmod 0755 "$HOLY_DEST/usr/share/holy-recipe-hook/hook.sh" printf 'data\\n' > "$HOLY_DEST/usr/share/holy-recipe-hook/data" PACKAGE """) run("build", root / "hook.recipe", "--output", out / "hook-out", "--yes") hook_meta, _ = read_metadata(out / "hook-out" / "holy-recipe-hook--noarch--nolibc.holy") assert 'hook postinstall "/bin/sh" "usr/share/holy-recipe-hook/hook.sh" sha256' in \ hook_meta["HOLY/hooks"] # review gates: unreviewed steps need approval, failures are visible run("build", root / "data.recipe", "--output", out / "review", "--noninteractive", status=3) write(root / "fail.recipe", """format holy-recipe-1 name holy-recipe-fail version 1.0 release 1 arch noarch libc nolibc output holy-recipe-fail runtime step package /bin/sh <