#!/usr/bin/env python3 """Top-level CI orchestrator (plan ยง4.3), invoked by the Drone pipeline (or by hand). Reads the run parameters from env (set by the comment-bridge via Drone build params): RECIPE recipe name (e.g. custom-html) [required] REF PR head commit sha [optional; recorded, used for fetch] PR PR number [optional, default 0] SRC head repo full_name on the mirror [optional] STAGES comma list: install,upgrade,backup [optional, default install] It fetches the recipe at REF, then runs the requested per-stage pytest files under tests//. Teardown is guaranteed by the conftest fixture finalizer. Run env (python with pytest+playwright, PLAYWRIGHT_BROWSERS_PATH) is provided by `cc-ci-run` (modules/harness.nix); invoke as: cc-ci-run runner/run_recipe_ci.py """ from __future__ import annotations import os import subprocess import sys ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) STAGE_FILES = { "install": "test_install.py", "upgrade": "test_upgrade.py", "backup": "test_backup.py", } def fetch_recipe(recipe: str, ref: str | None, src: str | None) -> None: """Make the recipe available at the code under test. If SRC+REF point at the mirror PR, clone it at that ref; otherwise fetch the catalogue copy.""" recipes_dir = os.path.expanduser("~/.abra/recipes") os.makedirs(recipes_dir, exist_ok=True) dest = os.path.join(recipes_dir, recipe) if src and ref: url = f"https://git.autonomic.zone/{src}.git" subprocess.run(["rm", "-rf", dest], check=False) subprocess.run(["git", "clone", "--quiet", url, dest], check=True) subprocess.run(["git", "-C", dest, "checkout", "--quiet", ref], check=True) else: subprocess.run(["abra", "recipe", "fetch", recipe, "-n"], check=True) def main() -> int: recipe = os.environ.get("RECIPE") if not recipe: print("RECIPE env is required", file=sys.stderr) return 2 ref = os.environ.get("REF") or None src = os.environ.get("SRC") or None stages = [s.strip() for s in os.environ.get("STAGES", "install").split(",") if s.strip()] print(f"== cc-ci run: recipe={recipe} ref={ref} pr={os.environ.get('PR', '0')} stages={stages}") fetch_recipe(recipe, ref, src) test_dir = os.path.join(ROOT, "tests", recipe) overall = 0 ran = 0 for stage in stages: fname = STAGE_FILES.get(stage) if not fname: print(f"unknown stage {stage}", file=sys.stderr) return 2 path = os.path.join(test_dir, fname) if not os.path.exists(path): print(f" (skip {stage}: {path} not present)") continue print(f"\n===== STAGE: {stage} =====", flush=True) # each stage is its own pytest invocation => its own reported result (D2 separate stages) rc = subprocess.call([sys.executable, "-m", "pytest", "-v", "-rA", path], cwd=ROOT) ran += 1 if rc != 0: overall = rc if ran == 0: print("no stage test files found", file=sys.stderr) return 1 return overall if __name__ == "__main__": raise SystemExit(main())