Skip to content

Commit b6c082e

Browse files
committed
fix(cli): validate --entrypoint-rules before analysis starts
detect_entrypoints ran after the symbol table, venv build, Jedi and PyCG, so a malformed --entrypoint-rules file cost minutes on a large repo before failing with a raw traceback. Load the rules once options are built (before the --emit schema short-circuit and well before Codeanalyzer.analyze()) and exit cleanly on RulesError. The existing load_rules call inside detect_entrypoints stays as-is -- loading twice is cheap and keeps the entrypoints pipeline self-contained.
1 parent 8edc96c commit b6c082e

2 files changed

Lines changed: 41 additions & 0 deletions

File tree

codeanalyzer/__main__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,20 @@ def main(
398398

399399
_set_log_level(options.verbosity)
400400

401+
# Entrypoint rules are configuration, validated before any analysis work
402+
# starts (#122 review) -- a typo must fail in milliseconds, not after the
403+
# symbol table, venv build, Jedi and PyCG have all run. `detect_entrypoints`
404+
# loads the rules again at its own call site; that second load is cheap
405+
# and keeps the entrypoints pipeline self-contained.
406+
if options.entrypoint_rules:
407+
from codeanalyzer.entrypoints.rules import RulesError, load_rules
408+
409+
try:
410+
load_rules(options.entrypoint_rules)
411+
except RulesError as exc:
412+
logger.error(f"Invalid --entrypoint-rules: {exc}")
413+
raise typer.Exit(code=1)
414+
401415
# The schema contract is a static artifact — no project analysis required.
402416
if options.emit == EmitTarget.SCHEMA:
403417
from codeanalyzer.neo4j.emit import emit_schema

test/test_entrypoints_e2e.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,30 @@ def test_decorated_function_flagged_and_helper_not(tmp_path):
4747
report = data["application"]["entrypoint_report"]
4848
assert "inhouse" in report["frameworks_detected"]
4949
assert report["errors"] == []
50+
51+
52+
def test_malformed_entrypoint_rules_fails_fast_before_analysis(tmp_path):
53+
"""A malformed ``--entrypoint-rules`` file must exit BEFORE the symbol
54+
table, venv build, Jedi and PyCG run -- not deep in the pipeline
55+
(#122 review, IMPORTANT 1). Proven by wall-clock: a real analysis of
56+
even this tiny fixture takes noticeably longer than the sub-second
57+
failure this must produce."""
58+
bad_rules = tmp_path / "bad.yml"
59+
bad_rules.write_text("frameworks: [not, a, mapping]\n")
60+
61+
result = subprocess.run(
62+
[
63+
"uv", "run", "canpy",
64+
"-i", str(FIXTURE),
65+
"-a", "1",
66+
"-o", str(tmp_path / "out"),
67+
"--no-venv",
68+
"--cache-dir", str(tmp_path / "cache"),
69+
"--entrypoint-rules", str(bad_rules),
70+
],
71+
capture_output=True,
72+
text=True,
73+
)
74+
assert result.returncode != 0
75+
assert not (tmp_path / "out" / "analysis.json").exists()
76+
assert "entrypoint-rules" in (result.stderr + result.stdout).lower()

0 commit comments

Comments
 (0)