Files
magnus919_agent-skills/scripts/check-artifacts.py
T
usernameandfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com> 96f0bd4e46 fix(ci): prevent sys.modules cross-contamination between test directories in check-artifacts
When unittest discover processes skills/binary-analysis/tests/ before
tests/, the integration package gets cached in sys.modules from the
first discover call. Later discover calls reuse the cached package
which doesn't contain the expected test modules, causing spurious
ModuleNotFoundError failures.

Fix: snapshot and restore sys.modules between discover iterations.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-07-30 19:10:03 -04:00

108 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Validate runnable repository artifacts selected from the Git index."""
import argparse
import json
import subprocess
import sys
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
def tracked_files() -> list[Path]:
result = subprocess.run(
["git", "ls-files", "-z"],
cwd=ROOT,
check=True,
capture_output=True,
)
return [ROOT / name for name in result.stdout.decode().split("\0") if name]
def is_shell_script(path: Path) -> bool:
if path.suffix in {".sh", ".bash"}:
return True
try:
first_line = path.open("rb").readline().decode("utf-8", "replace")
except OSError:
return False
return first_line.startswith("#!") and any(
shell in first_line for shell in ("/sh", "/bash", "env sh", "env bash")
)
def test_directories(files: list[Path]) -> list[Path]:
directories = set()
for path in files:
for parent in path.parents:
if parent.name == "tests":
directories.add(parent)
break
if parent == ROOT:
break
return sorted(directories)
def run_checks(files: list[Path]) -> list[str]:
errors = []
for path in files:
relative = path.relative_to(ROOT)
if path.suffix == ".py":
try:
compile(path.read_bytes(), str(relative), "exec")
except (OSError, SyntaxError) as error:
errors.append(f"python {relative}: {error}")
elif path.suffix == ".json":
try:
json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
errors.append(f"json {relative}: {error}")
if is_shell_script(path):
result = subprocess.run(
["bash", "-n", str(path)], cwd=ROOT, text=True, capture_output=True
)
if result.returncode:
errors.append(f"bash -n {relative}: {result.stderr.strip()}")
for directory in test_directories(files):
relative = directory.relative_to(ROOT)
before = set(sys.modules)
result = unittest.TextTestRunner(verbosity=0).run(
unittest.defaultTestLoader.discover(str(directory), top_level_dir=str(directory))
)
for mod in list(sys.modules):
if mod not in before:
del sys.modules[mod]
if not result.wasSuccessful():
errors.append(
f"unittest discover {relative}: "
f"{len(result.failures)} failures, {len(result.errors)} errors"
)
return errors
def self_check() -> None:
files = [ROOT / "example/tests/test_example.py", ROOT / "elsewhere/file.py"]
assert test_directories(files) == [ROOT / "example/tests"]
assert is_shell_script(ROOT / "scripts/check-artifacts.py") is False
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--self-check", action="store_true")
args = parser.parse_args()
if args.self_check:
self_check()
return 0
errors = run_checks(tracked_files())
for error in errors:
print(error, file=sys.stderr)
return 1 if errors else 0
if __name__ == "__main__":
sys.exit(main())