devdox-sonar 0.0.2__tar.gz → 0.0.4__tar.gz
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.
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/PKG-INFO +59 -68
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/README.md +56 -67
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/pyproject.toml +3 -1
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/cli.py +94 -55
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/config.py +1 -1
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/llm_fixer.py +64 -33
- devdox_sonar-0.0.4/src/devdox_ai_sonar/models/constant_naming.py +36 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/models/file_structures.py +2 -1
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/models/sonar.py +78 -0
- devdox_sonar-0.0.4/src/devdox_ai_sonar/prompts/python/constant_namer_system.j2 +18 -0
- devdox_sonar-0.0.4/src/devdox_ai_sonar/prompts/python/constant_namer_user.j2 +20 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/prompts/python/refactoring/user_prompt.j2 +3 -2
- devdox_sonar-0.0.4/src/devdox_ai_sonar/services/constant_namer.py +416 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/services/extractor.py +17 -7
- devdox_sonar-0.0.4/src/devdox_ai_sonar/services/rule_handler.py +1557 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/sonar_analyzer.py +155 -9
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/file_indentation.py +248 -17
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/function_finder.py +151 -15
- devdox_sonar-0.0.4/src/devdox_ai_sonar/utils/supported_programming_languages.py +241 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_sonar.egg-info/PKG-INFO +59 -68
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_sonar.egg-info/SOURCES.txt +5 -1
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_sonar.egg-info/requires.txt +2 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/tests/test_cli.py +167 -36
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/tests/test_config.py +2 -2
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/tests/test_fix_application.py +1 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/tests/test_llm_fixer.py +4 -4
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/tests/test_sonar_analyzer.py +276 -0
- devdox_sonar-0.0.2/src/devdox_ai_sonar/services/rule_handler.py +0 -749
- devdox_sonar-0.0.2/src/devdox_ai_sonar/utils/file_filter.py +0 -58
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/LICENSE +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/setup.cfg +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/__init__.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/fix_validator.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/models/llm.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/models/llm_config.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/prompts/python/refactoring/system_fix_issues.j2 +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/prompts/python/system_fix_issues.j2 +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/prompts/python/user_prompt.j2 +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/prompts/python/validator.j2 +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/services/configuration.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/services/rule_analyzer.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/sonar_fetcher.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/templates/md.j2 +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/__init__.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/async_file_io.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/constant.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/exceptions.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/provider_config.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/result.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/sonar_config.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/ui.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_ai_sonar/utils/validator.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_sonar.egg-info/dependency_links.txt +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_sonar.egg-info/entry_points.txt +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/src/devdox_sonar.egg-info/top_level.txt +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/tests/test_fix_validator.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/tests/test_integration.py +0 -0
- {devdox_sonar-0.0.2 → devdox_sonar-0.0.4}/tests/test_models.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devdox_sonar
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: A CLI tool to analyze SonarCloud issues and attempt LLM-powered fixes
|
|
5
5
|
Author-email: Hayat Bourji <hayat.bourgi@montyholding.com>
|
|
6
6
|
Maintainer-email: Hayat Bourji <hayat.bourgi@montyholding.com>
|
|
@@ -48,6 +48,8 @@ Requires-Dist: tomlkit==0.13.3
|
|
|
48
48
|
Requires-Dist: inquirer==3.4.1
|
|
49
49
|
Requires-Dist: aiofiles==25.1.0
|
|
50
50
|
Requires-Dist: langchain-core<1.0.0,>=0.3.78
|
|
51
|
+
Requires-Dist: yake>=0.4.8
|
|
52
|
+
Requires-Dist: langdetect>=1.0.9
|
|
51
53
|
Provides-Extra: dev
|
|
52
54
|
Requires-Dist: pytest==8.4.2; extra == "dev"
|
|
53
55
|
Requires-Dist: black==25.11.0; extra == "dev"
|
|
@@ -149,33 +151,18 @@ devdox_sonar
|
|
|
149
151
|
|
|
150
152
|
SonarCloud must have already scanned your project before DevDox AI Sonar can do anything. The tool reads SonarCloud's existing analysis report — it does not perform its own code analysis.
|
|
151
153
|
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
A -->|"analysis\nreport"| B --> C --> D --> E --> F --> G --> H
|
|
166
|
-
H -->|"apply = 1"| I
|
|
167
|
-
H -->|"apply = 0"| J
|
|
168
|
-
|
|
169
|
-
style A fill:#4a90d9,stroke:#2c6faa,color:#fff
|
|
170
|
-
style B fill:#7b68ee,stroke:#5b48ce,color:#fff
|
|
171
|
-
style C fill:#7b68ee,stroke:#5b48ce,color:#fff
|
|
172
|
-
style D fill:#7b68ee,stroke:#5b48ce,color:#fff
|
|
173
|
-
style E fill:#7b68ee,stroke:#5b48ce,color:#fff
|
|
174
|
-
style F fill:#f5a623,stroke:#d4891c,color:#fff
|
|
175
|
-
style G fill:#e67e22,stroke:#c96e1c,color:#fff
|
|
176
|
-
style H fill:#e8e8e8,stroke:#999,color:#333
|
|
177
|
-
style I fill:#50c878,stroke:#3da85e,color:#fff
|
|
178
|
-
style J fill:#ccc,stroke:#999,color:#666
|
|
154
|
+
```
|
|
155
|
+
SonarCloud Fetch Issues Clone Repo Extract Code Build Prompt
|
|
156
|
+
(already scanned) ──► from report ────► to /tmp ────────► + Context ────────► (Jinja2)
|
|
157
|
+
│
|
|
158
|
+
▼
|
|
159
|
+
┌── Apply + Changelog ◄── YES ── Preview ◄── Validate ◄── Call LLM
|
|
160
|
+
│ │
|
|
161
|
+
│ NO
|
|
162
|
+
│ │
|
|
163
|
+
│ ▼
|
|
164
|
+
│ Skip
|
|
165
|
+
▼
|
|
179
166
|
```
|
|
180
167
|
|
|
181
168
|
1. **Fetch** — Authenticates with SonarCloud and reads the analysis report via the Issues API. Filters by the types and severities you configured. Regular issues (bugs, code smells) are grouped **by rule** so all issues of the same kind are batched together. Security issues are grouped **by file**.
|
|
@@ -463,45 +450,49 @@ These options can be passed with any direct mode command to override your saved
|
|
|
463
450
|
|
|
464
451
|
### The fix_issues Pipeline
|
|
465
452
|
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
453
|
+
```
|
|
454
|
+
Load Config (auth.json + config.toml)
|
|
455
|
+
│
|
|
456
|
+
▼
|
|
457
|
+
Clone Repo (git clone to /tmp)
|
|
458
|
+
│
|
|
459
|
+
▼
|
|
460
|
+
Fetch Issues from SonarCloud report
|
|
461
|
+
Filter by type / severity
|
|
462
|
+
│
|
|
463
|
+
▼
|
|
464
|
+
Group by Rule
|
|
465
|
+
│
|
|
466
|
+
▼
|
|
467
|
+
┌────────────────────────────────────────┐
|
|
468
|
+
│ For Each Rule Group │
|
|
469
|
+
│ │
|
|
470
|
+
│ Extract Code (locate lines + context) │
|
|
471
|
+
│ │ │
|
|
472
|
+
│ ▼ │
|
|
473
|
+
│ Select Handler │
|
|
474
|
+
│ │ │
|
|
475
|
+
│ ▼ │
|
|
476
|
+
│ Generate Fix (LLM or AST-based) │
|
|
477
|
+
│ │ │
|
|
478
|
+
│ ▼ │
|
|
479
|
+
│ Preview (file, confidence, explain) │
|
|
480
|
+
│ / \ │
|
|
481
|
+
│ apply=1 apply=0 │
|
|
482
|
+
│ │ │ │
|
|
483
|
+
│ ▼ ▼ │
|
|
484
|
+
│ Apply + Skip │
|
|
485
|
+
│ Validate │ │
|
|
486
|
+
│ │ │ │
|
|
487
|
+
│ └─────┬─────┘ │
|
|
488
|
+
│ ▼ │
|
|
489
|
+
│ Continue to next issue? ─► YES ─┐ │
|
|
490
|
+
│ │ │ │
|
|
491
|
+
│ NO (loop)│ │
|
|
492
|
+
│ │ │ │
|
|
493
|
+
└────────────┼────────────────────────┘ │
|
|
494
|
+
▼ │
|
|
495
|
+
Write Changelog (CHANGES_REGULAR_*.md) │
|
|
505
496
|
```
|
|
506
497
|
|
|
507
498
|
**Specialized rule handlers** — Most rules go through the LLM via `DefaultRuleHandler`. Two rules have dedicated handlers:
|
|
@@ -719,7 +710,7 @@ This project is licensed under the [Apache License 2.0](LICENSE). You are free t
|
|
|
719
710
|
|
|
720
711
|
## Authors
|
|
721
712
|
|
|
722
|
-
Created and maintained by **Hayat Bourji** (hayat.bourgi@montyholding.com) at [Monty Mobile](https://github.com/montymobile1).
|
|
713
|
+
Created and maintained by **Hayat Bourji** (hayat.bourgi@montyholding.com) and **Mohammad Jaafar** (mohamadali.jaafar@montymobile.com) at [Monty Mobile](https://github.com/montymobile1).
|
|
723
714
|
|
|
724
715
|
## Acknowledgments
|
|
725
716
|
|
|
@@ -84,33 +84,18 @@ devdox_sonar
|
|
|
84
84
|
|
|
85
85
|
SonarCloud must have already scanned your project before DevDox AI Sonar can do anything. The tool reads SonarCloud's existing analysis report — it does not perform its own code analysis.
|
|
86
86
|
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
A -->|"analysis\nreport"| B --> C --> D --> E --> F --> G --> H
|
|
101
|
-
H -->|"apply = 1"| I
|
|
102
|
-
H -->|"apply = 0"| J
|
|
103
|
-
|
|
104
|
-
style A fill:#4a90d9,stroke:#2c6faa,color:#fff
|
|
105
|
-
style B fill:#7b68ee,stroke:#5b48ce,color:#fff
|
|
106
|
-
style C fill:#7b68ee,stroke:#5b48ce,color:#fff
|
|
107
|
-
style D fill:#7b68ee,stroke:#5b48ce,color:#fff
|
|
108
|
-
style E fill:#7b68ee,stroke:#5b48ce,color:#fff
|
|
109
|
-
style F fill:#f5a623,stroke:#d4891c,color:#fff
|
|
110
|
-
style G fill:#e67e22,stroke:#c96e1c,color:#fff
|
|
111
|
-
style H fill:#e8e8e8,stroke:#999,color:#333
|
|
112
|
-
style I fill:#50c878,stroke:#3da85e,color:#fff
|
|
113
|
-
style J fill:#ccc,stroke:#999,color:#666
|
|
87
|
+
```
|
|
88
|
+
SonarCloud Fetch Issues Clone Repo Extract Code Build Prompt
|
|
89
|
+
(already scanned) ──► from report ────► to /tmp ────────► + Context ────────► (Jinja2)
|
|
90
|
+
│
|
|
91
|
+
▼
|
|
92
|
+
┌── Apply + Changelog ◄── YES ── Preview ◄── Validate ◄── Call LLM
|
|
93
|
+
│ │
|
|
94
|
+
│ NO
|
|
95
|
+
│ │
|
|
96
|
+
│ ▼
|
|
97
|
+
│ Skip
|
|
98
|
+
▼
|
|
114
99
|
```
|
|
115
100
|
|
|
116
101
|
1. **Fetch** — Authenticates with SonarCloud and reads the analysis report via the Issues API. Filters by the types and severities you configured. Regular issues (bugs, code smells) are grouped **by rule** so all issues of the same kind are batched together. Security issues are grouped **by file**.
|
|
@@ -398,45 +383,49 @@ These options can be passed with any direct mode command to override your saved
|
|
|
398
383
|
|
|
399
384
|
### The fix_issues Pipeline
|
|
400
385
|
|
|
401
|
-
```
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
386
|
+
```
|
|
387
|
+
Load Config (auth.json + config.toml)
|
|
388
|
+
│
|
|
389
|
+
▼
|
|
390
|
+
Clone Repo (git clone to /tmp)
|
|
391
|
+
│
|
|
392
|
+
▼
|
|
393
|
+
Fetch Issues from SonarCloud report
|
|
394
|
+
Filter by type / severity
|
|
395
|
+
│
|
|
396
|
+
▼
|
|
397
|
+
Group by Rule
|
|
398
|
+
│
|
|
399
|
+
▼
|
|
400
|
+
┌────────────────────────────────────────┐
|
|
401
|
+
│ For Each Rule Group │
|
|
402
|
+
│ │
|
|
403
|
+
│ Extract Code (locate lines + context) │
|
|
404
|
+
│ │ │
|
|
405
|
+
│ ▼ │
|
|
406
|
+
│ Select Handler │
|
|
407
|
+
│ │ │
|
|
408
|
+
│ ▼ │
|
|
409
|
+
│ Generate Fix (LLM or AST-based) │
|
|
410
|
+
│ │ │
|
|
411
|
+
│ ▼ │
|
|
412
|
+
│ Preview (file, confidence, explain) │
|
|
413
|
+
│ / \ │
|
|
414
|
+
│ apply=1 apply=0 │
|
|
415
|
+
│ │ │ │
|
|
416
|
+
│ ▼ ▼ │
|
|
417
|
+
│ Apply + Skip │
|
|
418
|
+
│ Validate │ │
|
|
419
|
+
│ │ │ │
|
|
420
|
+
│ └─────┬─────┘ │
|
|
421
|
+
│ ▼ │
|
|
422
|
+
│ Continue to next issue? ─► YES ─┐ │
|
|
423
|
+
│ │ │ │
|
|
424
|
+
│ NO (loop)│ │
|
|
425
|
+
│ │ │ │
|
|
426
|
+
└────────────┼────────────────────────┘ │
|
|
427
|
+
▼ │
|
|
428
|
+
Write Changelog (CHANGES_REGULAR_*.md) │
|
|
440
429
|
```
|
|
441
430
|
|
|
442
431
|
**Specialized rule handlers** — Most rules go through the LLM via `DefaultRuleHandler`. Two rules have dedicated handlers:
|
|
@@ -654,7 +643,7 @@ This project is licensed under the [Apache License 2.0](LICENSE). You are free t
|
|
|
654
643
|
|
|
655
644
|
## Authors
|
|
656
645
|
|
|
657
|
-
Created and maintained by **Hayat Bourji** (hayat.bourgi@montyholding.com) at [Monty Mobile](https://github.com/montymobile1).
|
|
646
|
+
Created and maintained by **Hayat Bourji** (hayat.bourgi@montyholding.com) and **Mohammad Jaafar** (mohamadali.jaafar@montymobile.com) at [Monty Mobile](https://github.com/montymobile1).
|
|
658
647
|
|
|
659
648
|
## Acknowledgments
|
|
660
649
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "devdox_sonar"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.4"
|
|
8
8
|
description = "A CLI tool to analyze SonarCloud issues and attempt LLM-powered fixes"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -58,6 +58,8 @@ dependencies = [
|
|
|
58
58
|
"inquirer==3.4.1",
|
|
59
59
|
"aiofiles==25.1.0",
|
|
60
60
|
"langchain-core>=0.3.78,<1.0.0", # Satisfy langchain-openai requirements
|
|
61
|
+
"yake>=0.4.8",
|
|
62
|
+
"langdetect>=1.0.9",
|
|
61
63
|
]
|
|
62
64
|
|
|
63
65
|
[project.optional-dependencies]
|
|
@@ -27,9 +27,9 @@ from devdox_ai_sonar.llm_fixer import LLMFixer
|
|
|
27
27
|
from devdox_ai_sonar.models.llm_config import ConfigManager
|
|
28
28
|
from devdox_ai_sonar.models.llm import ProviderType
|
|
29
29
|
from devdox_ai_sonar.utils.file_indentation import (
|
|
30
|
-
remove_tmp_files,
|
|
31
30
|
download_latest_version,
|
|
32
|
-
|
|
31
|
+
TmpCloneManager,
|
|
32
|
+
sweep_orphaned_tmp_dirs,
|
|
33
33
|
)
|
|
34
34
|
from devdox_ai_sonar.utils.validator import InputValidator, IssueType
|
|
35
35
|
from devdox_ai_sonar.utils.sonar_config import SonarCloudConfigUI
|
|
@@ -47,7 +47,11 @@ from devdox_ai_sonar.utils.provider_config import (
|
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
from devdox_ai_sonar.utils.exceptions import SwitchCommandException
|
|
50
|
-
from devdox_ai_sonar.utils.
|
|
50
|
+
from devdox_ai_sonar.utils.supported_programming_languages import (
|
|
51
|
+
PYTHON,
|
|
52
|
+
IPYTHON,
|
|
53
|
+
is_file_processable,
|
|
54
|
+
)
|
|
51
55
|
from devdox_ai_sonar.utils.ui import smart_prompt, smart_confirm
|
|
52
56
|
from devdox_ai_sonar.utils import constant
|
|
53
57
|
from devdox_ai_sonar.config import settings
|
|
@@ -57,6 +61,18 @@ EXCLUDE_RULE_CONFIG_FIELD = "configuration.exclude_rules"
|
|
|
57
61
|
console = Console()
|
|
58
62
|
|
|
59
63
|
|
|
64
|
+
def _should_skip_file(file_path: Optional[str]) -> bool:
|
|
65
|
+
"""Return True if *file_path* should be skipped during processing."""
|
|
66
|
+
return bool(
|
|
67
|
+
file_path
|
|
68
|
+
and not is_file_processable(
|
|
69
|
+
file_path,
|
|
70
|
+
allowed_suffixes=PYTHON.file_extensions | IPYTHON.file_extensions,
|
|
71
|
+
excluded_prefixes={"test_"},
|
|
72
|
+
)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
60
76
|
def async_command(f: Any) -> Any:
|
|
61
77
|
"""Decorator to run async functions with Click."""
|
|
62
78
|
|
|
@@ -750,7 +766,10 @@ async def main( # ← Async main
|
|
|
750
766
|
"pull_request": pull_request,
|
|
751
767
|
"excluded_rules": excluded_rules,
|
|
752
768
|
}
|
|
753
|
-
|
|
769
|
+
|
|
770
|
+
# Sweep orphaned temp directories from previous crashed runs
|
|
771
|
+
sweep_orphaned_tmp_dirs(on_status=lambda msg: console.print(f"[dim]{msg}[/dim]"))
|
|
772
|
+
|
|
754
773
|
# If command specified, run it directly
|
|
755
774
|
if command:
|
|
756
775
|
await _execute_command_async(ctx, command)
|
|
@@ -1088,6 +1107,7 @@ async def _run_fix_issues(**kwargs: Any) -> None:
|
|
|
1088
1107
|
issue_type=IssueType.REGULAR,
|
|
1089
1108
|
download_latest=True,
|
|
1090
1109
|
system_ask=True,
|
|
1110
|
+
check_tmp_path=True,
|
|
1091
1111
|
)
|
|
1092
1112
|
|
|
1093
1113
|
except SwitchCommandException:
|
|
@@ -1130,6 +1150,7 @@ async def _run_fix_security_issues(**kwargs: Any) -> None:
|
|
|
1130
1150
|
issue_type=IssueType.SECURITY,
|
|
1131
1151
|
download_latest=True,
|
|
1132
1152
|
system_ask=True,
|
|
1153
|
+
check_tmp_path=True,
|
|
1133
1154
|
)
|
|
1134
1155
|
|
|
1135
1156
|
except SwitchCommandException:
|
|
@@ -1292,6 +1313,7 @@ async def fix_multiple(**kwargs: Any) -> None:
|
|
|
1292
1313
|
issue_type=IssueType.REGULAR,
|
|
1293
1314
|
download_latest=False,
|
|
1294
1315
|
system_ask=False,
|
|
1316
|
+
check_tmp_path=False,
|
|
1295
1317
|
)
|
|
1296
1318
|
# security issues
|
|
1297
1319
|
await _process_and_fix_issues(
|
|
@@ -1303,6 +1325,7 @@ async def fix_multiple(**kwargs: Any) -> None:
|
|
|
1303
1325
|
issue_type=IssueType.SECURITY,
|
|
1304
1326
|
download_latest=False,
|
|
1305
1327
|
system_ask=False,
|
|
1328
|
+
check_tmp_path=False,
|
|
1306
1329
|
)
|
|
1307
1330
|
|
|
1308
1331
|
except SwitchCommandException:
|
|
@@ -1419,58 +1442,66 @@ async def _process_and_fix_issues(
|
|
|
1419
1442
|
issue_type: IssueType = IssueType.REGULAR,
|
|
1420
1443
|
download_latest: bool = True,
|
|
1421
1444
|
system_ask: bool = True,
|
|
1445
|
+
check_tmp_path: bool = True,
|
|
1422
1446
|
) -> None:
|
|
1423
1447
|
"""Process and fix issues - Refactored."""
|
|
1424
|
-
|
|
1425
1448
|
services = _initialize_fix_services(auth_config, llm_config)
|
|
1426
1449
|
|
|
1450
|
+
branch_downloaded = branch
|
|
1427
1451
|
if download_latest:
|
|
1428
|
-
tmp_path = generate_tmp_path()
|
|
1429
|
-
branch_downloaded = branch
|
|
1430
1452
|
if pull_request and int(pull_request) > 0:
|
|
1431
1453
|
branch_downloaded = services["analyzer"].get_branch_from_pr(
|
|
1432
1454
|
project_key=auth_config.project, pull_request=pull_request
|
|
1433
1455
|
)
|
|
1434
|
-
|
|
1435
1456
|
if not branch_downloaded:
|
|
1436
1457
|
console.print("[red]Could not determine branch to download[/red]")
|
|
1437
1458
|
raise click.Abort()
|
|
1438
1459
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1460
|
+
# Orphaned temp dirs (from SIGKILL/OOM) are cleaned by
|
|
1461
|
+
# sweep_orphaned_tmp_dirs() at CLI startup.
|
|
1462
|
+
async with TmpCloneManager(
|
|
1463
|
+
on_cleanup=lambda p: console.print(
|
|
1464
|
+
f"[dim]Cleaning up temporary files: {p}[/dim]"
|
|
1442
1465
|
)
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
"
|
|
1455
|
-
|
|
1456
|
-
|
|
1466
|
+
) as tmp_path:
|
|
1467
|
+
if download_latest:
|
|
1468
|
+
console.print(f"Cloning {auth_config.project} to {tmp_path}")
|
|
1469
|
+
downloaded = download_latest_version(
|
|
1470
|
+
auth_config.git_url, str(tmp_path), branch_downloaded
|
|
1471
|
+
)
|
|
1472
|
+
if not downloaded:
|
|
1473
|
+
console.print("Not able to download latest version")
|
|
1474
|
+
raise click.Abort()
|
|
1475
|
+
|
|
1476
|
+
issues = _fetch_issues_by_type(
|
|
1477
|
+
services["analyzer"],
|
|
1478
|
+
auth_config,
|
|
1479
|
+
branch,
|
|
1480
|
+
pull_request,
|
|
1481
|
+
fix_params,
|
|
1482
|
+
issue_type,
|
|
1457
1483
|
)
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1484
|
+
if not issues:
|
|
1485
|
+
msg = (
|
|
1486
|
+
"No fixable security issues found"
|
|
1487
|
+
if issue_type == IssueType.SECURITY
|
|
1488
|
+
else "No fixable issues found"
|
|
1489
|
+
)
|
|
1490
|
+
console.print(f"[yellow]{msg}[/yellow]")
|
|
1491
|
+
return
|
|
1462
1492
|
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1493
|
+
total_issues = sum(len(issue_list) for issue_list in issues.values())
|
|
1494
|
+
console.print(f"\n[green]✓ Found {total_issues} fixable issues[/green]\n")
|
|
1495
|
+
await _process_files_with_issues(
|
|
1496
|
+
issues,
|
|
1497
|
+
services,
|
|
1498
|
+
auth_config,
|
|
1499
|
+
fix_params,
|
|
1500
|
+
issue_type,
|
|
1501
|
+
tmp_path,
|
|
1502
|
+
system_ask=system_ask,
|
|
1503
|
+
check_tmp_path=check_tmp_path,
|
|
1504
|
+
)
|
|
1474
1505
|
|
|
1475
1506
|
|
|
1476
1507
|
def _initialize_fix_services(
|
|
@@ -1498,6 +1529,7 @@ async def _process_files_with_issues(
|
|
|
1498
1529
|
issue_type: IssueType,
|
|
1499
1530
|
tmp_path: Path,
|
|
1500
1531
|
system_ask: bool = True,
|
|
1532
|
+
check_tmp_path: bool = True,
|
|
1501
1533
|
) -> None:
|
|
1502
1534
|
"""
|
|
1503
1535
|
Process files with issues.
|
|
@@ -1511,7 +1543,13 @@ async def _process_files_with_issues(
|
|
|
1511
1543
|
)
|
|
1512
1544
|
if issue_type == IssueType.SECURITY:
|
|
1513
1545
|
await _process_security_issues(
|
|
1514
|
-
issues_by_file,
|
|
1546
|
+
issues_by_file,
|
|
1547
|
+
services,
|
|
1548
|
+
auth_config,
|
|
1549
|
+
fix_params,
|
|
1550
|
+
md_file_path,
|
|
1551
|
+
tmp_path,
|
|
1552
|
+
check_tmp_path,
|
|
1515
1553
|
)
|
|
1516
1554
|
else:
|
|
1517
1555
|
issues_by_rule_nested = {
|
|
@@ -1525,6 +1563,7 @@ async def _process_files_with_issues(
|
|
|
1525
1563
|
md_file_path,
|
|
1526
1564
|
tmp_path,
|
|
1527
1565
|
system_ask,
|
|
1566
|
+
check_tmp_path,
|
|
1528
1567
|
)
|
|
1529
1568
|
|
|
1530
1569
|
|
|
@@ -1536,6 +1575,7 @@ async def _process_regular_issues(
|
|
|
1536
1575
|
md_file_path: Path,
|
|
1537
1576
|
tmp_path: Path,
|
|
1538
1577
|
system_ask: bool = True,
|
|
1578
|
+
check_tmp_path: bool = True,
|
|
1539
1579
|
) -> None:
|
|
1540
1580
|
"""
|
|
1541
1581
|
Process regular issues grouped by rule.
|
|
@@ -1559,6 +1599,7 @@ async def _process_regular_issues(
|
|
|
1559
1599
|
md_file_path,
|
|
1560
1600
|
tmp_path,
|
|
1561
1601
|
system_ask,
|
|
1602
|
+
check_tmp_path,
|
|
1562
1603
|
)
|
|
1563
1604
|
|
|
1564
1605
|
if not success:
|
|
@@ -1574,6 +1615,7 @@ async def _process_issues_for_rule(
|
|
|
1574
1615
|
md_file_path: Path,
|
|
1575
1616
|
tmp_path: Path,
|
|
1576
1617
|
system_ask: bool = True,
|
|
1618
|
+
check_tmp_path: bool = True,
|
|
1577
1619
|
) -> bool:
|
|
1578
1620
|
"""
|
|
1579
1621
|
Process all issues for a specific rule.
|
|
@@ -1586,13 +1628,7 @@ async def _process_issues_for_rule(
|
|
|
1586
1628
|
total_issues = len(issues_list)
|
|
1587
1629
|
|
|
1588
1630
|
for idx, issue in enumerate(issues_list, 1):
|
|
1589
|
-
if issue.file
|
|
1590
|
-
issue.file,
|
|
1591
|
-
allowed_suffixes=[".py"],
|
|
1592
|
-
excluded_suffixes=[],
|
|
1593
|
-
allowed_prefixes=[],
|
|
1594
|
-
excluded_prefixes=["test_"],
|
|
1595
|
-
):
|
|
1631
|
+
if _should_skip_file(issue.file):
|
|
1596
1632
|
console.print(
|
|
1597
1633
|
f"[dim]Skipping {issue.file} "
|
|
1598
1634
|
f"(only .py files excluding test_ are processed)[/dim]"
|
|
@@ -1608,6 +1644,7 @@ async def _process_issues_for_rule(
|
|
|
1608
1644
|
rule_key=rule_key,
|
|
1609
1645
|
md_file_path=md_file_path,
|
|
1610
1646
|
tmp_path=tmp_path,
|
|
1647
|
+
check_tmp_path=check_tmp_path,
|
|
1611
1648
|
)
|
|
1612
1649
|
|
|
1613
1650
|
if not await _should_continue_to_next_issue(
|
|
@@ -1627,6 +1664,7 @@ async def _process_single_fix(
|
|
|
1627
1664
|
rule_key: str,
|
|
1628
1665
|
tmp_path: Path,
|
|
1629
1666
|
md_file_path: Optional[Path] = None,
|
|
1667
|
+
check_tmp_path: bool = True,
|
|
1630
1668
|
) -> None:
|
|
1631
1669
|
"""
|
|
1632
1670
|
Generate and handle a single fix.
|
|
@@ -1639,6 +1677,7 @@ async def _process_single_fix(
|
|
|
1639
1677
|
str(tmp_path),
|
|
1640
1678
|
rule_key,
|
|
1641
1679
|
md_file_path,
|
|
1680
|
+
check_tmp_path=check_tmp_path,
|
|
1642
1681
|
)
|
|
1643
1682
|
|
|
1644
1683
|
if fixes:
|
|
@@ -1655,6 +1694,7 @@ async def _process_security_issues(
|
|
|
1655
1694
|
fix_params: Dict[str, Any],
|
|
1656
1695
|
md_file_path: Path,
|
|
1657
1696
|
tmp_path: Path,
|
|
1697
|
+
check_tmp_path: bool = True,
|
|
1658
1698
|
) -> None:
|
|
1659
1699
|
"""
|
|
1660
1700
|
Process security issues grouped by file.
|
|
@@ -1666,13 +1706,7 @@ async def _process_security_issues(
|
|
|
1666
1706
|
for idx, (file_key, issues) in enumerate(issues_by_file.items(), 1):
|
|
1667
1707
|
console.print(f"\n[blue]Processing ({idx}/{total_files}): {file_key}[/blue]")
|
|
1668
1708
|
for idx_new, issue in enumerate(issues, 1):
|
|
1669
|
-
if issue.file
|
|
1670
|
-
issue.file,
|
|
1671
|
-
allowed_suffixes=[".py"],
|
|
1672
|
-
excluded_suffixes=[],
|
|
1673
|
-
allowed_prefixes=[],
|
|
1674
|
-
excluded_prefixes=["test_"],
|
|
1675
|
-
):
|
|
1709
|
+
if _should_skip_file(issue.file):
|
|
1676
1710
|
console.print(
|
|
1677
1711
|
f"[dim]Skipping {issue.file} "
|
|
1678
1712
|
f"(only .py files excluding test_ are processed)[/dim]"
|
|
@@ -1688,6 +1722,7 @@ async def _process_security_issues(
|
|
|
1688
1722
|
rule_key=file_key,
|
|
1689
1723
|
md_file_path=md_file_path,
|
|
1690
1724
|
tmp_path=tmp_path,
|
|
1725
|
+
check_tmp_path=check_tmp_path,
|
|
1691
1726
|
)
|
|
1692
1727
|
|
|
1693
1728
|
if not await _should_continue_to_next_issue(idx, total_files):
|
|
@@ -1732,6 +1767,7 @@ async def _generate_fix_for_file(
|
|
|
1732
1767
|
tmp_path: str,
|
|
1733
1768
|
rule_name: Optional[str] = None,
|
|
1734
1769
|
md_file_path: Optional[Path] = None,
|
|
1770
|
+
check_tmp_path: bool = True,
|
|
1735
1771
|
) -> Optional[List[FixSuggestion]]:
|
|
1736
1772
|
"""Generate fix for a file."""
|
|
1737
1773
|
with show_progress("Generating fixes...", total=len(issues)) as (progress, task):
|
|
@@ -1746,6 +1782,7 @@ async def _generate_fix_for_file(
|
|
|
1746
1782
|
project_path=Path(str(auth_config.project_path)),
|
|
1747
1783
|
tmp_path=Path(tmp_path),
|
|
1748
1784
|
file_md=file_md_str,
|
|
1785
|
+
check_tmp_path=check_tmp_path,
|
|
1749
1786
|
)
|
|
1750
1787
|
return result
|
|
1751
1788
|
|
|
@@ -1875,6 +1912,7 @@ def _fetch_issues_by_type(
|
|
|
1875
1912
|
branch=branch or "",
|
|
1876
1913
|
pull_request=pr_number,
|
|
1877
1914
|
max_issues=fix_params["max_fixes"],
|
|
1915
|
+
language=PYTHON,
|
|
1878
1916
|
)
|
|
1879
1917
|
else:
|
|
1880
1918
|
with show_progress(constant.FETCHING_ISSUES) as (progress, task):
|
|
@@ -1887,6 +1925,7 @@ def _fetch_issues_by_type(
|
|
|
1887
1925
|
types_list=fix_params["types_list"],
|
|
1888
1926
|
rules_excluded=fix_params["exclude_rules"],
|
|
1889
1927
|
group_by="rules",
|
|
1928
|
+
languages=[PYTHON.sonar_language_key, IPYTHON.sonar_language_key],
|
|
1890
1929
|
)
|
|
1891
1930
|
|
|
1892
1931
|
|