full_scan.py
- Pfad:
/var/www/tools/ki-protokoll/claude-hook/quality/full_scan.py - Namespace: claude-hook.quality
- Zeilen: 321 | Größe: 9,220 Bytes
- Geändert: 2025-12-25 11:05:29 | Gescannt: 2025-12-31 10:22:15
Code Hygiene Score: 77
- Dependencies: 40 (25%)
- LOC: 59 (20%)
- Methods: 100 (20%)
- Secrets: 100 (15%)
- Classes: 100 (10%)
- Magic Numbers: 100 (10%)
Keine Issues gefunden.
Dependencies 11
- use argparse
- use json
- use os
- use sys
- use pathlib.Path
- use typing.Dict
- use typing.List
- use typing.Any
- use pre_rules
- use post_rules
- use task_creator
Funktionen 7
-
find_php_files()Zeile 47 -
scan_file()Zeile 62 -
scan_all()Zeile 93 -
format_console()Zeile 137 -
format_json()Zeile 186 -
create_tasks_for_violations()Zeile 196 -
main()Zeile 262
Code
#!/usr/bin/python3
"""
Full Codebase Quality Scan.
Scannt alle PHP-Dateien gegen Pre- und Post-Hook Regeln.
Erstellt optional Tasks für Violations.
Usage:
full_scan.py # Scan mit Output
full_scan.py --json # JSON-Output
full_scan.py --create-tasks # Tasks für Violations erstellen
full_scan.py --path /src # Nur bestimmten Pfad scannen
"""
import argparse
import json
import os
import sys
from pathlib import Path
from typing import Dict, List, Any
# Eigene Module laden
sys.path.insert(0, str(Path(__file__).parent))
import pre_rules
import post_rules
# =============================================================================
# CONFIGURATION
# =============================================================================
DEFAULT_SCAN_PATH = "/var/www/dev.campus.systemische-tools.de/src"
SKIP_DIRS = [
"/vendor/",
"/node_modules/",
"/.git/",
"/cache/",
"/var/cache/",
"/var/log/",
]
# =============================================================================
# SCANNER
# =============================================================================
def find_php_files(base_path: str) -> List[Path]:
"""Findet alle PHP-Dateien im Pfad."""
base = Path(base_path)
if not base.exists():
return []
files = []
for php_file in base.rglob("*.php"):
file_str = str(php_file)
if not any(skip in file_str for skip in SKIP_DIRS):
files.append(php_file)
return sorted(files)
def scan_file(file_path: Path) -> Dict[str, Any]:
"""Scannt eine einzelne Datei."""
try:
content = file_path.read_text(encoding="utf-8")
except (OSError, UnicodeDecodeError) as e:
return {
"file": str(file_path),
"error": str(e),
"blocks": [],
"warnings": [],
}
file_str = str(file_path)
# Pre-Rules (Blocks)
pre_result = pre_rules.check(file_str, content)
blocks = []
if not pre_result.get("allowed", True):
blocks.append(pre_result.get("message", "Unknown block"))
# Post-Rules (Warnings)
post_result = post_rules.check(file_str, content)
warnings = post_result.get("warnings", [])
return {
"file": file_str,
"blocks": blocks,
"warnings": warnings,
}
def scan_all(base_path: str) -> Dict[str, Any]:
"""Scannt alle PHP-Dateien."""
files = find_php_files(base_path)
results = {
"scan_path": base_path,
"total_files": len(files),
"files_with_blocks": 0,
"files_with_warnings": 0,
"total_blocks": 0,
"total_warnings": 0,
"violations": [],
}
for file_path in files:
file_result = scan_file(file_path)
if file_result.get("error"):
results["violations"].append(file_result)
continue
has_issues = False
if file_result["blocks"]:
results["files_with_blocks"] += 1
results["total_blocks"] += len(file_result["blocks"])
has_issues = True
if file_result["warnings"]:
results["files_with_warnings"] += 1
results["total_warnings"] += len(file_result["warnings"])
has_issues = True
if has_issues:
results["violations"].append(file_result)
return results
# =============================================================================
# OUTPUT FORMATTERS
# =============================================================================
def format_console(results: Dict[str, Any]) -> str:
"""Formatiert Ergebnis für Console-Output."""
lines = []
lines.append("=" * 70)
lines.append("QUALITY SCAN REPORT")
lines.append("=" * 70)
lines.append(f"Scan Path: {results['scan_path']}")
lines.append(f"Total Files: {results['total_files']}")
lines.append("")
lines.append(f"Files with BLOCKS: {results['files_with_blocks']}")
lines.append(f"Files with WARNINGS: {results['files_with_warnings']}")
lines.append(f"Total BLOCKS: {results['total_blocks']}")
lines.append(f"Total WARNINGS: {results['total_warnings']}")
lines.append("")
if results["violations"]:
lines.append("-" * 70)
lines.append("VIOLATIONS:")
lines.append("-" * 70)
for v in results["violations"]:
# Kurzer Pfad
short_path = v["file"].replace("/var/www/dev.campus.systemische-tools.de/", "")
lines.append(f"\n{short_path}")
if v.get("error"):
lines.append(f" ERROR: {v['error']}")
for block in v.get("blocks", []):
lines.append(f" [BLOCK] {block}")
for warning in v.get("warnings", []):
lines.append(f" [WARN] {warning}")
lines.append("")
lines.append("=" * 70)
if results["total_blocks"] > 0:
lines.append("STATUS: FAILED (has blocking violations)")
elif results["total_warnings"] > 0:
lines.append("STATUS: PASSED with warnings")
else:
lines.append("STATUS: PASSED")
lines.append("=" * 70)
return "\n".join(lines)
def format_json(results: Dict[str, Any]) -> str:
"""Formatiert Ergebnis als JSON."""
return json.dumps(results, indent=2, ensure_ascii=False)
# =============================================================================
# TASK CREATION
# =============================================================================
def create_tasks_for_violations(results: Dict[str, Any]) -> List[Dict[str, Any]]:
"""Erstellt Tasks für alle Violations."""
try:
import task_creator
except ImportError:
return [{"error": "task_creator module not found"}]
created = []
for v in results["violations"]:
file_path = v["file"]
for block in v.get("blocks", []):
# Parse Rule-ID aus Message
rule_id = "UNKNOWN"
if "[" in block and "]" in block:
rule_id = block.split("[")[1].split("]")[0]
try:
task_id = task_creator.create_violation_task(
file_path, rule_id, block, "block"
)
created.append({
"file": file_path,
"rule": rule_id,
"type": "block",
"task_id": task_id,
})
except Exception as e:
created.append({
"file": file_path,
"rule": rule_id,
"type": "block",
"error": str(e),
})
for warning in v.get("warnings", []):
# Parse Rule-ID aus Warning
rule_id = "UNKNOWN"
if warning.startswith("W") and ":" in warning:
rule_id = warning.split(":")[0]
try:
task_id = task_creator.create_warning_task(file_path, rule_id, warning)
created.append({
"file": file_path,
"rule": rule_id,
"type": "warning",
"task_id": task_id,
})
except Exception as e:
created.append({
"file": file_path,
"rule": rule_id,
"type": "warning",
"error": str(e),
})
return created
# =============================================================================
# MAIN
# =============================================================================
def main():
parser = argparse.ArgumentParser(description="Full Codebase Quality Scan")
parser.add_argument(
"--path",
default=DEFAULT_SCAN_PATH,
help=f"Path to scan (default: {DEFAULT_SCAN_PATH})",
)
parser.add_argument(
"--json",
action="store_true",
help="Output as JSON",
)
parser.add_argument(
"--create-tasks",
action="store_true",
help="Create tasks for violations",
)
parser.add_argument(
"--blocks-only",
action="store_true",
help="Only report blocking violations",
)
args = parser.parse_args()
# Scan durchführen
results = scan_all(args.path)
# Wenn blocks-only, Warnings entfernen
if args.blocks_only:
for v in results["violations"]:
v["warnings"] = []
results["violations"] = [
v for v in results["violations"] if v["blocks"]
]
results["total_warnings"] = 0
results["files_with_warnings"] = 0
# Tasks erstellen wenn gewünscht
if args.create_tasks:
created_tasks = create_tasks_for_violations(results)
results["created_tasks"] = created_tasks
# Output
if args.json:
print(format_json(results))
else:
print(format_console(results))
if args.create_tasks and results.get("created_tasks"):
print(f"\nCreated {len(results['created_tasks'])} tasks for violations.")
# Exit Code
if results["total_blocks"] > 0:
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
main()