codeguardian-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- CODEGUARDIAN/__init__.py +0 -0
- CODEGUARDIAN/analyzers/__init__.py +0 -0
- CODEGUARDIAN/analyzers/circular_dependency_analyzer.py +101 -0
- CODEGUARDIAN/analyzers/db_access_analyzer.py +55 -0
- CODEGUARDIAN/analyzers/dependency_analyzer.py +259 -0
- CODEGUARDIAN/analyzers/file_analyzer.py +96 -0
- CODEGUARDIAN/analyzers/function_analyzer.py +199 -0
- CODEGUARDIAN/analyzers/source_code_analyzer.py +169 -0
- CODEGUARDIAN/cli/__init__.py +0 -0
- CODEGUARDIAN/cli/commands.py +619 -0
- CODEGUARDIAN/config/__init__.py +0 -0
- CODEGUARDIAN/config/config_loader.py +76 -0
- CODEGUARDIAN/config/settings.py +2 -0
- CODEGUARDIAN/main.py +4 -0
- CODEGUARDIAN/reports/__init__.py +0 -0
- CODEGUARDIAN/reports/architecture_score.py +68 -0
- CODEGUARDIAN/reports/console_reporter.py +487 -0
- CODEGUARDIAN/reports/html_reporter.py +293 -0
- CODEGUARDIAN/reports/json_reporter.py +69 -0
- CODEGUARDIAN/reports/markdown_reporter.py +130 -0
- CODEGUARDIAN/reports/severity.py +15 -0
- CODEGUARDIAN/reports/statistics_reporter.py +183 -0
- CODEGUARDIAN/rules/__init__.py +0 -0
- CODEGUARDIAN/rules/architecture_rules.py +5 -0
- CODEGUARDIAN/rules/architecture_validator.py +69 -0
- CODEGUARDIAN/rules/controllers/user_controller.py +16 -0
- CODEGUARDIAN/src/__init__.py +0 -0
- CODEGUARDIAN/src/discovery/__init__.py +0 -0
- CODEGUARDIAN/src/discovery/finder.py +39 -0
- CODEGUARDIAN/src/extractor/__init__.py +0 -0
- CODEGUARDIAN/src/extractor/class_extractor.py +32 -0
- CODEGUARDIAN/src/extractor/function_extractor.py +17 -0
- CODEGUARDIAN/src/extractor/import_extractor.py +23 -0
- CODEGUARDIAN/src/extractor/python_class_extractor.py +13 -0
- CODEGUARDIAN/src/extractor/python_function_extractor.py +16 -0
- CODEGUARDIAN/src/extractor/python_imports_extractor.py +24 -0
- CODEGUARDIAN/src/main.py +65 -0
- CODEGUARDIAN/src/parser/__init__.py +0 -0
- CODEGUARDIAN/src/parser/ts_parser.py +14 -0
- CODEGUARDIAN/src/tree/Walker.py +10 -0
- CODEGUARDIAN/src/tree/__init__.py +0 -0
- CODEGUARDIAN/utils/__init__.py +0 -0
- CODEGUARDIAN/utils/ast_cache.py +47 -0
- CODEGUARDIAN/utils/file_cache.py +30 -0
- CODEGUARDIAN/utils/layer_utils.py +19 -0
- codeguardian_cli-1.0.0.dist-info/METADATA +43 -0
- codeguardian_cli-1.0.0.dist-info/RECORD +50 -0
- codeguardian_cli-1.0.0.dist-info/WHEEL +5 -0
- codeguardian_cli-1.0.0.dist-info/entry_points.txt +2 -0
- codeguardian_cli-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
from CODEGUARDIAN.config.config_loader import (
|
|
2
|
+
ConfigLoader
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ArchitectureScoreCalculator:
|
|
7
|
+
|
|
8
|
+
def __init__(self):
|
|
9
|
+
|
|
10
|
+
config = ConfigLoader.load()
|
|
11
|
+
|
|
12
|
+
self.penalties = config.get(
|
|
13
|
+
"architecture_score",
|
|
14
|
+
{}
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
def calculate(
|
|
18
|
+
self,
|
|
19
|
+
file_violations,
|
|
20
|
+
function_violations,
|
|
21
|
+
architecture_violations,
|
|
22
|
+
circular_violations,
|
|
23
|
+
db_access_violations
|
|
24
|
+
):
|
|
25
|
+
|
|
26
|
+
score = 100
|
|
27
|
+
|
|
28
|
+
score -= (
|
|
29
|
+
len(file_violations)
|
|
30
|
+
* self.penalties.get(
|
|
31
|
+
"file_penalty",
|
|
32
|
+
10
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
score -= (
|
|
37
|
+
len(function_violations)
|
|
38
|
+
* self.penalties.get(
|
|
39
|
+
"function_penalty",
|
|
40
|
+
5
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
score -= (
|
|
45
|
+
len(architecture_violations)
|
|
46
|
+
* self.penalties.get(
|
|
47
|
+
"architecture_penalty",
|
|
48
|
+
20
|
|
49
|
+
)
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
score -= (
|
|
53
|
+
len(circular_violations)
|
|
54
|
+
* self.penalties.get(
|
|
55
|
+
"circular_dependency_penalty",
|
|
56
|
+
30
|
|
57
|
+
)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
score -= (
|
|
61
|
+
len(db_access_violations)
|
|
62
|
+
* self.penalties.get(
|
|
63
|
+
"db_access_penalty",
|
|
64
|
+
15
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
return max(score, 0)
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
from rich.console import Console
|
|
2
|
+
from rich.table import Table
|
|
3
|
+
|
|
4
|
+
from CODEGUARDIAN.reports.severity import (
|
|
5
|
+
get_severity
|
|
6
|
+
)
|
|
7
|
+
from rich.panel import Panel
|
|
8
|
+
from rich.text import Text
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ConsoleReporter:
|
|
13
|
+
|
|
14
|
+
def __init__(self):
|
|
15
|
+
|
|
16
|
+
self.console = Console()
|
|
17
|
+
|
|
18
|
+
def show_dashboard(
|
|
19
|
+
self,
|
|
20
|
+
source_analysis,
|
|
21
|
+
file_violations,
|
|
22
|
+
function_violations,
|
|
23
|
+
architecture_violations,
|
|
24
|
+
circular_violations,
|
|
25
|
+
db_access_violations,
|
|
26
|
+
score
|
|
27
|
+
):
|
|
28
|
+
|
|
29
|
+
total_files = len(source_analysis)
|
|
30
|
+
|
|
31
|
+
total_classes = sum(
|
|
32
|
+
len(item["classes"])
|
|
33
|
+
for item in source_analysis
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
total_functions = sum(
|
|
37
|
+
len(item["functions"])
|
|
38
|
+
for item in source_analysis
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
total_imports = sum(
|
|
42
|
+
len(item["imports"])
|
|
43
|
+
for item in source_analysis
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
if score >= 90:
|
|
47
|
+
score_style = "green"
|
|
48
|
+
|
|
49
|
+
elif score >= 70:
|
|
50
|
+
score_style = "yellow"
|
|
51
|
+
|
|
52
|
+
else:
|
|
53
|
+
score_style = "red"
|
|
54
|
+
|
|
55
|
+
dashboard = Text()
|
|
56
|
+
|
|
57
|
+
dashboard.append(
|
|
58
|
+
f"✔ Files Scanned : {total_files}\n",
|
|
59
|
+
style="cyan"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
dashboard.append(
|
|
63
|
+
f"✔ Classes : {total_classes}\n",
|
|
64
|
+
style="green"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
dashboard.append(
|
|
68
|
+
f"✔ Functions : {total_functions}\n",
|
|
69
|
+
style="green"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
dashboard.append(
|
|
73
|
+
f"✔ Imports : {total_imports}\n\n",
|
|
74
|
+
style="green"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Oversized Files
|
|
78
|
+
if len(file_violations) == 0:
|
|
79
|
+
dashboard.append(
|
|
80
|
+
f"✔ Oversized Files : 0\n",
|
|
81
|
+
style="green"
|
|
82
|
+
)
|
|
83
|
+
else:
|
|
84
|
+
dashboard.append(
|
|
85
|
+
f"⚠ Oversized Files : {len(file_violations)}\n",
|
|
86
|
+
style="yellow"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Oversized Functions
|
|
90
|
+
if len(function_violations) == 0:
|
|
91
|
+
dashboard.append(
|
|
92
|
+
f"✔ Oversized Functions : 0\n",
|
|
93
|
+
style="green"
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
dashboard.append(
|
|
97
|
+
f"⚠ Oversized Functions : {len(function_violations)}\n",
|
|
98
|
+
style="yellow"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Architecture Violations
|
|
102
|
+
if len(architecture_violations) == 0:
|
|
103
|
+
dashboard.append(
|
|
104
|
+
f"✔ Architecture Violations : 0\n",
|
|
105
|
+
style="green"
|
|
106
|
+
)
|
|
107
|
+
else:
|
|
108
|
+
dashboard.append(
|
|
109
|
+
f"✖ Architecture Violations : {len(architecture_violations)}\n",
|
|
110
|
+
style="red"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
# Circular Dependencies
|
|
114
|
+
if len(circular_violations) == 0:
|
|
115
|
+
dashboard.append(
|
|
116
|
+
f"✔ Circular Dependencies : 0\n",
|
|
117
|
+
style="green"
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
dashboard.append(
|
|
121
|
+
f"✖ Circular Dependencies : {len(circular_violations)}\n",
|
|
122
|
+
style="red"
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Direct Database Access
|
|
126
|
+
if len(db_access_violations) == 0:
|
|
127
|
+
dashboard.append(
|
|
128
|
+
f"✔ Direct DB Access : 0\n\n",
|
|
129
|
+
style="green"
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
dashboard.append(
|
|
133
|
+
f"✖ Direct DB Access : {len(db_access_violations)}\n\n",
|
|
134
|
+
style="red"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
self.console.print(
|
|
138
|
+
Panel.fit(
|
|
139
|
+
dashboard,
|
|
140
|
+
title="Architecture Dashboard",
|
|
141
|
+
border_style="cyan"
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def show_file_violations(
|
|
146
|
+
self,
|
|
147
|
+
violations
|
|
148
|
+
):
|
|
149
|
+
|
|
150
|
+
table = Table(
|
|
151
|
+
title="Oversized Files"
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
table.add_column(
|
|
155
|
+
"File",
|
|
156
|
+
style="cyan"
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
table.add_column(
|
|
160
|
+
"Lines",
|
|
161
|
+
style="red"
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
table.add_column(
|
|
165
|
+
"Severity",
|
|
166
|
+
style="yellow"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
for violation in violations:
|
|
170
|
+
|
|
171
|
+
severity = get_severity(
|
|
172
|
+
violation.line_count,
|
|
173
|
+
warning_threshold=300,
|
|
174
|
+
critical_threshold=500
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
table.add_row(
|
|
178
|
+
violation.file_path,
|
|
179
|
+
str(violation.line_count),
|
|
180
|
+
severity
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
self.console.print(table)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def show_function_violations(
|
|
187
|
+
self,
|
|
188
|
+
violations
|
|
189
|
+
):
|
|
190
|
+
|
|
191
|
+
table = Table(
|
|
192
|
+
title="Oversized Functions"
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
table.add_column(
|
|
196
|
+
"Function",
|
|
197
|
+
style="cyan"
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
table.add_column(
|
|
201
|
+
"Lines",
|
|
202
|
+
style="red"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
table.add_column(
|
|
206
|
+
"Severity",
|
|
207
|
+
style="yellow"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
for violation in violations:
|
|
211
|
+
|
|
212
|
+
severity = get_severity(
|
|
213
|
+
violation.line_count,
|
|
214
|
+
warning_threshold=50,
|
|
215
|
+
critical_threshold=100
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
table.add_row(
|
|
219
|
+
violation.function_name,
|
|
220
|
+
str(violation.line_count),
|
|
221
|
+
severity
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
self.console.print(table)
|
|
225
|
+
|
|
226
|
+
def show_architecture_violations(
|
|
227
|
+
self,
|
|
228
|
+
violations
|
|
229
|
+
):
|
|
230
|
+
|
|
231
|
+
table = Table(
|
|
232
|
+
title="Architecture Violations"
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
table.add_column(
|
|
236
|
+
"File",
|
|
237
|
+
style="cyan"
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
table.add_column(
|
|
241
|
+
"Rule Broken",
|
|
242
|
+
style="red"
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
table.add_column(
|
|
246
|
+
"Severity",
|
|
247
|
+
style="red"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
for violation in violations:
|
|
251
|
+
|
|
252
|
+
table.add_row(
|
|
253
|
+
violation.source_file,
|
|
254
|
+
(
|
|
255
|
+
f"{violation.source_layer}"
|
|
256
|
+
f" → "
|
|
257
|
+
f"{violation.target_layer}"
|
|
258
|
+
),
|
|
259
|
+
"🔴 CRITICAL"
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
self.console.print(table)
|
|
263
|
+
|
|
264
|
+
def show_circular_dependencies(
|
|
265
|
+
self,
|
|
266
|
+
violations
|
|
267
|
+
):
|
|
268
|
+
|
|
269
|
+
table = Table(
|
|
270
|
+
title="Circular Dependencies"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
table.add_column(
|
|
274
|
+
"Dependency Cycle",
|
|
275
|
+
style="red"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
table.add_column(
|
|
279
|
+
"Severity",
|
|
280
|
+
style="red"
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
for violation in violations:
|
|
284
|
+
|
|
285
|
+
table.add_row(
|
|
286
|
+
" → ".join(
|
|
287
|
+
violation.cycle
|
|
288
|
+
),
|
|
289
|
+
"🔴 CRITICAL"
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
self.console.print(table)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def show_db_access_violations(
|
|
296
|
+
self,
|
|
297
|
+
violations
|
|
298
|
+
):
|
|
299
|
+
|
|
300
|
+
table = Table(
|
|
301
|
+
title="Direct Database Access"
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
table.add_column(
|
|
305
|
+
"Controller",
|
|
306
|
+
style="cyan"
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
table.add_column(
|
|
310
|
+
"Database",
|
|
311
|
+
style="red"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
for violation in violations:
|
|
315
|
+
|
|
316
|
+
table.add_row(
|
|
317
|
+
|
|
318
|
+
violation.source_file,
|
|
319
|
+
|
|
320
|
+
violation.database_library
|
|
321
|
+
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
self.console.print(table)
|
|
325
|
+
|
|
326
|
+
def show_summary(
|
|
327
|
+
self,
|
|
328
|
+
total_files,
|
|
329
|
+
file_violations,
|
|
330
|
+
function_violations,
|
|
331
|
+
architecture_violations,
|
|
332
|
+
circular_violations,
|
|
333
|
+
db_access_violations,
|
|
334
|
+
score
|
|
335
|
+
):
|
|
336
|
+
|
|
337
|
+
table = Table(
|
|
338
|
+
title="CodeGuardian Summary"
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
table.add_column(
|
|
342
|
+
"Metric",
|
|
343
|
+
style="cyan"
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
table.add_column(
|
|
347
|
+
"Value",
|
|
348
|
+
style="green"
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
table.add_row(
|
|
352
|
+
"Files Scanned",
|
|
353
|
+
str(total_files)
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
table.add_row(
|
|
357
|
+
"Oversized Files",
|
|
358
|
+
str(len(file_violations))
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
table.add_row(
|
|
362
|
+
"Oversized Functions",
|
|
363
|
+
str(len(function_violations))
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
table.add_row(
|
|
367
|
+
"Architecture Violations",
|
|
368
|
+
str(len(architecture_violations))
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
table.add_row(
|
|
372
|
+
"Circular Dependencies",
|
|
373
|
+
str(len(circular_violations))
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
table.add_row(
|
|
377
|
+
"Direct Database Access",
|
|
378
|
+
str(len(db_access_violations))
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
table.add_row(
|
|
382
|
+
"Architecture Score",
|
|
383
|
+
f"{score}/100"
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
self.console.print(table)
|
|
387
|
+
|
|
388
|
+
def show_source_analysis(
|
|
389
|
+
self,
|
|
390
|
+
source_analysis,
|
|
391
|
+
details=False
|
|
392
|
+
):
|
|
393
|
+
|
|
394
|
+
table = Table(
|
|
395
|
+
title="Source Code Analysis"
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
table.add_column(
|
|
399
|
+
"File",
|
|
400
|
+
style="cyan"
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
table.add_column(
|
|
404
|
+
"Classes",
|
|
405
|
+
justify="center",
|
|
406
|
+
style="green"
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
table.add_column(
|
|
410
|
+
"Functions",
|
|
411
|
+
justify="center",
|
|
412
|
+
style="yellow"
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
table.add_column(
|
|
416
|
+
"Imports",
|
|
417
|
+
justify="center",
|
|
418
|
+
style="magenta"
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
for item in source_analysis:
|
|
422
|
+
|
|
423
|
+
table.add_row(
|
|
424
|
+
item["file"],
|
|
425
|
+
str(len(item["classes"])),
|
|
426
|
+
str(len(item["functions"])),
|
|
427
|
+
str(len(item["imports"]))
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
self.console.print(table)
|
|
431
|
+
|
|
432
|
+
if not details:
|
|
433
|
+
return
|
|
434
|
+
|
|
435
|
+
self.console.rule(
|
|
436
|
+
"[bold blue]Source Code Details[/bold blue]"
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
for item in source_analysis:
|
|
440
|
+
|
|
441
|
+
# Skip completely empty files
|
|
442
|
+
if (
|
|
443
|
+
not item["classes"]
|
|
444
|
+
and not item["functions"]
|
|
445
|
+
and not item["imports"]
|
|
446
|
+
):
|
|
447
|
+
continue
|
|
448
|
+
|
|
449
|
+
self.console.print()
|
|
450
|
+
|
|
451
|
+
self.console.print(
|
|
452
|
+
f"[bold cyan]{item['file']}[/bold cyan]"
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
classes = (
|
|
456
|
+
", ".join(item["classes"])
|
|
457
|
+
if item["classes"]
|
|
458
|
+
else "-"
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
functions = (
|
|
462
|
+
", ".join(item["functions"])
|
|
463
|
+
if item["functions"]
|
|
464
|
+
else "-"
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
imports = (
|
|
468
|
+
", ".join(item["imports"])
|
|
469
|
+
if item["imports"]
|
|
470
|
+
else "-"
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
self.console.print(
|
|
474
|
+
f"[green]Classes:[/green] {classes}"
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
self.console.print(
|
|
478
|
+
f"[yellow]Functions:[/yellow] {functions}"
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
self.console.print(
|
|
482
|
+
f"[magenta]Imports:[/magenta] {imports}"
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
self.console.print(
|
|
486
|
+
"-" * 70
|
|
487
|
+
)
|