Add CSS validation and new checkbox toggle slider styles; update validation tools documentation
This commit is contained in:
@@ -45,6 +45,7 @@ class EntityValidator:
|
||||
self.custom_path = self.base_path / "custom" / "Espo" / "Custom" / "Resources"
|
||||
self.metadata_path = self.custom_path / "metadata"
|
||||
self.i18n_path = self.custom_path / "i18n"
|
||||
self.client_custom_path = self.base_path / "client" / "custom"
|
||||
self.errors = []
|
||||
self.warnings = []
|
||||
self.entity_defs = {}
|
||||
@@ -379,9 +380,262 @@ class EntityValidator:
|
||||
print_warning(f"Konnte Dateirechte nicht prüfen: {e}")
|
||||
return True
|
||||
|
||||
def validate_css_files(self) -> bool:
|
||||
"""Validiere CSS-Dateien mit csslint."""
|
||||
print_header("7. CSS-VALIDIERUNG")
|
||||
|
||||
css_files = []
|
||||
if self.client_custom_path.exists():
|
||||
css_files = list((self.client_custom_path / "css").rglob("*.css")) if (self.client_custom_path / "css").exists() else []
|
||||
|
||||
if not css_files:
|
||||
print_info("Keine CSS-Dateien gefunden")
|
||||
return True
|
||||
|
||||
# Prüfe ob csslint verfügbar ist
|
||||
try:
|
||||
subprocess.run(['csslint', '--version'], capture_output=True, timeout=5)
|
||||
use_csslint = True
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
use_csslint = False
|
||||
print_warning("csslint nicht gefunden, verwende Basis-Validierung")
|
||||
|
||||
invalid_files = []
|
||||
for css_file in css_files:
|
||||
if use_csslint:
|
||||
# Verwende csslint für professionelle Validierung
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['csslint', '--format=compact', '--quiet', str(css_file)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result.returncode != 0 and result.stdout.strip():
|
||||
# Parse csslint Ausgabe
|
||||
errors = []
|
||||
for line in result.stdout.strip().split('\n'):
|
||||
if 'Error' in line or 'Warning' in line:
|
||||
# Extrahiere nur die Fehlermeldung ohne Pfad
|
||||
parts = line.split(': ', 2)
|
||||
if len(parts) >= 3:
|
||||
errors.append(parts[2])
|
||||
else:
|
||||
errors.append(line)
|
||||
|
||||
if errors:
|
||||
# Nur echte Fehler, keine Warnungen als kritisch behandeln
|
||||
if any('Error' in err for err in errors):
|
||||
self.errors.append(f"CSS-Fehler in {css_file.relative_to(self.base_path)}")
|
||||
invalid_files.append((str(css_file.relative_to(self.base_path)), errors))
|
||||
else:
|
||||
# Nur Warnungen
|
||||
for err in errors:
|
||||
self.warnings.append(f"{css_file.relative_to(self.base_path)}: {err}")
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
self.warnings.append(f"CSS-Validierung timeout für {css_file.relative_to(self.base_path)}")
|
||||
except Exception as e:
|
||||
self.warnings.append(f"Konnte {css_file.relative_to(self.base_path)} nicht validieren: {e}")
|
||||
else:
|
||||
# Fallback: Basis-Validierung
|
||||
try:
|
||||
with open(css_file, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
errors = []
|
||||
|
||||
# Geschlossene Klammern
|
||||
open_braces = content.count('{')
|
||||
close_braces = content.count('}')
|
||||
if open_braces != close_braces:
|
||||
errors.append(f"Ungleiche Klammern: {open_braces} {{ vs {close_braces} }}")
|
||||
|
||||
if errors:
|
||||
self.errors.append(f"CSS-Fehler in {css_file.relative_to(self.base_path)}")
|
||||
invalid_files.append((str(css_file.relative_to(self.base_path)), errors))
|
||||
|
||||
except Exception as e:
|
||||
self.errors.append(f"Konnte {css_file.relative_to(self.base_path)} nicht lesen: {e}")
|
||||
invalid_files.append((str(css_file.relative_to(self.base_path)), [str(e)]))
|
||||
|
||||
if invalid_files:
|
||||
print_error(f"{len(invalid_files)} CSS-Datei(en) mit Fehlern:")
|
||||
for filepath, errors in invalid_files:
|
||||
print(f" {Colors.RED}•{Colors.END} {filepath}")
|
||||
for err in errors[:5]: # Maximal 5 Fehler pro Datei anzeigen
|
||||
print(f" {Colors.RED}→{Colors.END} {err}")
|
||||
if len(errors) > 5:
|
||||
print(f" {Colors.RED}...{Colors.END} und {len(errors) - 5} weitere Fehler")
|
||||
return False
|
||||
else:
|
||||
print_success(f"Alle {len(css_files)} CSS-Dateien sind syntaktisch korrekt")
|
||||
return True
|
||||
|
||||
def validate_js_files(self) -> bool:
|
||||
"""Validiere JavaScript-Dateien mit jshint."""
|
||||
print_header("8. JAVASCRIPT-VALIDIERUNG")
|
||||
|
||||
js_files = []
|
||||
if self.client_custom_path.exists():
|
||||
src_path = self.client_custom_path / "src"
|
||||
js_files = list(src_path.rglob("*.js")) if src_path.exists() else []
|
||||
|
||||
if not js_files:
|
||||
print_info("Keine JavaScript-Dateien gefunden")
|
||||
return True
|
||||
|
||||
# Prüfe ob jshint verfügbar ist
|
||||
try:
|
||||
subprocess.run(['jshint', '--version'], capture_output=True, timeout=5)
|
||||
use_jshint = True
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
use_jshint = False
|
||||
print_warning("jshint nicht gefunden, verwende Basis-Validierung")
|
||||
|
||||
invalid_files = []
|
||||
for js_file in js_files:
|
||||
if use_jshint:
|
||||
# Verwende jshint für professionelle Validierung
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['jshint', '--config=/dev/null', str(js_file)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result.returncode != 0 and result.stdout.strip():
|
||||
errors = []
|
||||
for line in result.stdout.strip().split('\n'):
|
||||
if line and not line.startswith('Lint'):
|
||||
# Parse jshint Ausgabe
|
||||
errors.append(line)
|
||||
|
||||
if errors:
|
||||
self.errors.append(f"JavaScript-Fehler in {js_file.relative_to(self.base_path)}")
|
||||
invalid_files.append((str(js_file.relative_to(self.base_path)), errors))
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
self.warnings.append(f"JavaScript-Validierung timeout für {js_file.relative_to(self.base_path)}")
|
||||
except Exception as e:
|
||||
self.warnings.append(f"Konnte {js_file.relative_to(self.base_path)} nicht validieren: {e}")
|
||||
else:
|
||||
# Fallback: Basis-Validierung
|
||||
try:
|
||||
with open(js_file, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
errors = []
|
||||
|
||||
# Geschlossene Klammern
|
||||
open_paren = content.count('(')
|
||||
close_paren = content.count(')')
|
||||
if open_paren != close_paren:
|
||||
errors.append(f"Ungleiche runde Klammern: {open_paren} ( vs {close_paren} )")
|
||||
|
||||
open_braces = content.count('{')
|
||||
close_braces = content.count('}')
|
||||
if open_braces != close_braces:
|
||||
errors.append(f"Ungleiche geschweifte Klammern: {open_braces} {{ vs {close_braces} }}")
|
||||
|
||||
open_brackets = content.count('[')
|
||||
close_brackets = content.count(']')
|
||||
if open_brackets != close_brackets:
|
||||
errors.append(f"Ungleiche eckige Klammern: {open_brackets} [ vs {close_brackets} ]")
|
||||
|
||||
if errors:
|
||||
self.errors.append(f"JavaScript-Fehler in {js_file.relative_to(self.base_path)}")
|
||||
invalid_files.append((str(js_file.relative_to(self.base_path)), errors))
|
||||
|
||||
except Exception as e:
|
||||
self.errors.append(f"Konnte {js_file.relative_to(self.base_path)} nicht lesen: {e}")
|
||||
invalid_files.append((str(js_file.relative_to(self.base_path)), [str(e)]))
|
||||
|
||||
if invalid_files:
|
||||
print_error(f"{len(invalid_files)} JavaScript-Datei(en) mit Fehlern:")
|
||||
for filepath, errors in invalid_files:
|
||||
print(f" {Colors.RED}•{Colors.END} {filepath}")
|
||||
for err in errors[:5]: # Maximal 5 Fehler pro Datei
|
||||
print(f" {Colors.RED}→{Colors.END} {err}")
|
||||
if len(errors) > 5:
|
||||
print(f" {Colors.RED}...{Colors.END} und {len(errors) - 5} weitere Fehler")
|
||||
return False
|
||||
else:
|
||||
print_success(f"Alle {len(js_files)} JavaScript-Dateien sind syntaktisch korrekt")
|
||||
return True
|
||||
|
||||
def validate_php_files(self) -> bool:
|
||||
"""Validiere PHP-Dateien mit php -l (Lint)."""
|
||||
print_header("9. PHP-VALIDIERUNG")
|
||||
|
||||
php_files = []
|
||||
custom_espo_path = self.base_path / "custom" / "Espo"
|
||||
if custom_espo_path.exists():
|
||||
php_files = list(custom_espo_path.rglob("*.php"))
|
||||
|
||||
if not php_files:
|
||||
print_info("Keine PHP-Dateien gefunden")
|
||||
return True
|
||||
|
||||
# Prüfe ob php verfügbar ist
|
||||
try:
|
||||
subprocess.run(['php', '--version'], capture_output=True, timeout=5)
|
||||
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||
print_warning("PHP-CLI nicht gefunden, überspringe PHP-Validierung")
|
||||
return True
|
||||
|
||||
invalid_files = []
|
||||
for php_file in php_files:
|
||||
try:
|
||||
# Verwende php -l für Syntax-Check
|
||||
result = subprocess.run(
|
||||
['php', '-l', str(php_file)],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
error_lines = []
|
||||
output = result.stderr.strip() or result.stdout.strip()
|
||||
|
||||
for line in output.split('\n'):
|
||||
# Filtere die relevanten Fehlerzeilen
|
||||
if line and not line.startswith('No syntax errors'):
|
||||
# Entferne Datei-Pfad aus Fehlermeldung für bessere Lesbarkeit
|
||||
clean_line = re.sub(r'^.*?in\s+.*?on\s+', '', line)
|
||||
if clean_line != line: # Wenn Ersetzung stattfand
|
||||
error_lines.append(clean_line)
|
||||
else:
|
||||
error_lines.append(line)
|
||||
|
||||
if error_lines:
|
||||
self.errors.append(f"PHP-Syntax-Fehler in {php_file.relative_to(self.base_path)}")
|
||||
invalid_files.append((str(php_file.relative_to(self.base_path)), error_lines))
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
self.warnings.append(f"PHP-Validierung timeout für {php_file.relative_to(self.base_path)}")
|
||||
except Exception as e:
|
||||
self.warnings.append(f"Konnte {php_file.relative_to(self.base_path)} nicht validieren: {e}")
|
||||
|
||||
if invalid_files:
|
||||
print_error(f"{len(invalid_files)} PHP-Datei(en) mit Syntax-Fehlern:")
|
||||
for filepath, errors in invalid_files:
|
||||
print(f" {Colors.RED}•{Colors.END} {filepath}")
|
||||
for err in errors[:3]: # Maximal 3 Fehler pro Datei
|
||||
print(f" {Colors.RED}→{Colors.END} {err}")
|
||||
if len(errors) > 3:
|
||||
print(f" {Colors.RED}...{Colors.END} und {len(errors) - 3} weitere Fehler")
|
||||
return False
|
||||
else:
|
||||
print_success(f"Alle {len(php_files)} PHP-Dateien sind syntaktisch korrekt")
|
||||
return True
|
||||
|
||||
def run_rebuild(self) -> bool:
|
||||
"""Führe den EspoCRM Rebuild aus."""
|
||||
print_header("7. ESPOCRM REBUILD")
|
||||
print_header("10. ESPOCRM REBUILD")
|
||||
|
||||
# Prüfe ob wir in einem Docker-Volume sind
|
||||
is_docker_volume = '/docker/volumes/' in str(self.base_path)
|
||||
@@ -553,6 +807,18 @@ class EntityValidator:
|
||||
# 6. Dateirechte (nicht kritisch für Rebuild)
|
||||
self.check_file_permissions()
|
||||
|
||||
# 7. CSS-Validierung (kritisch)
|
||||
if not self.validate_css_files():
|
||||
all_valid = False
|
||||
|
||||
# 8. JavaScript-Validierung (kritisch)
|
||||
if not self.validate_js_files():
|
||||
all_valid = False
|
||||
|
||||
# 9. PHP-Validierung (kritisch)
|
||||
if not self.validate_php_files():
|
||||
all_valid = False
|
||||
|
||||
return all_valid
|
||||
|
||||
def main():
|
||||
|
||||
Reference in New Issue
Block a user