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.
Files changed (50) hide show
  1. CODEGUARDIAN/__init__.py +0 -0
  2. CODEGUARDIAN/analyzers/__init__.py +0 -0
  3. CODEGUARDIAN/analyzers/circular_dependency_analyzer.py +101 -0
  4. CODEGUARDIAN/analyzers/db_access_analyzer.py +55 -0
  5. CODEGUARDIAN/analyzers/dependency_analyzer.py +259 -0
  6. CODEGUARDIAN/analyzers/file_analyzer.py +96 -0
  7. CODEGUARDIAN/analyzers/function_analyzer.py +199 -0
  8. CODEGUARDIAN/analyzers/source_code_analyzer.py +169 -0
  9. CODEGUARDIAN/cli/__init__.py +0 -0
  10. CODEGUARDIAN/cli/commands.py +619 -0
  11. CODEGUARDIAN/config/__init__.py +0 -0
  12. CODEGUARDIAN/config/config_loader.py +76 -0
  13. CODEGUARDIAN/config/settings.py +2 -0
  14. CODEGUARDIAN/main.py +4 -0
  15. CODEGUARDIAN/reports/__init__.py +0 -0
  16. CODEGUARDIAN/reports/architecture_score.py +68 -0
  17. CODEGUARDIAN/reports/console_reporter.py +487 -0
  18. CODEGUARDIAN/reports/html_reporter.py +293 -0
  19. CODEGUARDIAN/reports/json_reporter.py +69 -0
  20. CODEGUARDIAN/reports/markdown_reporter.py +130 -0
  21. CODEGUARDIAN/reports/severity.py +15 -0
  22. CODEGUARDIAN/reports/statistics_reporter.py +183 -0
  23. CODEGUARDIAN/rules/__init__.py +0 -0
  24. CODEGUARDIAN/rules/architecture_rules.py +5 -0
  25. CODEGUARDIAN/rules/architecture_validator.py +69 -0
  26. CODEGUARDIAN/rules/controllers/user_controller.py +16 -0
  27. CODEGUARDIAN/src/__init__.py +0 -0
  28. CODEGUARDIAN/src/discovery/__init__.py +0 -0
  29. CODEGUARDIAN/src/discovery/finder.py +39 -0
  30. CODEGUARDIAN/src/extractor/__init__.py +0 -0
  31. CODEGUARDIAN/src/extractor/class_extractor.py +32 -0
  32. CODEGUARDIAN/src/extractor/function_extractor.py +17 -0
  33. CODEGUARDIAN/src/extractor/import_extractor.py +23 -0
  34. CODEGUARDIAN/src/extractor/python_class_extractor.py +13 -0
  35. CODEGUARDIAN/src/extractor/python_function_extractor.py +16 -0
  36. CODEGUARDIAN/src/extractor/python_imports_extractor.py +24 -0
  37. CODEGUARDIAN/src/main.py +65 -0
  38. CODEGUARDIAN/src/parser/__init__.py +0 -0
  39. CODEGUARDIAN/src/parser/ts_parser.py +14 -0
  40. CODEGUARDIAN/src/tree/Walker.py +10 -0
  41. CODEGUARDIAN/src/tree/__init__.py +0 -0
  42. CODEGUARDIAN/utils/__init__.py +0 -0
  43. CODEGUARDIAN/utils/ast_cache.py +47 -0
  44. CODEGUARDIAN/utils/file_cache.py +30 -0
  45. CODEGUARDIAN/utils/layer_utils.py +19 -0
  46. codeguardian_cli-1.0.0.dist-info/METADATA +43 -0
  47. codeguardian_cli-1.0.0.dist-info/RECORD +50 -0
  48. codeguardian_cli-1.0.0.dist-info/WHEEL +5 -0
  49. codeguardian_cli-1.0.0.dist-info/entry_points.txt +2 -0
  50. codeguardian_cli-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,619 @@
