Files
cc-ci/runner/run_recipe_ci.py
autonomic-bot 7eb0dd3c77
All checks were successful
continuous-integration/drone/push Build is passing
M5: upgrade + backup/restore stages green (custom-html); backup-bot-two oneshot
3-stage run green (install/upgrade/backup), clean teardown. backupbot deployed
via reconcile oneshot; PTY (script) for abra backup/restore; -m for secret generate
(no value leak). M5 CLAIMED.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 00:53:16 +01:00

84 lines
3.2 KiB
Python

#!/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/<recipe>/. 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())