From c08247c9ce768141e187ccbf6fdf1a4f1bc0ed35 Mon Sep 17 00:00:00 2001 From: Kartavya Dikshit Date: Sun, 21 Jun 2026 03:12:00 +0200 Subject: [PATCH] Add demo_monorepo_ci.py example and update docs (Issue #4) --- README.md | 1 + docs/usage.md | 1 + examples/demo_monorepo_ci.py | 71 ++++++++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 examples/demo_monorepo_ci.py diff --git a/README.md b/README.md index 987da10..1b2fb6d 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,7 @@ python examples/demo_h2_moat.py # measure cross-tenant skill transfer python examples/demo_canary_rollback.py # bad merge fails canary → safe auto git-revert python examples/demo_capability_jail.py # learn a tool's syscalls → deny everything it didn't earn python examples/demo_polyglot_ci.py # Python/JS/Go + perf + security graders on one bus +python examples/demo_monorepo_ci.py # separate stage per package with a unified gate python examples/demo_consolidation.py # failures → structured rules → a 2nd-order schema python examples/demo_distributed_fleet.py # concurrent managers (fencing) + multi-repo DAG python examples/run_h2.py # LIVE: build skills, measure cross-tenant transfer diff --git a/docs/usage.md b/docs/usage.md index 67589f2..ed38ab4 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -415,6 +415,7 @@ Runnable, mostly offline — see the [`examples/`](https://github.com/amitpatole | gate a repo on tests+lint+types | `verel-ci check --repo .` | | self-heal failing tests | `python examples/demo_selfheal.py` | | grade Python/JS/Go + perf + security on one bus | `python examples/demo_polyglot_ci.py` | +| gate a monorepo with separate stages per package | `python examples/demo_monorepo_ci.py` | | consolidate failures → rules → schema → revise | `python examples/demo_consolidation.py` | | sandbox a tool to only the syscalls it earned | `python examples/demo_capability_jail.py` | | run concurrent managers + a multi-repo saga | `python examples/demo_distributed_fleet.py` | diff --git a/examples/demo_monorepo_ci.py b/examples/demo_monorepo_ci.py new file mode 100644 index 0000000..2439cbe --- /dev/null +++ b/examples/demo_monorepo_ci.py @@ -0,0 +1,71 @@ +"""Monorepo CI demo (§7.4) — separate stage per package, unified gate. + +In a monorepo, different packages might use different languages or tools. +We can run a separate stage per package (e.g. one Python, one Node) and +then gate the entire build on the combined results. + +Run: python examples/demo_monorepo_ci.py +""" + +from __future__ import annotations + +import json + +from verel.ci import inner_loop_stage, premerge_stage, run_stage +from verel.verdict import Verdict + +# Canned tool output keyed by the binary the stage invokes — the injected runner returns it. +OUT = { + "node": "TAP version 13\nok 1 - renders\nnot ok 2 - submit posts the form\n", + "bandit": json.dumps({"results": []}), +} + +def canned(cmd, cwd=None): + head = cmd[0] + if head == "node": + return (1, OUT["node"], "") + if head == "bandit": + return (0, OUT["bandit"], "") + # ruff/eslint/etc. — clean + return (0, "", "") + +def show(pkg, res): + print(f"\nPackage [{pkg}] ({res.name}): {res.verdict.value.upper()}") + for r in res.reports: + n = len(r.issues) + flag = " (errored)" if r.errored else "" + print(f" {r.grader.value:9} [{getattr(r, 'model', None) or '-'}] " + f"{n} issue(s){flag}" + (f" — {r.issues[0].message}" if n else "")) + +def main() -> None: + # A monorepo with a Python backend and a JS frontend. + packages = [ + {"name": "frontend", "path": "/repo/packages/frontend", "language": "js"}, + {"name": "backend", "path": "/repo/packages/backend", "language": "python"}, + ] + + print("── Monorepo CI: Running separate stages per package ──") + results = [] + overall_verdict = Verdict.PASS + + for pkg in packages: + # We can configure stages differently per package + if pkg["language"] == "python": + stage = premerge_stage(pkg["path"], language="python", security=True) + else: + stage = inner_loop_stage(pkg["path"], language=pkg["language"]) + + # run_stage accepts a runner that intercepts grader shell commands + res = run_stage(stage, runner=canned) + show(pkg["name"], res) + results.append(res) + + # Any failing package fails the entire monorepo build + if res.verdict == Verdict.FAIL: + overall_verdict = Verdict.FAIL + + print("\n── Unified Monorepo Gate ──") + print(f"Overall Monorepo Verdict: {overall_verdict.value.upper()}") + +if __name__ == "__main__": + main()