1
+ import os
2
+ import typer
3
+
4
+ from rich.console import Console
5
+ from rich.panel import Panel
6
+ from rich.progress import Progress
7
+
8
+ from CODEGUARDIAN.analyzers.file_analyzer import FileAnalyzer
9
+ from CODEGUARDIAN.analyzers.function_analyzer import FunctionAnalyzer
10
+ from CODEGUARDIAN.analyzers.dependency_analyzer import (
11
+ DependencyAnalyzer
12
+ )
13
+ from CODEGUARDIAN.analyzers.circular_dependency_analyzer import (
14
+ CircularDependencyAnalyzer
15
+ )
16
+ from CODEGUARDIAN.analyzers.source_code_analyzer import (
17
+ SourceCodeAnalyzer
18
+ )
19
+
20
+ from CODEGUARDIAN.rules.architecture_validator import (
21
+ ArchitectureValidator
22
+ )
23
+
24
+ from CODEGUARDIAN.reports.console_reporter import (
25
+ ConsoleReporter
26
+ )
27
+
28
+ from CODEGUARDIAN.reports.json_reporter import (
29
+ JsonReporter
30
+ )
31
+
32
+ from CODEGUARDIAN.reports.architecture_score import (
33
+ ArchitectureScoreCalculator
34
+ )
35
+ from CODEGUARDIAN.reports.html_reporter import (
36
+ HtmlReporter
37
+ )
38
+ from CODEGUARDIAN.reports.markdown_reporter import (
39
+ MarkdownReporter
40
+ )
41
+
42
+ from CODEGUARDIAN.analyzers.db_access_analyzer import (
43
+ DBAccessAnalyzer
44
+ )
45
+ from CODEGUARDIAN.reports.statistics_reporter import (
46
+ StatisticsReporter
47
+ )
48
+
49
+
50
+ app = typer.Typer(
51
+ help="""
52
+ CodeGuardian
53
+
54
+ Analyze your project's architecture and code quality.
55
+
56
+ Supported languages:
57
+ • Python (.py)
58
+ • TypeScript (.ts)
59
+ • JavaScript (.js)
60
+
61
+ Generate reports in Console, JSON, HTML and Markdown.
62
+ """
63
+ )
64
+ console = Console()
65
+
66
+
67
+ def get_score_color(score):
68
+
69
+ if score >= 90:
70
+ return "green"
71
+
72
+ elif score >= 70:
73
+ return "yellow"
74
+
75
+ return "red"
76
+
77
+
78
+ def calculate_project_score(
79
+ path,
80
+ max_file_lines=None,
81
+ max_function_lines=None
82
+ ):
83
+ file_violations = (
84
+ FileAnalyzer(
85
+ max_file_lines
86
+ ).analyze(path)
87
+ )
88
+
89
+ function_violations = (
90
+ FunctionAnalyzer(
91
+ max_function_lines
92
+ ).analyze_project(path)
93
+ )
94
+
95
+ dependencies = (
96
+ DependencyAnalyzer()
97
+ .analyze_project(path)
98
+ )
99
+ db_access_violations = (
100
+ DBAccessAnalyzer()
101
+ .analyze(dependencies)
102
+ )
103
+
104
+
105
+
106
+ architecture_violations = (
107
+ ArchitectureValidator()
108
+ .validate(dependencies)
109
+ )
110
+
111
+ circular_violations = (
112
+ CircularDependencyAnalyzer()
113
+ .detect(dependencies)
114
+ )
115
+
116
+ return (
117
+ ArchitectureScoreCalculator()
118
+ .calculate(
119
+ file_violations,
120
+ function_violations,
121
+ architecture_violations,
122
+ circular_violations,
123
+ db_access_violations
124
+ )
125
+ )
126
+
127
+
128
+ def count_python_files(path):
129
+
130
+ count = 0
131
+
132
+ ignore_dirs = {
133
+ "venv",
134
+ ".git",
135
+ "__pycache__",
136
+ ".pytest_cache"
137
+ }
138
+
139
+ for root, dirs, files in os.walk(path):
140
+
141
+ dirs[:] = [
142
+ d for d in dirs
143
+ if d not in ignore_dirs
144
+ ]
145
+
146
+ for file in files:
147
+
148
+ if file.endswith(".py"):
149
+
150
+ count += 1
151
+
152
+ return count
153
+
154
+
155
+ @app.command(
156
+ help="""
157
+ Run a complete architecture scan.
158
+
159
+ Examples:
160
+
161
+ python main.py scan
162
+
163
+ python main.py scan --json
164
+
165
+ python main.py scan --html
166
+
167
+ python main.py scan --markdown
168
+
169
+ python main.py scan --score
170
+
171
+ python main.py scan --details
172
+ """
173
+ )
174
+ def scan(
175
+
176
+ path: str = ".",
177
+ max_file_lines: int | None = typer.Option(
178
+ None,
179
+ "--max-file-lines",
180
+ help="Maximum allowed lines per file."
181
+ ),
182
+
183
+ max_function_lines: int | None = typer.Option(
184
+ None,
185
+ "--max-function-lines",
186
+ help="Maximum allowed lines per function."
187
+ ),
188
+ json_output: bool = typer.Option(
189
+ False,
190
+ "--json",
191
+ "-j",
192
+ help="Generate a JSON report."
193
+ ),
194
+
195
+ html_output: bool = typer.Option(
196
+ False,
197
+ "--html",
198
+ help="Generate an HTML report."
199
+ ),
200
+
201
+ score_only: bool = typer.Option(
202
+ False,
203
+ "--score",
204
+ help="Display only the architecture score."
205
+ ),
206
+ details: bool = typer.Option(
207
+ False,
208
+ "--details",
209
+ help="Show detailed classes, functions and imports."
210
+ ),
211
+ markdown_output: bool = typer.Option(
212
+ False,
213
+ "--markdown",
214
+ help="Generate a Markdown report."
215
+ ),
216
+ statistics: bool = typer.Option(
217
+ False,
218
+ "--statistics",
219
+ "-s",
220
+ help="Display project scan statistics."
221
+ ),
222
+
223
+ ):
224
+ if not (
225
+ json_output
226
+ or html_output
227
+ or score_only
228
+ ):
229
+
230
+ console.print(
231
+ Panel.fit(
232
+ f"Scanning project: "
233
+ f"[bold cyan]{path}[/bold cyan]",
234
+ title="CodeGuardian"
235
+ )
236
+ )
237
+
238
+ reporter = ConsoleReporter()
239
+
240
+ with Progress(
241
+ disable=(
242
+ json_output
243
+ or html_output
244
+ or markdown_output
245
+ or score_only
246
+ )
247
+ ) as progress:
248
+
249
+ file_task = progress.add_task(
250
+ "[cyan]Analyzing files...",
251
+ total=1
252
+ )
253
+
254
+ file_violations = (
255
+ FileAnalyzer(
256
+ max_file_lines
257
+ ).analyze(path)
258
+ )
259
+
260
+ progress.update(
261
+ file_task,
262
+ advance=1
263
+ )
264
+
265
+ function_task = progress.add_task(
266
+ "[green]Analyzing functions...",
267
+ total=1
268
+ )
269
+
270
+ function_violations = (
271
+ FunctionAnalyzer(
272
+ max_function_lines
273
+ ).analyze_project(path)
274
+ )
275
+
276
+ progress.update(
277
+ function_task,
278
+ advance=1
279
+ )
280
+
281
+ dependency_task = progress.add_task(
282
+ "[yellow]Analyzing dependencies...",
283
+ total=1
284
+ )
285
+
286
+ dependencies = (
287
+ DependencyAnalyzer()
288
+ .analyze_project(path)
289
+ )
290
+
291
+ db_access_violations = (
292
+ DBAccessAnalyzer()
293
+ .analyze(dependencies)
294
+ )
295
+
296
+ progress.update(
297
+ dependency_task,
298
+ advance=1
299
+ )
300
+
301
+ architecture_task = progress.add_task(
302
+ "[red]Validating architecture...",
303
+ total=1
304
+ )
305
+
306
+ architecture_violations = (
307
+ ArchitectureValidator()
308
+ .validate(dependencies)
309
+ )
310
+
311
+ progress.update(
312
+ architecture_task,
313
+ advance=1
314
+ )
315
+
316
+ circular_task = progress.add_task(
317
+ "[magenta]Checking circular dependencies...",
318
+ total=1
319
+ )
320
+
321
+ circular_violations = (
322
+ CircularDependencyAnalyzer()
323
+ .detect(dependencies)
324
+ )
325
+
326
+ progress.update(
327
+ circular_task,
328
+ advance=1
329
+ )
330
+
331
+ source_task = progress.add_task(
332
+ "[blue]Analyzing JS/TS files...",
333
+ total=1
334
+ )
335
+
336
+ source_analysis = (
337
+ SourceCodeAnalyzer()
338
+ .analyze(path)
339
+ )
340
+
341
+ progress.update(
342
+ source_task,
343
+ advance=1
344
+ )
345
+
346
+ score_task = progress.add_task(
347
+ "[white]Calculating score...",
348
+ total=1
349
+ )
350
+
351
+ score = (
352
+ ArchitectureScoreCalculator()
353
+ .calculate(file_violations,function_violations,architecture_violations,circular_violations,db_access_violations
354
+ )
355
+ )
356
+
357
+ progress.update(
358
+ score_task,
359
+ advance=1
360
+ )
361
+
362
+ if json_output:
363
+
364
+ output = (
365
+ JsonReporter()
366
+ .generate(
367
+
368
+ file_violations,
369
+ function_violations,
370
+ architecture_violations,
371
+ circular_violations,
372
+ db_access_violations,
373
+ source_analysis,
374
+ score
375
+ )
376
+ )
377
+
378
+ print(output)
379
+
380
+ return
381
+
382
+ if html_output:
383
+
384
+ HtmlReporter().generate(
385
+
386
+ file_violations,
387
+ function_violations,
388
+ architecture_violations,
389
+ circular_violations,
390
+ db_access_violations,
391
+ source_analysis,
392
+ score
393
+ )
394
+
395
+ console.print(
396
+ "[green]✓ report.html generated[/green]"
397
+ )
398
+
399
+ return
400
+
401
+ if markdown_output:
402
+
403
+ MarkdownReporter().generate(
404
+
405
+ file_violations,
406
+ function_violations,
407
+ architecture_violations,
408
+ circular_violations,
409
+ db_access_violations,
410
+ source_analysis,
411
+ score
412
+ )
413
+
414
+ console.print(
415
+ "[green]✓ report.md generated[/green]"
416
+ )
417
+
418
+ return
419
+
420
+
421
+ if score_only:
422
+
423
+ score_color = get_score_color(
424
+ score
425
+ )
426
+
427
+ console.print(
428
+ f"[bold {score_color}]"
429
+ f"{score}/100"
430
+ f"[/bold {score_color}]"
431
+ )
432
+
433
+ return
434
+ if statistics:
435
+
436
+ StatisticsReporter().show_statistics(
437
+ source_analysis,
438
+ file_violations,
439
+ function_violations,
440
+ architecture_violations,
441
+ circular_violations,
442
+ db_access_violations,
443
+ score
444
+ )
445
+ return
446
+
447
+ reporter.show_dashboard(
448
+ source_analysis,
449
+ file_violations,
450
+ function_violations,
451
+ architecture_violations,
452
+ circular_violations,
453
+ db_access_violations,
454
+ score
455
+ )
456
+
457
+ console.print()
458
+
459
+ if file_violations:
460
+
461
+ reporter.show_file_violations(
462
+ file_violations
463
+ )
464
+
465
+ else:
466
+
467
+ console.print(
468
+ "[green]✓ No oversized files found[/green]"
469
+ )
470
+ if function_violations:
471
+
472
+ reporter.show_function_violations(
473
+ function_violations
474
+ )
475
+
476
+ else:
477
+
478
+ console.print(
479
+ "[green]✓ No oversized functions found[/green]"
480
+ )
481
+
482
+ if architecture_violations:
483
+
484
+ reporter.show_architecture_violations(
485
+ architecture_violations
486
+ )
487
+
488
+ else:
489
+
490
+ console.print(
491
+ "[green]✓ No architecture violations found[/green]"
492
+ )
493
+
494
+ if circular_violations:
495
+
496
+ reporter.show_circular_dependencies(
497
+ circular_violations
498
+ )
499
+
500
+ else:
501
+
502
+ console.print(
503
+ "[green]✓ No circular dependencies found[/green]"
504
+ )
505
+
506
+
507
+
508
+ if db_access_violations:
509
+
510
+ reporter.show_db_access_violations(
511
+ db_access_violations
512
+ )
513
+
514
+ else:
515
+
516
+ console.print(
517
+ "[green]✓ No direct database access found[/green]"
518
+ )
519
+
520
+ if source_analysis:
521
+
522
+ reporter.show_source_analysis(
523
+ source_analysis,
524
+ details
525
+ )
526
+
527
+ score_color = get_score_color(score)
528
+
529
+ console.print()
530
+
531
+
532
+
533
+ console.print(
534
+ Panel.fit(
535
+ f"[bold {score_color}]"
536
+ f"Architecture Score: "
537
+ f"{score}/100"
538
+ f"[/bold {score_color}]",
539
+ title="Health Report"
540
+ )
541
+ )
542
+
543
+ critical_found = (
544
+ len(architecture_violations) > 0
545
+ or len(circular_violations) > 0
546
+ )
547
+
548
+ if critical_found:
549
+ raise typer.Exit(code=1)
550
+
551
+
552
+ console.print(
553
+ "[bold green]✓ Scan completed successfully[/bold green]"
554
+ )
555
+
556
+
557
+
558
+
559
+
560
+ @app.command()
561
+ def report(
562
+
563
+ path: str = ".",
564
+
565
+ max_file_lines: int | None = typer.Option(
566
+ None,
567
+ "--max-file-lines"
568
+ ),
569
+
570
+ max_function_lines: int | None = typer.Option(
571
+ None,
572
+ "--max-function-lines"
573
+ )
574
+ ):
575
+
576
+ reporter = ConsoleReporter()
577
+
578
+ file_violations = (
579
+ FileAnalyzer(
580
+ max_file_lines
581
+ ).analyze(path)
582
+ )
583
+
584
+ function_violations = (
585
+ FunctionAnalyzer(
586
+ max_function_lines
587
+ ).analyze_project(path)
588
+ )
589
+
590
+ dependencies = (
591
+ DependencyAnalyzer()
592
+ .analyze_project(path)
593
+ )
594
+ db_access_violations = (
595
+ DBAccessAnalyzer()
596
+ .analyze(dependencies)
597
+ )
598
+
599
+ architecture_violations = (
600
+ ArchitectureValidator()
601
+ .validate(dependencies)
602
+ )
603
+
604
+ circular_violations = (
605
+ CircularDependencyAnalyzer()
606
+ .detect(dependencies)
607
+ )
608
+
609
+
610
+ score = (
611
+ ArchitectureScoreCalculator()
612
+ .calculate(file_violations,function_violations,architecture_violations,circular_violations,db_access_violations
613
+ )
614
+ )
615
+
616
+ total_files = count_python_files(path)
617
+
618
+ reporter.show_summary(total_files,file_violations,function_violations,architecture_violations,circular_violations,db_access_violations,score
619
+ )
File without changes
@@ -0,0 +1,76 @@
1
+ import json
2
+ import os
3
+
4
+
5
+ class ConfigLoader:
6
+
7
+ DEFAULT_CONFIG = {
8
+
9
+ "max_file_lines": 300,
10
+
11
+ "max_function_lines": 50,
12
+ "supported_extensions": [
13
+ ".py",
14
+ ".ts",
15
+ ".js"
16
+ ],
17
+
18
+ "ignored_directories": [
19
+ "venv",
20
+ ".git",
21
+ "__pycache__",
22
+ ".pytest_cache"
23
+ ],
24
+
25
+ "forbidden_dependencies": [
26
+ [
27
+ "controllers",
28
+ "repositories"
29
+ ]
30
+ ],
31
+
32
+ "enable_circular_dependency_check": True,
33
+
34
+ "architecture_score": {
35
+
36
+ "file_penalty": 10,
37
+
38
+ "function_penalty": 5,
39
+
40
+ "architecture_penalty": 20,
41
+
42
+ "circular_dependency_penalty": 30
43
+ }
44
+ }
45
+
46
+ @classmethod
47
+ def load(
48
+ cls,
49
+ config_path="codeguardian.json"
50
+ ):
51
+
52
+ if not os.path.exists(config_path):
53
+
54
+ print(
55
+ "⚠ codeguardian.json not found."
56
+ )
57
+
58
+ print(
59
+ "Using default configuration."
60
+ )
61
+
62
+ return cls.DEFAULT_CONFIG
63
+
64
+ with open(
65
+ config_path,
66
+ "r",
67
+ encoding="utf-8"
68
+ ) as file:
69
+
70
+ user_config = json.load(file)
71
+
72
+ config = cls.DEFAULT_CONFIG.copy()
73
+
74
+ config.update(user_config)
75
+
76
+ return config
@@ -0,0 +1,2 @@
1
+ MAX_FILE_LINES = 300
2
+ MAX_FUNCTION_LINES = 50
CODEGUARDIAN/main.py ADDED
@@ -0,0 +1,4 @@
1
+ from CODEGUARDIAN.cli.commands import app
2
+
3
+ if __name__ == "__main__":
4
+ app()
File without changes