diff --git a/tools/verify-aot-autoinstrumentation-goal.py b/tools/verify-aot-autoinstrumentation-goal.py index fffd6302..120151ef 100644 --- a/tools/verify-aot-autoinstrumentation-goal.py +++ b/tools/verify-aot-autoinstrumentation-goal.py @@ -7,6 +7,8 @@ import sys from pathlib import Path +from verify_helpers import remove_publish_outputs + ROOT = Path(__file__).resolve().parents[1] @@ -125,6 +127,11 @@ def main() -> None: action="store_true", help='Run only the container-backed "real * demo" verifiers.', ) + parser.add_argument( + "--keep-publish", + action="store_true", + help="Keep artifacts/publish after a successful run (default: removed — pure verification byproduct, multiple GB over the full demo matrix).", + ) args = parser.parse_args() if args.list: @@ -150,6 +157,11 @@ def main() -> None: if completed.returncode != 0: raise SystemExit(f"{name} failed with exit code {completed.returncode}") + # Only reached when every selected verifier passed; failures SystemExit above and + # keep artifacts/publish around for inspection. + if not args.keep_publish: + print(remove_publish_outputs()) + if commands == COMMANDS: print("aot-autoinstrumentation-goal-ok") else: diff --git a/tools/verify-aot-publish-gate.py b/tools/verify-aot-publish-gate.py index e8b05d27..c4fc70a5 100644 --- a/tools/verify-aot-publish-gate.py +++ b/tools/verify-aot-publish-gate.py @@ -38,6 +38,8 @@ import subprocess from pathlib import Path +from verify_helpers import remove_publish_outputs + ROOT = Path(__file__).resolve().parents[1] DEMOS_DIR = ROOT / "demos" GENERATOR_PROJECT = (ROOT / "src" / "Qyl.OpenTelemetry.AutoInstrumentation.SourceGenerators" @@ -186,6 +188,9 @@ def main() -> None: parser.add_argument("--strict-promotion", action="store_true", help="treat a promotion (vendor demo gone warning-clean) as a gate failure") parser.add_argument("--list", action="store_true", help="print the classification and exit") + parser.add_argument("--keep-publish", action="store_true", + help="keep artifacts/publish after a passing gate (default: removed — " + "pure verification byproduct, multiple GB over the full matrix)") args = parser.parse_args() if args.list: @@ -239,7 +244,10 @@ def main() -> None: fh.write(f"\n**{failures} failure(s), {promotions} promotion(s)** over {len(rows)} demo(s).\n") if failures: + # Keep artifacts/publish on failure so the offending binaries stay inspectable. raise SystemExit(f"aot-publish-gate FAILED: {failures} failure(s), {promotions} promotion(s)") + if not args.keep_publish: + print(remove_publish_outputs()) print(f"aot-publish-gate-ok ({len(rows)} demos, {promotions} promotion notice(s))") diff --git a/tools/verify_helpers.py b/tools/verify_helpers.py index 93db1201..76613bfc 100644 --- a/tools/verify_helpers.py +++ b/tools/verify_helpers.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import shutil import subprocess from pathlib import Path @@ -9,6 +10,32 @@ PROPS_PATH = ROOT / "Directory.Build.props" +def remove_publish_outputs() -> str: + """Delete artifacts/publish and return a one-line summary of what was freed. + + The AOT demos must publish into the repo's standard artifacts/ layout (redirecting + via --artifacts-path breaks the prebuilt-Analyzer path contract — see + verify-aot-publish-gate.py publish()), so every demo run regrows ~50-150 MB there + and a full matrix leaves multiple GB behind. The publish tree is a pure verification + byproduct — each verifier executes the binary and asserts within the same run — so + successful gates drop it; failed gates keep it for inspection. + """ + publish_root = ROOT / "artifacts" / "publish" + if not publish_root.exists(): + return "artifacts/publish absent — nothing to remove" + + try: + size_bytes = sum(f.stat().st_size for f in publish_root.rglob("*") if f.is_file()) + shutil.rmtree(publish_root) + except OSError as error: + # Best-effort hygiene: a locked file (AV scanner, IDE indexer, Windows handle) + # must not flip a passing gate into a false failure. Leave the remainder for + # the next run to sweep. + return f"artifacts/publish cleanup skipped: {error}" + + return f"removed artifacts/publish ({size_bytes / (1024 * 1024):.0f} MB)" + + def clean_env() -> dict[str, str]: env = dict(os.environ) for key in list(env):