-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
154 lines (133 loc) · 5.15 KB
/
main.py
File metadata and controls
154 lines (133 loc) · 5.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env python3
"""
Run the full migration pipeline: parse project → load dependencies → match rules → print violations.
Usage:
python main.py [PROJECT_PATH] [--requirements PATH] [--rules PATH]
Examples:
python main.py
python main.py .
python main.py /path/to/repo
python main.py ./tests/sample_project --requirements ./tests/sample_project/requirements.txt
"""
import argparse
import importlib.util
import os
import sys
# Allow importing from src when run as python main.py from project root
_PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
_SRC = os.path.join(_PROJECT_ROOT, "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
from parser import parse_project
from dependency.requirements_parser import parse_requirements, DependencyContext
# Load rule_engine from src/engine/rule_engine.py (avoid conflict with src/engine.py)
_rule_engine_path = os.path.join(_SRC, "engine", "rule_engine.py")
_spec = importlib.util.spec_from_file_location("rule_engine", _rule_engine_path)
_rule_engine = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_rule_engine)
match_rules = _rule_engine.match_rules
# Load migration_strategy_llm from src/engine/ (avoid conflict with src/engine.py)
_llm_path = os.path.join(_SRC, "engine", "migration_strategy_llm.py")
_llm_spec = importlib.util.spec_from_file_location("migration_strategy_llm", _llm_path)
_llm_module = importlib.util.module_from_spec(_llm_spec)
_llm_spec.loader.exec_module(_llm_module)
generate_migration_strategy = _llm_module.generate_migration_strategy
def main() -> None:
parser = argparse.ArgumentParser(
description="Run migration pipeline: parse project, check rules, report violations."
)
parser.add_argument(
"project_path",
nargs="?",
default=".",
help="Path to the project directory to analyze (default: current directory)",
)
parser.add_argument(
"--requirements",
"-r",
default=None,
help="Path to requirements.txt (default: PROJECT_PATH/requirements.txt if it exists)",
)
parser.add_argument(
"--rules",
default=None,
help="Path to rules YAML (default: src/kb/rules.yaml)",
)
args = parser.parse_args()
project_path = os.path.abspath(args.project_path)
if not os.path.isdir(project_path):
print(f"Error: not a directory: {project_path}", file=sys.stderr)
sys.exit(1)
# Resolve requirements path
req_path = args.requirements
if req_path is None:
req_path = os.path.join(project_path, "requirements.txt")
if not os.path.isfile(req_path):
req_path = None
# Resolve rules path
rules_path = args.rules
if rules_path is None:
rules_path = os.path.join(_SRC, "kb", "rules.yaml")
if not os.path.isfile(rules_path):
print(f"Warning: rules file not found: {rules_path}", file=sys.stderr)
# 1. Parse project IR
print(f"Parsing project: {project_path}")
project_ir = parse_project(project_path)
print(f" Found {len(project_ir.files)} Python file(s)")
# 2. Dependency context
if req_path:
dependency_context = parse_requirements(req_path)
print(f" Loaded dependencies from {req_path} ({len(dependency_context.versions)} package(s))")
else:
dependency_context = DependencyContext(versions={})
print(" No requirements.txt found; using empty dependency context")
# 3. Match rules
violations = match_rules(project_ir, dependency_context, rules_path or "")
# 4. Report
print()
if not violations:
print("No violations found.")
return
print(f"Found {len(violations)} violation(s):\n")
for v in violations:
file_path = v.get("file", "")
line = v.get("line", 0)
library = v.get("library", "")
method = v.get("method", "")
message = v.get("message", "")
suggested_fix = v.get("suggested_fix", "")
severity = v.get("severity", "")
loc = f"{file_path}:{line}" if line else file_path
print(f" [{severity or 'info'}] {loc}")
print(f" {library}.{method or '?'}: {message}")
if suggested_fix:
print(f" → {suggested_fix}")
print()
print("Generating migration strategy (Gemini)...\n")
strategy = generate_migration_strategy(violations, dependency_context.versions)
# Print LLM summarizer output
print("Migration strategy")
print("=" * 60)
print(f"Scope: {strategy.get('migration_scope', '—')}")
print()
if strategy.get("affected_areas"):
print("Affected areas:")
for a in strategy["affected_areas"]:
print(f" • {a}")
print()
if strategy.get("recommended_order"):
print("Recommended order:")
for i, step in enumerate(strategy["recommended_order"], 1):
print(f" {i}. {step}")
print()
if strategy.get("testing_focus"):
print("Testing focus:")
for f in strategy["testing_focus"]:
print(f" • {f}")
print()
if strategy.get("risk_notes"):
print("Risk notes:")
print(f" {strategy['risk_notes']}")
print()
if __name__ == "__main__":
main()