Replace legacy Bash check and rebuild script with a new Python-based validator; add comprehensive validation checks and improve documentation for usage and features.
This commit is contained in:
27
README.md
27
README.md
@@ -44,22 +44,28 @@ Keine integrierte KI-Schnittstelle existiert, aber mit Dateizugriff können auto
|
||||
|
||||
**WICHTIG:** Nach jeder Änderung an Custom-Dateien muss ein Rebuild durchgeführt werden!
|
||||
|
||||
### Check & Rebuild Script (Empfohlen)
|
||||
### Validate & Rebuild Script (Empfohlen)
|
||||
|
||||
**Zentrales Tool:** `custom/scripts/check_and_rebuild.sh`
|
||||
**Zentrales Tool:** `custom/scripts/validate_and_rebuild.py`
|
||||
|
||||
**NEU ab Januar 2026:** Erweitertes Python-basiertes Validierungs-Tool mit automatischen Checks!
|
||||
|
||||
Dieses Script sollte **IMMER** verwendet werden (nicht manueller Rebuild). Es führt automatisch aus:
|
||||
|
||||
✅ **Validierungen:**
|
||||
- JSON-Syntax-Prüfung aller `.json` Dateien im `custom/` Verzeichnis
|
||||
- **Relationship-Konsistenz-Prüfung** (bidirektionale Links, foreign-Definitionen)
|
||||
- **Formula-Script Platzierung** (korrekt in `/formula/` statt `/entityDefs/`)
|
||||
- **i18n-Vollständigkeit** (fehlende Übersetzungen für Links)
|
||||
- Layout-Struktur-Prüfung
|
||||
- Dateirechte-Prüfung (`www-data:www-data` Owner)
|
||||
- System-Checks (Cache-Verzeichnis, Logs-Verzeichnis)
|
||||
|
||||
✅ **Automatische Korrekturen:**
|
||||
- Setzt fehlerhafte Dateirechte auf `www-data:www-data`
|
||||
- Korrigiert Verzeichnis-Permissions (775) und Datei-Permissions (664)
|
||||
|
||||
✅ **Rebuild:**
|
||||
- **Nur wenn keine kritischen Fehler gefunden werden!**
|
||||
- Merged alle Custom-Metadata mit Core-Definitionen
|
||||
- Aktualisiert Datenbank-Schema (neue Felder, Tabellen, Indizes)
|
||||
- Leert Cache-Verzeichnis
|
||||
@@ -68,18 +74,25 @@ Dieses Script sollte **IMMER** verwendet werden (nicht manueller Rebuild). Es f
|
||||
**Verwendung:**
|
||||
```bash
|
||||
# Im EspoCRM-Root-Verzeichnis ausführen
|
||||
./custom/scripts/check_and_rebuild.sh
|
||||
python3 custom/scripts/validate_and_rebuild.py
|
||||
|
||||
# Nur Validierung ohne Rebuild
|
||||
python3 custom/scripts/validate_and_rebuild.py --dry-run
|
||||
```
|
||||
|
||||
**Ausgabe:**
|
||||
- ✓ Grün: Alles in Ordnung, Rebuild erfolgreich
|
||||
- ⚠ Gelb: Warnungen, Rebuild wird trotzdem ausgeführt
|
||||
- ✗ Rot: Fehler (z.B. ungültiges JSON), Rebuild wird NICHT ausgeführt
|
||||
- ⚠ Gelb: Warnungen (z.B. fehlende i18n), Rebuild wird trotzdem ausgeführt
|
||||
- ✗ Rot: Kritische Fehler (z.B. ungültiges JSON, fehlende Relationships), Rebuild wird NICHT ausgeführt
|
||||
|
||||
**Bei Fehlern:**
|
||||
- JSON-Syntax-Fehler werden mit Datei und Zeilennummer angezeigt
|
||||
- Relationship-Fehler zeigen fehlende Links zwischen Entities
|
||||
- Formula-Platzierungsfehler werden erkannt und gemeldet
|
||||
- i18n-Probleme werden als Warnungen angezeigt (kein Abbruch)
|
||||
- Dateirechte-Probleme werden automatisch korrigiert
|
||||
- System-Fehler (fehlende Verzeichnisse) müssen manuell behoben werden
|
||||
|
||||
**Detaillierte Dokumentation:** Siehe `custom/scripts/VALIDATOR_README.md`
|
||||
|
||||
### Wann Rebuild erforderlich?
|
||||
|
||||
|
||||
120
custom/scripts/QUICKSTART.md
Normal file
120
custom/scripts/QUICKSTART.md
Normal file
@@ -0,0 +1,120 @@
|
||||
# 🚀 EspoCRM Validator - Quick Start
|
||||
|
||||
## Installation bereits abgeschlossen ✅
|
||||
|
||||
Das Validierungs-Tool ist bereits installiert und einsatzbereit!
|
||||
|
||||
## Verwendung
|
||||
|
||||
```bash
|
||||
# Im EspoCRM-Root-Verzeichnis
|
||||
python3 custom/scripts/validate_and_rebuild.py
|
||||
```
|
||||
|
||||
Das war's! Das Script führt automatisch alle Checks durch und startet den Rebuild.
|
||||
|
||||
**Tipp:** Für nur Validierung ohne Rebuild: `python3 custom/scripts/validate_and_rebuild.py --dry-run`
|
||||
|
||||
## Was wird geprüft?
|
||||
|
||||
1. ✅ **JSON-Syntax** - Alle Custom-Dateien
|
||||
2. ✅ **Relationships** - Bidirektionale Link-Konsistenz
|
||||
3. ✅ **Formula-Scripts** - Korrekte Platzierung
|
||||
4. ✅ **i18n** - Vollständigkeit der Übersetzungen
|
||||
5. ✅ **Layouts** - Struktur-Validierung
|
||||
6. ✅ **Dateirechte** - Owner & Permissions
|
||||
7. ✅ **Rebuild** - Nur bei Fehlerfreiheit
|
||||
|
||||
## Output verstehen
|
||||
|
||||
| Symbol | Bedeutung | Aktion |
|
||||
|--------|-----------|--------|
|
||||
| ✓ Grün | OK | Keine |
|
||||
| ⚠ Gelb | Warnung | Optional beheben |
|
||||
| ✗ Rot | Fehler | **Muss behoben werden!** |
|
||||
|
||||
## Beispiel
|
||||
|
||||
```bash
|
||||
$ python3 custom/scripts/validate_and_rebuild.py
|
||||
|
||||
EspoCRM Custom Entity Validator & Rebuild Tool
|
||||
Arbeitsverzeichnis: /var/lib/docker/volumes/vmh-espocrm_espocrm/_data
|
||||
|
||||
======================================================================
|
||||
1. JSON-SYNTAX VALIDIERUNG
|
||||
======================================================================
|
||||
|
||||
✓ Alle 547 JSON-Dateien sind syntaktisch korrekt
|
||||
|
||||
======================================================================
|
||||
2. RELATIONSHIP-KONSISTENZ
|
||||
======================================================================
|
||||
|
||||
✗ 4 Relationship-Fehler gefunden:
|
||||
• CMietobjekt.vmhRumungsklages → CVmhRumungsklage:
|
||||
Foreign link 'mietobjekte' fehlt in CVmhRumungsklage
|
||||
...
|
||||
|
||||
✗ REBUILD ABGEBROCHEN: Kritische Fehler müssen behoben werden!
|
||||
```
|
||||
|
||||
## Häufige Fehler beheben
|
||||
|
||||
### Relationship-Fehler
|
||||
```json
|
||||
// In CVmhRumungsklage.json HINZUFÜGEN:
|
||||
{
|
||||
"links": {
|
||||
"mietobjekte": {
|
||||
"type": "hasMany",
|
||||
"entity": "CMietobjekt",
|
||||
"foreign": "vmhRumungsklages"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### JSON-Syntax-Fehler
|
||||
- Mit einem JSON-Validator prüfen (z.B. `python3 -m json.tool datei.json`)
|
||||
- Auf fehlende Kommata, geschweifte Klammern achten
|
||||
|
||||
### i18n-Warnung (optional)
|
||||
```json
|
||||
// In i18n/de_DE/Entity.json ERGÄNZEN:
|
||||
{
|
||||
"links": {
|
||||
"meinLink": "Mein Link"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Mehr Infos
|
||||
|
||||
- **Ausführliche Doku:** `custom/scripts/VALIDATOR_README.md`
|
||||
- **EspoCRM-Doku:** `README.md`
|
||||
- **Custom-Struktur:** `custom/CUSTOM_DIRECTORY.md`
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Python3 nicht gefunden?**
|
||||
```bash
|
||||
sudo apt-get install python3 # Ubuntu/Debian
|
||||
sudo yum install python3 # CentOS/RHEL
|
||||
```
|
||||
|
||||
**Keine Berechtigung?**
|
||||
```bash
|
||||
chmod +x custom/scripts/validate_and_rebuild.py
|
||||
```
|
||||
|
||||
**Script findet rebuild.php nicht?**
|
||||
```bash
|
||||
# Ins richtige Verzeichnis wechseln
|
||||
cd /var/lib/docker/volumes/vmh-espocrm_espocrm/_data
|
||||
python3 custom/scripts/validate_and_rebuild.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**💡 Tipp:** Führe das Script nach **jeder** Änderung an Custom-Dateien aus!
|
||||
182
custom/scripts/VALIDATOR_README.md
Normal file
182
custom/scripts/VALIDATOR_README.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# EspoCRM Validator & Rebuild Tool
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das neue Python-basierte Validierungs-Tool `validate_and_rebuild.py` ersetzt das bisherige Bash-Script und bietet erweiterte Prüfungen für EspoCRM Custom-Entities.
|
||||
|
||||
## Features
|
||||
|
||||
### ✅ Automatische Validierungen
|
||||
|
||||
1. **JSON-Syntax-Prüfung**
|
||||
- Validiert alle `.json` Dateien im `custom/` Verzeichnis
|
||||
- Findet Syntax-Fehler mit Dateiname und Zeilennummer
|
||||
|
||||
2. **Relationship-Konsistenz**
|
||||
- Prüft bidirektionale Relationships (hasMany/hasOne)
|
||||
- Validiert `foreign`-Links zwischen Entities
|
||||
- Überprüft `relationName`-Konsistenz
|
||||
- Erkennt fehlende Gegenseiten-Definitionen
|
||||
|
||||
3. **Formula-Script Platzierung**
|
||||
- Prüft ob Formula-Scripts in `/formula/` statt `/entityDefs/` liegen
|
||||
- Warnt vor leeren Formula-Definitionen
|
||||
|
||||
4. **i18n-Vollständigkeit**
|
||||
- Findet fehlende Übersetzungen (de_DE / en_US)
|
||||
- Prüft Link-Labels für alle Custom-Relationships
|
||||
- Warnt bei komplett fehlenden i18n-Dateien
|
||||
|
||||
5. **Layout-Struktur**
|
||||
- Validiert clientDefs und bottomPanels
|
||||
- Findet unnötige `false`-Elemente
|
||||
|
||||
6. **Dateirechte**
|
||||
- Prüft Owner (www-data:www-data)
|
||||
- Korrigiert Permissions automatisch
|
||||
|
||||
7. **Rebuild-Ausführung**
|
||||
- Führt `rebuild.php` nur aus, wenn keine kritischen Fehler vorliegen
|
||||
|
||||
## Verwendung
|
||||
|
||||
### Direkt ausführen
|
||||
```bash
|
||||
cd /path/to/espocrm
|
||||
python3 custom/scripts/validate_and_rebuild.py
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Exit Codes
|
||||
|
||||
- `0` - Erfolg: Alle Validierungen bestanden, Rebuild erfolgreich
|
||||
- `1` - Fehler: Kritische Fehler gefunden oder Rebuild fehlgeschlagen
|
||||
|
||||
## Output-Format
|
||||
|
||||
Das Script verwendet farbcodierte Ausgaben:
|
||||
|
||||
- 🟢 **Grün (✓)**: Erfolgreich / OK
|
||||
- 🟡 **Gelb (⚠)**: Warnungen (nicht kritisch)
|
||||
- 🔴 **Rot (✗)**: Fehler (kritisch, Rebuild wird abgebrochen)
|
||||
- 🔵 **Blau (ℹ)**: Informationen
|
||||
|
||||
## Fehlertypen
|
||||
|
||||
### Kritische Fehler (Rebuild-Abbruch)
|
||||
- JSON-Syntax-Fehler
|
||||
- Fehlende Relationship-Definitionen
|
||||
- Falsch platzierte Formula-Scripts
|
||||
|
||||
### Warnungen (kein Abbruch)
|
||||
- Fehlende i18n-Übersetzungen
|
||||
- Layout-Strukturprobleme
|
||||
- Dateirechte-Probleme
|
||||
|
||||
## Beispiel-Output
|
||||
|
||||
```
|
||||
EspoCRM Custom Entity Validator & Rebuild Tool
|
||||
Arbeitsverzeichnis: /var/lib/docker/volumes/vmh-espocrm_espocrm/_data
|
||||
|
||||
======================================================================
|
||||
1. JSON-SYNTAX VALIDIERUNG
|
||||
======================================================================
|
||||
|
||||
✓ Alle 547 JSON-Dateien sind syntaktisch korrekt
|
||||
|
||||
======================================================================
|
||||
2. RELATIONSHIP-KONSISTENZ
|
||||
======================================================================
|
||||
|
||||
✗ 4 Relationship-Fehler gefunden:
|
||||
• Contact.cBankverbindungenContact: Ziel-Entity 'CBankverbindung' existiert nicht
|
||||
• CMietobjekt.vmhRumungsklages → CVmhRumungsklage: Foreign link 'mietobjekte' fehlt
|
||||
|
||||
======================================================================
|
||||
ZUSAMMENFASSUNG
|
||||
======================================================================
|
||||
|
||||
FEHLER: 4
|
||||
✗ Contact.cBankverbindungenContact: Ziel-Entity 'CBankverbindung' existiert nicht
|
||||
...
|
||||
|
||||
REBUILD ABGEBROCHEN: Kritische Fehler müssen behoben werden!
|
||||
```
|
||||
|
||||
## Erweiterung
|
||||
|
||||
Das Script ist modular aufgebaut. Neue Validierungen können einfach hinzugefügt werden:
|
||||
|
||||
```python
|
||||
def validate_custom_check(self) -> bool:
|
||||
"""Eigene Validierung."""
|
||||
print_header("X. CUSTOM CHECK")
|
||||
|
||||
# Prüflogik hier
|
||||
if error_found:
|
||||
self.errors.append("Fehlerbeschreibung")
|
||||
return False
|
||||
|
||||
print_success("Check erfolgreich")
|
||||
return True
|
||||
```
|
||||
|
||||
Dann in `validate_all()` hinzufügen:
|
||||
|
||||
```python
|
||||
if not self.validate_custom_check():
|
||||
all_valid = False
|
||||
```
|
||||
|
||||
## Anforderungen
|
||||
|
||||
- Python 3.6+
|
||||
- Keine zusätzlichen Packages erforderlich (nur Standard-Library)
|
||||
- Optionale sudo-Rechte für Dateirechte-Korrektur
|
||||
|
||||
## Integration in Workflow
|
||||
|
||||
Das Script kann in automatisierte Workflows integriert werden:
|
||||
|
||||
```bash
|
||||
# In CI/CD Pipeline
|
||||
python3 custom/scripts/validate_and_rebuild.py || exit 1
|
||||
|
||||
# Als Git Pre-Commit Hook
|
||||
#!/bin/bash
|
||||
python3 custom/scripts/validate_and_rebuild.py --dry-run
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Script findet rebuild.php nicht
|
||||
```bash
|
||||
# Stelle sicher, dass du im EspoCRM-Root bist
|
||||
cd /var/lib/docker/volumes/vmh-espocrm_espocrm/_data
|
||||
python3 custom/scripts/validate_and_rebuild.py
|
||||
```
|
||||
|
||||
### Dateirechte können nicht korrigiert werden
|
||||
```bash
|
||||
# Manuell mit sudo korrigieren
|
||||
sudo chown -R www-data:www-data custom/
|
||||
sudo find custom/ -type f -exec chmod 664 {} \;
|
||||
sudo find custom/ -type d -exec chmod 775 {} \;
|
||||
```
|
||||
|
||||
### Python3 nicht verfügbar
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
sudo apt-get install python3
|
||||
|
||||
# CentOS/RHEL
|
||||
sudo yum install python3
|
||||
```
|
||||
|
||||
## Siehe auch
|
||||
|
||||
- [README.md](../../README.md) - Hauptdokumentation
|
||||
- [CUSTOM_DIRECTORY.md](../CUSTOM_DIRECTORY.md) - Custom-Verzeichnis-Struktur
|
||||
- [workflow_manager.php](workflow_manager.php) - Workflow-Management-Tool
|
||||
@@ -1,218 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# EspoCRM Check & Rebuild Script
|
||||
# Prüft auf häufige Fehler und führt bei Fehlerfreiheit einen Rebuild durch
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
CUSTOM_DIR="$SCRIPT_DIR/custom"
|
||||
ERRORS=0
|
||||
WARNINGS=0
|
||||
|
||||
echo "=========================================="
|
||||
echo "EspoCRM Check & Rebuild Script"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Farben für Output
|
||||
RED='\033[0;31m'
|
||||
YELLOW='\033[1;33m'
|
||||
GREEN='\033[0;32m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# 1. JSON-Syntax prüfen
|
||||
echo -e "${BLUE}[1/3] Prüfe JSON-Syntax...${NC}"
|
||||
echo "---"
|
||||
|
||||
JSON_FILES=$(find "$CUSTOM_DIR" -type f -name "*.json" 2>/dev/null)
|
||||
JSON_COUNT=$(echo "$JSON_FILES" | grep -c . || echo 0)
|
||||
|
||||
if [ "$JSON_COUNT" -eq 0 ]; then
|
||||
echo -e "${YELLOW}⚠ Warnung: Keine JSON-Dateien im custom/ Verzeichnis gefunden${NC}"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
else
|
||||
echo "Gefundene JSON-Dateien: $JSON_COUNT"
|
||||
|
||||
INVALID_JSON=0
|
||||
while IFS= read -r file; do
|
||||
if [ -n "$file" ]; then
|
||||
if ! jq empty "$file" 2>/dev/null; then
|
||||
echo -e "${RED}✗ Fehler: Ungültiges JSON in $file${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
INVALID_JSON=$((INVALID_JSON + 1))
|
||||
fi
|
||||
fi
|
||||
done <<< "$JSON_FILES"
|
||||
|
||||
if [ "$INVALID_JSON" -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Alle JSON-Dateien sind gültig${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ $INVALID_JSON JSON-Dateien mit Syntaxfehlern gefunden${NC}"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 2. Dateirechte prüfen und korrigieren
|
||||
echo -e "${BLUE}[2/3] Prüfe Dateirechte...${NC}"
|
||||
echo "---"
|
||||
|
||||
WRONG_OWNER=0
|
||||
FIXED_FILES=0
|
||||
CUSTOM_FILES=$(find "$CUSTOM_DIR" -type f 2>/dev/null || echo "")
|
||||
CUSTOM_DIRS=$(find "$CUSTOM_DIR" -type d 2>/dev/null || echo "")
|
||||
|
||||
if [ -z "$CUSTOM_FILES" ]; then
|
||||
echo -e "${YELLOW}⚠ Warnung: Keine Dateien im custom/ Verzeichnis gefunden${NC}"
|
||||
WARNINGS=$((WARNINGS + 1))
|
||||
else
|
||||
# Prüfe und korrigiere Dateien einzeln
|
||||
while IFS= read -r file; do
|
||||
if [ -n "$file" ]; then
|
||||
OWNER=$(stat -c '%U:%G' "$file" 2>/dev/null || echo "unknown:unknown")
|
||||
if [ "$OWNER" != "www-data:www-data" ]; then
|
||||
WRONG_OWNER=$((WRONG_OWNER + 1))
|
||||
# Korrigiere direkt nur diese Datei
|
||||
if sudo chown www-data:www-data "$file" 2>/dev/null && sudo chmod 664 "$file" 2>/dev/null; then
|
||||
FIXED_FILES=$((FIXED_FILES + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done <<< "$CUSTOM_FILES"
|
||||
|
||||
# Prüfe und korrigiere Verzeichnisse einzeln
|
||||
while IFS= read -r dir; do
|
||||
if [ -n "$dir" ]; then
|
||||
OWNER=$(stat -c '%U:%G' "$dir" 2>/dev/null || echo "unknown:unknown")
|
||||
if [ "$OWNER" != "www-data:www-data" ]; then
|
||||
sudo chown www-data:www-data "$dir" 2>/dev/null && sudo chmod 775 "$dir" 2>/dev/null
|
||||
fi
|
||||
fi
|
||||
done <<< "$CUSTOM_DIRS"
|
||||
|
||||
if [ "$WRONG_OWNER" -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Alle Dateien haben korrekte Berechtigungen (www-data:www-data)${NC}"
|
||||
else
|
||||
if [ "$FIXED_FILES" -eq "$WRONG_OWNER" ]; then
|
||||
echo -e "${GREEN}✓ $FIXED_FILES Dateien korrigiert${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Fehler: Konnte nicht alle Berechtigungen korrigieren${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# 3. Cache-Verzeichnis prüfen
|
||||
echo -e "${BLUE}[3/3] Prüfe System...${NC}"
|
||||
echo "---"
|
||||
|
||||
if [ ! -d "$SCRIPT_DIR/data/cache" ]; then
|
||||
echo -e "${RED}✗ Fehler: Cache-Verzeichnis existiert nicht${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo -e "${GREEN}✓ Cache-Verzeichnis existiert${NC}"
|
||||
fi
|
||||
|
||||
if [ ! -d "$SCRIPT_DIR/data/logs" ]; then
|
||||
echo -e "${RED}✗ Fehler: Logs-Verzeichnis existiert nicht${NC}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo -e "${GREEN}✓ Logs-Verzeichnis existiert${NC}"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Zusammenfassung:"
|
||||
echo "---"
|
||||
|
||||
if [ "$ERRORS" -eq 0 ] && [ "$WARNINGS" -eq 0 ]; then
|
||||
echo -e "${GREEN}✓ Keine Fehler oder Warnungen gefunden${NC}"
|
||||
elif [ "$ERRORS" -eq 0 ]; then
|
||||
echo -e "${YELLOW}⚠ $WARNINGS Warnung(en) gefunden (Rebuild wird trotzdem ausgeführt)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ $ERRORS Fehler und $WARNINGS Warnung(en) gefunden${NC}"
|
||||
fi
|
||||
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Entscheidung: Rebuild durchführen oder nicht
|
||||
if [ "$ERRORS" -gt 0 ]; then
|
||||
echo -e "${RED}REBUILD WIRD NICHT DURCHGEFÜHRT${NC}"
|
||||
echo "Bitte behebe die oben genannten Fehler und führe das Script erneut aus."
|
||||
exit 1
|
||||
else
|
||||
echo -e "${GREEN}Starte Rebuild und Cache-Bereinigung...${NC}"
|
||||
echo ""
|
||||
|
||||
# Rebuild und Cache-Bereinigung durchführen
|
||||
if command -v docker &> /dev/null; then
|
||||
echo -e "${BLUE}[1/2] Führe Clear Cache aus...${NC}"
|
||||
if docker exec espocrm php /var/www/html/command.php ClearCache 2>&1; then
|
||||
echo -e "${GREEN}✓ Cache erfolgreich gelöscht${NC}"
|
||||
echo ""
|
||||
|
||||
echo -e "${BLUE}[2/2] Führe Rebuild aus...${NC}"
|
||||
if docker exec espocrm php /var/www/html/command.php rebuild 2>&1; then
|
||||
echo ""
|
||||
echo -e "${GREEN}=========================================="
|
||||
echo "✓ REBUILD ERFOLGREICH ABGESCHLOSSEN"
|
||||
echo "==========================================${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo ""
|
||||
echo -e "${RED}=========================================="
|
||||
echo "✗ REBUILD FEHLGESCHLAGEN"
|
||||
echo "==========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Letzte Log-Einträge:${NC}"
|
||||
echo "---"
|
||||
# Zeige die letzten 30 Zeilen der neuesten Log-Datei
|
||||
LATEST_LOG=$(ls -t "$SCRIPT_DIR/data/logs/"*.log 2>/dev/null | head -1)
|
||||
if [ -n "$LATEST_LOG" ]; then
|
||||
echo -e "${BLUE}Aus: $(basename "$LATEST_LOG")${NC}"
|
||||
tail -n 30 "$LATEST_LOG"
|
||||
else
|
||||
echo -e "${YELLOW}Keine Log-Dateien gefunden in data/logs/${NC}"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ Clear Cache fehlgeschlagen${NC}"
|
||||
echo "Fahre mit Rebuild fort..."
|
||||
echo ""
|
||||
|
||||
if docker exec espocrm php /var/www/html/command.php rebuild 2>&1; then
|
||||
echo ""
|
||||
echo -e "${GREEN}=========================================="
|
||||
echo "✓ REBUILD ERFOLGREICH ABGESCHLOSSEN"
|
||||
echo "==========================================${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo ""
|
||||
echo -e "${RED}=========================================="
|
||||
echo "✗ REBUILD FEHLGESCHLAGEN"
|
||||
echo "==========================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}Letzte Log-Einträge:${NC}"
|
||||
echo "---"
|
||||
# Zeige die letzten 30 Zeilen der neuesten Log-Datei
|
||||
LATEST_LOG=$(ls -t "$SCRIPT_DIR/data/logs/"*.log 2>/dev/null | head -1)
|
||||
if [ -n "$LATEST_LOG" ]; then
|
||||
echo -e "${BLUE}Aus: $(basename "$LATEST_LOG")${NC}"
|
||||
tail -n 30 "$LATEST_LOG"
|
||||
else
|
||||
echo -e "${YELLOW}Keine Log-Dateien gefunden in data/logs/${NC}"
|
||||
fi
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo -e "${RED}✗ Docker nicht gefunden. Rebuild kann nicht durchgeführt werden.${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
536
custom/scripts/validate_and_rebuild.py
Executable file
536
custom/scripts/validate_and_rebuild.py
Executable file
@@ -0,0 +1,536 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
EspoCRM Custom Entity Validator & Rebuild Tool
|
||||
Führt umfassende Validierungen durch bevor der Rebuild ausgeführt wird.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple, Set
|
||||
from collections import defaultdict
|
||||
|
||||
# ANSI Color Codes
|
||||
class Colors:
|
||||
GREEN = '\033[92m'
|
||||
YELLOW = '\033[93m'
|
||||
RED = '\033[91m'
|
||||
BLUE = '\033[94m'
|
||||
BOLD = '\033[1m'
|
||||
END = '\033[0m'
|
||||
|
||||
def print_header(text: str):
|
||||
print(f"\n{Colors.BOLD}{Colors.BLUE}{'='*70}{Colors.END}")
|
||||
print(f"{Colors.BOLD}{Colors.BLUE}{text.center(70)}{Colors.END}")
|
||||
print(f"{Colors.BOLD}{Colors.BLUE}{'='*70}{Colors.END}\n")
|
||||
|
||||
def print_success(text: str):
|
||||
print(f"{Colors.GREEN}✓{Colors.END} {text}")
|
||||
|
||||
def print_warning(text: str):
|
||||
print(f"{Colors.YELLOW}⚠{Colors.END} {text}")
|
||||
|
||||
def print_error(text: str):
|
||||
print(f"{Colors.RED}✗{Colors.END} {text}")
|
||||
|
||||
def print_info(text: str):
|
||||
print(f"{Colors.BLUE}ℹ{Colors.END} {text}")
|
||||
|
||||
class EntityValidator:
|
||||
def __init__(self, base_path: str):
|
||||
self.base_path = Path(base_path)
|
||||
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.errors = []
|
||||
self.warnings = []
|
||||
self.entity_defs = {}
|
||||
self.relationships = defaultdict(list)
|
||||
|
||||
def validate_json_syntax(self) -> bool:
|
||||
"""Validiere JSON-Syntax aller Dateien im custom-Verzeichnis."""
|
||||
print_header("1. JSON-SYNTAX VALIDIERUNG")
|
||||
|
||||
json_files = list(self.custom_path.rglob("*.json"))
|
||||
if not json_files:
|
||||
print_warning("Keine JSON-Dateien gefunden")
|
||||
return True
|
||||
|
||||
invalid_files = []
|
||||
for json_file in json_files:
|
||||
try:
|
||||
with open(json_file, 'r', encoding='utf-8') as f:
|
||||
json.load(f)
|
||||
except json.JSONDecodeError as e:
|
||||
self.errors.append(f"JSON-Fehler in {json_file.relative_to(self.base_path)}: {e}")
|
||||
invalid_files.append(str(json_file.relative_to(self.base_path)))
|
||||
|
||||
if invalid_files:
|
||||
print_error(f"{len(invalid_files)} Datei(en) mit JSON-Fehlern gefunden:")
|
||||
for f in invalid_files:
|
||||
print(f" {Colors.RED}•{Colors.END} {f}")
|
||||
return False
|
||||
else:
|
||||
print_success(f"Alle {len(json_files)} JSON-Dateien sind syntaktisch korrekt")
|
||||
return True
|
||||
|
||||
def load_entity_defs(self):
|
||||
"""Lade alle entityDefs für weitere Analysen."""
|
||||
entity_defs_path = self.metadata_path / "entityDefs"
|
||||
if not entity_defs_path.exists():
|
||||
return
|
||||
|
||||
for json_file in entity_defs_path.glob("*.json"):
|
||||
entity_name = json_file.stem
|
||||
try:
|
||||
with open(json_file, 'r', encoding='utf-8') as f:
|
||||
self.entity_defs[entity_name] = json.load(f)
|
||||
except Exception as e:
|
||||
# Fehler wird bereits in JSON-Validierung gemeldet
|
||||
pass
|
||||
|
||||
def validate_relationships(self) -> bool:
|
||||
"""Validiere Relationship-Definitionen zwischen Entities."""
|
||||
print_header("2. RELATIONSHIP-KONSISTENZ")
|
||||
|
||||
if not self.entity_defs:
|
||||
print_warning("Keine entityDefs geladen")
|
||||
return True
|
||||
|
||||
relationship_errors = []
|
||||
checked_pairs = set()
|
||||
|
||||
# Links die nicht geprüft werden (Standard-EspoCRM parent-Relationships)
|
||||
skip_foreign_links = {'parent', 'parents'}
|
||||
|
||||
for entity_name, entity_def in self.entity_defs.items():
|
||||
links = entity_def.get('links', {})
|
||||
|
||||
for link_name, link_def in links.items():
|
||||
link_type = link_def.get('type')
|
||||
target_entity = link_def.get('entity')
|
||||
foreign = link_def.get('foreign')
|
||||
relation_name = link_def.get('relationName')
|
||||
|
||||
# Überspringe parent-Links (Standard Activities-Relationship)
|
||||
if foreign in skip_foreign_links:
|
||||
continue
|
||||
|
||||
# Nur hasMany und hasOne prüfen (nicht belongsTo, da das die Gegenseite ist)
|
||||
if link_type in ['hasMany', 'hasOne'] and target_entity and foreign:
|
||||
pair_key = tuple(sorted([f"{entity_name}.{link_name}", f"{target_entity}.{foreign}"]))
|
||||
if pair_key in checked_pairs:
|
||||
continue
|
||||
checked_pairs.add(pair_key)
|
||||
|
||||
# Prüfe ob Ziel-Entity existiert
|
||||
if target_entity not in self.entity_defs:
|
||||
relationship_errors.append(
|
||||
f"{entity_name}.{link_name}: Ziel-Entity '{target_entity}' existiert nicht"
|
||||
)
|
||||
continue
|
||||
|
||||
target_links = self.entity_defs[target_entity].get('links', {})
|
||||
|
||||
# Prüfe ob foreign Link existiert
|
||||
if foreign not in target_links:
|
||||
relationship_errors.append(
|
||||
f"{entity_name}.{link_name} → {target_entity}: "
|
||||
f"Foreign link '{foreign}' fehlt in {target_entity}"
|
||||
)
|
||||
continue
|
||||
|
||||
foreign_def = target_links[foreign]
|
||||
foreign_foreign = foreign_def.get('foreign')
|
||||
foreign_relation_name = foreign_def.get('relationName')
|
||||
|
||||
# Prüfe ob foreign.foreign zurück zeigt
|
||||
if foreign_foreign != link_name:
|
||||
relationship_errors.append(
|
||||
f"{entity_name}.{link_name} ↔ {target_entity}.{foreign}: "
|
||||
f"Foreign zeigt auf '{foreign_foreign}' statt auf '{link_name}'"
|
||||
)
|
||||
|
||||
# Prüfe ob relationName übereinstimmt (falls beide definiert)
|
||||
if relation_name and foreign_relation_name and relation_name != foreign_relation_name:
|
||||
relationship_errors.append(
|
||||
f"{entity_name}.{link_name} ↔ {target_entity}.{foreign}: "
|
||||
f"relationName unterschiedlich ('{relation_name}' vs '{foreign_relation_name}')"
|
||||
)
|
||||
|
||||
if relationship_errors:
|
||||
print_error(f"{len(relationship_errors)} Relationship-Fehler gefunden:")
|
||||
for err in relationship_errors:
|
||||
print(f" {Colors.RED}•{Colors.END} {err}")
|
||||
self.errors.extend(relationship_errors)
|
||||
return False
|
||||
else:
|
||||
print_success(f"{len(checked_pairs)} Relationships geprüft - alle konsistent")
|
||||
return True
|
||||
|
||||
def validate_formula_placement(self) -> bool:
|
||||
"""Prüfe ob Formula-Scripts korrekt in /formula/ statt /entityDefs/ platziert sind."""
|
||||
print_header("3. FORMULA-SCRIPT PLATZIERUNG")
|
||||
|
||||
misplaced_formulas = []
|
||||
|
||||
# Prüfe entityDefs auf formula-Definitionen (sollte nicht da sein)
|
||||
for entity_name, entity_def in self.entity_defs.items():
|
||||
if 'formula' in entity_def:
|
||||
misplaced_formulas.append(
|
||||
f"entityDefs/{entity_name}.json enthält 'formula' - "
|
||||
f"sollte in formula/{entity_name}.json sein"
|
||||
)
|
||||
|
||||
# Prüfe ob formula-Dateien existieren und valide sind
|
||||
formula_path = self.metadata_path / "formula"
|
||||
formula_count = 0
|
||||
if formula_path.exists():
|
||||
for formula_file in formula_path.glob("*.json"):
|
||||
formula_count += 1
|
||||
try:
|
||||
with open(formula_file, 'r', encoding='utf-8') as f:
|
||||
formula_def = json.load(f)
|
||||
# Prüfe auf leere oder null Scripts
|
||||
for key, value in formula_def.items():
|
||||
if value == "" or value is None:
|
||||
self.warnings.append(
|
||||
f"formula/{formula_file.name}: '{key}' ist leer oder null"
|
||||
)
|
||||
except Exception:
|
||||
pass # JSON-Fehler bereits gemeldet
|
||||
|
||||
if misplaced_formulas:
|
||||
print_error(f"{len(misplaced_formulas)} Formula-Platzierungsfehler:")
|
||||
for err in misplaced_formulas:
|
||||
print(f" {Colors.RED}•{Colors.END} {err}")
|
||||
self.errors.extend(misplaced_formulas)
|
||||
return False
|
||||
else:
|
||||
print_success(f"{formula_count} Formula-Definitionen korrekt platziert")
|
||||
return True
|
||||
|
||||
def validate_i18n_completeness(self) -> bool:
|
||||
"""Prüfe i18n-Definitionen auf Vollständigkeit."""
|
||||
print_header("4. i18n-VOLLSTÄNDIGKEIT")
|
||||
|
||||
if not self.entity_defs:
|
||||
print_warning("Keine entityDefs zum Prüfen")
|
||||
return True
|
||||
|
||||
missing_i18n = []
|
||||
incomplete_i18n = []
|
||||
|
||||
languages = ['de_DE', 'en_US']
|
||||
custom_entities = [name for name in self.entity_defs.keys()
|
||||
if name.startswith('C') or name.startswith('CVmh')]
|
||||
|
||||
for entity_name in custom_entities:
|
||||
entity_def = self.entity_defs[entity_name]
|
||||
links = entity_def.get('links', {})
|
||||
|
||||
# Finde alle hasMany/hasOne Links die übersetzt werden sollten
|
||||
custom_links = []
|
||||
for link_name, link_def in links.items():
|
||||
link_type = link_def.get('type')
|
||||
if link_type in ['hasMany', 'hasOne']:
|
||||
# Überspringe System-Links
|
||||
if link_name not in ['createdBy', 'modifiedBy', 'assignedUser', 'teams']:
|
||||
custom_links.append(link_name)
|
||||
|
||||
if not custom_links:
|
||||
continue
|
||||
|
||||
for lang in languages:
|
||||
i18n_file = self.i18n_path / lang / f"{entity_name}.json"
|
||||
|
||||
if not i18n_file.exists():
|
||||
missing_i18n.append(f"{entity_name}: {lang} fehlt komplett")
|
||||
continue
|
||||
|
||||
try:
|
||||
with open(i18n_file, 'r', encoding='utf-8') as f:
|
||||
i18n_def = json.load(f)
|
||||
links_i18n = i18n_def.get('links', {})
|
||||
|
||||
# Prüfe ob alle custom Links übersetzt sind
|
||||
for link_name in custom_links:
|
||||
if link_name not in links_i18n:
|
||||
incomplete_i18n.append(
|
||||
f"{entity_name} ({lang}): Link '{link_name}' fehlt in i18n"
|
||||
)
|
||||
except Exception:
|
||||
pass # JSON-Fehler bereits gemeldet
|
||||
|
||||
total_issues = len(missing_i18n) + len(incomplete_i18n)
|
||||
|
||||
if missing_i18n:
|
||||
print_error(f"{len(missing_i18n)} komplett fehlende i18n-Dateien:")
|
||||
for err in missing_i18n[:10]: # Max 10 anzeigen
|
||||
print(f" {Colors.RED}•{Colors.END} {err}")
|
||||
if len(missing_i18n) > 10:
|
||||
print(f" {Colors.RED}...{Colors.END} und {len(missing_i18n) - 10} weitere")
|
||||
|
||||
if incomplete_i18n:
|
||||
print_warning(f"{len(incomplete_i18n)} unvollständige i18n-Definitionen:")
|
||||
for err in incomplete_i18n[:10]: # Max 10 anzeigen
|
||||
print(f" {Colors.YELLOW}•{Colors.END} {err}")
|
||||
if len(incomplete_i18n) > 10:
|
||||
print(f" {Colors.YELLOW}...{Colors.END} und {len(incomplete_i18n) - 10} weitere")
|
||||
|
||||
if not missing_i18n and not incomplete_i18n:
|
||||
print_success(f"i18n für {len(custom_entities)} Custom-Entities vollständig")
|
||||
|
||||
# i18n-Fehler sind nur Warnungen, kein Abbruch
|
||||
self.warnings.extend(missing_i18n + incomplete_i18n)
|
||||
return True
|
||||
|
||||
def validate_layout_structure(self) -> bool:
|
||||
"""Prüfe Layout-Dateien auf häufige Fehler."""
|
||||
print_header("5. LAYOUT-STRUKTUR VALIDIERUNG")
|
||||
|
||||
layouts_path = self.metadata_path / "clientDefs"
|
||||
if not layouts_path.exists():
|
||||
print_warning("Keine clientDefs gefunden")
|
||||
return True
|
||||
|
||||
layout_errors = []
|
||||
checked_layouts = 0
|
||||
|
||||
for client_def_file in layouts_path.glob("*.json"):
|
||||
try:
|
||||
with open(client_def_file, 'r', encoding='utf-8') as f:
|
||||
client_def = json.load(f)
|
||||
|
||||
# Prüfe auf häufige Layout-Fehler in bottomPanels
|
||||
bottom_panels = client_def.get('bottomPanelsDetail', {})
|
||||
for panel_key, panel_def in bottom_panels.items():
|
||||
checked_layouts += 1
|
||||
|
||||
# Prüfe auf unnötige false-Elemente
|
||||
if isinstance(panel_def, dict):
|
||||
for key, value in panel_def.items():
|
||||
if value is False and key not in ['disabled', 'sticked']:
|
||||
layout_errors.append(
|
||||
f"{client_def_file.stem}: bottomPanelsDetail.{panel_key}.{key} "
|
||||
f"sollte nicht 'false' sein"
|
||||
)
|
||||
except Exception:
|
||||
pass # JSON-Fehler bereits gemeldet
|
||||
|
||||
if layout_errors:
|
||||
print_warning(f"{len(layout_errors)} Layout-Strukturprobleme:")
|
||||
for err in layout_errors[:5]:
|
||||
print(f" {Colors.YELLOW}•{Colors.END} {err}")
|
||||
if len(layout_errors) > 5:
|
||||
print(f" {Colors.YELLOW}...{Colors.END} und {len(layout_errors) - 5} weitere")
|
||||
self.warnings.extend(layout_errors)
|
||||
else:
|
||||
print_success(f"{checked_layouts} Layout-Definitionen geprüft")
|
||||
|
||||
return True
|
||||
|
||||
def check_file_permissions(self) -> bool:
|
||||
"""Prüfe Dateirechte im custom-Verzeichnis."""
|
||||
print_header("6. DATEIRECHTE-PRÜFUNG")
|
||||
|
||||
try:
|
||||
# Prüfe ob Dateien von www-data gehören
|
||||
result = subprocess.run(
|
||||
['find', str(self.custom_path), '!', '-user', 'www-data', '-o', '!', '-group', 'www-data'],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
wrong_owner_files = [line for line in result.stdout.strip().split('\n') if line]
|
||||
|
||||
if wrong_owner_files:
|
||||
print_warning(f"{len(wrong_owner_files)} Dateien mit falschen Rechten gefunden")
|
||||
print_info("Versuche automatische Korrektur...")
|
||||
|
||||
# Versuche Rechte zu korrigieren
|
||||
try:
|
||||
subprocess.run(
|
||||
['sudo', 'chown', '-R', 'www-data:www-data', str(self.custom_path)],
|
||||
check=True,
|
||||
capture_output=True
|
||||
)
|
||||
subprocess.run(
|
||||
['sudo', 'find', str(self.custom_path), '-type', 'f', '-exec', 'chmod', '664', '{}', ';'],
|
||||
check=True,
|
||||
capture_output=True
|
||||
)
|
||||
subprocess.run(
|
||||
['sudo', 'find', str(self.custom_path), '-type', 'd', '-exec', 'chmod', '775', '{}', ';'],
|
||||
check=True,
|
||||
capture_output=True
|
||||
)
|
||||
print_success("Dateirechte korrigiert")
|
||||
except subprocess.CalledProcessError:
|
||||
print_warning("Konnte Dateirechte nicht automatisch korrigieren (sudo erforderlich)")
|
||||
else:
|
||||
print_success("Alle Dateirechte korrekt (www-data:www-data)")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print_warning(f"Konnte Dateirechte nicht prüfen: {e}")
|
||||
return True
|
||||
|
||||
def run_rebuild(self) -> bool:
|
||||
"""Führe den EspoCRM Rebuild aus."""
|
||||
print_header("7. ESPOCRM REBUILD")
|
||||
|
||||
rebuild_script = self.base_path / "rebuild.php"
|
||||
if not rebuild_script.exists():
|
||||
print_error(f"rebuild.php nicht gefunden in {self.base_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
print_info("Starte Rebuild (kann 10-30 Sekunden dauern)...")
|
||||
result = subprocess.run(
|
||||
['php', str(rebuild_script)],
|
||||
cwd=str(self.base_path),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
print_success("Rebuild erfolgreich abgeschlossen")
|
||||
return True
|
||||
else:
|
||||
print_error("Rebuild fehlgeschlagen:")
|
||||
if result.stderr:
|
||||
print(f"\n{result.stderr}")
|
||||
return False
|
||||
except subprocess.TimeoutExpired:
|
||||
print_error("Rebuild-Timeout (>60 Sekunden)")
|
||||
return False
|
||||
except Exception as e:
|
||||
print_error(f"Rebuild-Fehler: {e}")
|
||||
return False
|
||||
|
||||
def print_summary(self):
|
||||
"""Drucke Zusammenfassung aller Ergebnisse."""
|
||||
print_header("ZUSAMMENFASSUNG")
|
||||
|
||||
if self.errors:
|
||||
print(f"\n{Colors.RED}{Colors.BOLD}FEHLER: {len(self.errors)}{Colors.END}")
|
||||
for err in self.errors:
|
||||
print(f" {Colors.RED}✗{Colors.END} {err}")
|
||||
|
||||
if self.warnings:
|
||||
print(f"\n{Colors.YELLOW}{Colors.BOLD}WARNUNGEN: {len(self.warnings)}{Colors.END}")
|
||||
for warn in self.warnings[:10]:
|
||||
print(f" {Colors.YELLOW}⚠{Colors.END} {warn}")
|
||||
if len(self.warnings) > 10:
|
||||
print(f" {Colors.YELLOW}...{Colors.END} und {len(self.warnings) - 10} weitere Warnungen")
|
||||
|
||||
if not self.errors and not self.warnings:
|
||||
print(f"\n{Colors.GREEN}{Colors.BOLD}✓ ALLE PRÜFUNGEN BESTANDEN{Colors.END}")
|
||||
|
||||
print()
|
||||
|
||||
def validate_all(self) -> bool:
|
||||
"""Führe alle Validierungen durch."""
|
||||
all_valid = True
|
||||
|
||||
# 1. JSON-Syntax (kritisch)
|
||||
if not self.validate_json_syntax():
|
||||
all_valid = False
|
||||
print_error("\nAbbruch: JSON-Syntax-Fehler müssen behoben werden!\n")
|
||||
return False
|
||||
|
||||
# Lade entityDefs für weitere Checks
|
||||
self.load_entity_defs()
|
||||
|
||||
# 2. Relationships (kritisch)
|
||||
if not self.validate_relationships():
|
||||
all_valid = False
|
||||
|
||||
# 3. Formula-Platzierung (kritisch)
|
||||
if not self.validate_formula_placement():
|
||||
all_valid = False
|
||||
|
||||
# 4. i18n-Vollständigkeit (nur Warnung)
|
||||
self.validate_i18n_completeness()
|
||||
|
||||
# 5. Layout-Struktur (nur Warnung)
|
||||
self.validate_layout_structure()
|
||||
|
||||
# 6. Dateirechte (nicht kritisch für Rebuild)
|
||||
self.check_file_permissions()
|
||||
|
||||
return all_valid
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='EspoCRM Custom Entity Validator & Rebuild Tool'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
action='store_true',
|
||||
help='Nur Validierungen durchführen, kein Rebuild'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--no-rebuild',
|
||||
action='store_true',
|
||||
help='Synonym für --dry-run'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
dry_run = args.dry_run or args.no_rebuild
|
||||
|
||||
# Finde EspoCRM Root-Verzeichnis
|
||||
script_dir = Path(__file__).parent.parent.parent
|
||||
|
||||
if not (script_dir / "rebuild.php").exists():
|
||||
print_error("Fehler: Nicht im EspoCRM-Root-Verzeichnis!")
|
||||
print_info(f"Aktueller Pfad: {script_dir}")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"{Colors.BOLD}EspoCRM Custom Entity Validator & Rebuild Tool{Colors.END}")
|
||||
print(f"Arbeitsverzeichnis: {script_dir}")
|
||||
if dry_run:
|
||||
print(f"{Colors.YELLOW}Modus: DRY-RUN (kein Rebuild){Colors.END}")
|
||||
print()
|
||||
|
||||
validator = EntityValidator(str(script_dir))
|
||||
|
||||
# Validierungen durchführen
|
||||
all_valid = validator.validate_all()
|
||||
|
||||
# Zusammenfassung drucken
|
||||
validator.print_summary()
|
||||
|
||||
# Entscheidung über Rebuild
|
||||
if not all_valid:
|
||||
print_error("REBUILD ABGEBROCHEN: Kritische Fehler müssen behoben werden!")
|
||||
sys.exit(1)
|
||||
|
||||
if dry_run:
|
||||
print_info("Dry-Run Modus: Rebuild übersprungen")
|
||||
print(f"\n{Colors.GREEN}{Colors.BOLD}✓ VALIDIERUNGEN ABGESCHLOSSEN{Colors.END}\n")
|
||||
sys.exit(0)
|
||||
|
||||
if validator.warnings:
|
||||
print_warning(
|
||||
f"Es gibt {len(validator.warnings)} Warnungen, aber keine kritischen Fehler."
|
||||
)
|
||||
print_info("Rebuild wird trotzdem durchgeführt...\n")
|
||||
|
||||
# Rebuild ausführen
|
||||
if validator.run_rebuild():
|
||||
print(f"\n{Colors.GREEN}{Colors.BOLD}✓ ERFOLGREICH ABGESCHLOSSEN{Colors.END}\n")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"\n{Colors.RED}{Colors.BOLD}✗ REBUILD FEHLGESCHLAGEN{Colors.END}\n")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user