309 lines
9.0 KiB
Python
Executable File
309 lines
9.0 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
Dotfiles Management System
|
||
|
||
Ein modernes Python-System zur Verwaltung von Konfigurationsdateien
|
||
ohne Symlinks - mit echter Datei-Synchronisation und Template-System.
|
||
"""
|
||
|
||
import sys
|
||
import click
|
||
from pathlib import Path
|
||
from rich.console import Console
|
||
from rich.table import Table
|
||
from rich import print as rprint
|
||
|
||
# Füge src/ zum Python-Pfad hinzu
|
||
sys.path.insert(0, str(Path(__file__).parent / "src"))
|
||
|
||
from src.config import ConfigManager
|
||
from src.sync import FileSynchronizer
|
||
from src.templates import TemplateManager, create_example_templates
|
||
|
||
console = Console()
|
||
|
||
|
||
@click.group()
|
||
@click.version_option(version="2.0.0")
|
||
@click.pass_context
|
||
def cli(ctx):
|
||
"""
|
||
🐍 Dotfiles Management System v2.0
|
||
|
||
Modernes Python-System zur Verwaltung von Konfigurationsdateien
|
||
ohne Symlinks - mit echter Datei-Synchronisation und Template-System.
|
||
"""
|
||
try:
|
||
ctx.ensure_object(dict)
|
||
ctx.obj['config'] = ConfigManager()
|
||
ctx.obj['sync'] = FileSynchronizer(ctx.obj['config'])
|
||
ctx.obj['templates'] = TemplateManager(ctx.obj['config'])
|
||
|
||
except Exception as e:
|
||
rprint(f"[red]❌ Fehler beim Initialisieren: {e}[/red]")
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.option('--dry-run', '-n', is_flag=True, help='Zeige nur was gemacht würde')
|
||
@click.option('--force', '-f', is_flag=True, help='Überschreibe ohne Bestätigung')
|
||
@click.option('--no-interactive', is_flag=True, help='Keine interaktiven Abfragen')
|
||
@click.pass_context
|
||
def install(ctx, dry_run, force, no_interactive):
|
||
"""
|
||
📦 Installiert Templates ins Home-Verzeichnis
|
||
|
||
Kopiert alle konfigurierten Template-Dateien ins Home-Verzeichnis
|
||
und rendert dabei Jinja2-Templates mit maschinenspezifischen Variablen.
|
||
"""
|
||
config = ctx.obj['config']
|
||
sync = ctx.obj['sync']
|
||
|
||
if not config.current_machine:
|
||
rprint("[red]❌ Keine Maschine erkannt![/red]")
|
||
sys.exit(1)
|
||
|
||
rprint(f"[green]🖥️ Aktuelle Maschine: {config.current_machine}[/green]")
|
||
|
||
# Installation ausführen
|
||
result = sync.install(
|
||
dry_run=dry_run,
|
||
force=force,
|
||
interactive=not no_interactive
|
||
)
|
||
|
||
if result.errors:
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.option('--dry-run', '-n', is_flag=True, help='Zeige nur was gemacht würde')
|
||
@click.option('--push', is_flag=True, help='Pushe Änderungen automatisch ins Git-Repository')
|
||
@click.pass_context
|
||
def backup(ctx, dry_run, push):
|
||
"""
|
||
💾 Sichert Dateien vom Home-Verzeichnis ins Repository
|
||
|
||
Kopiert aktuelle Konfigurationsdateien zurück ins Repository
|
||
um Änderungen zu sichern. Mit --push werden die Änderungen
|
||
automatisch committet und gepusht.
|
||
"""
|
||
sync = ctx.obj['sync']
|
||
|
||
result = sync.backup(dry_run=dry_run, git_push=push)
|
||
|
||
if result.errors:
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.pass_context
|
||
def status(ctx):
|
||
"""
|
||
📊 Zeigt Status aller Konfigurationsdateien
|
||
|
||
Vergleicht Template-Dateien mit installierten Dateien
|
||
und zeigt Unterschiede an.
|
||
"""
|
||
sync = ctx.obj['sync']
|
||
sync.status()
|
||
|
||
|
||
@cli.command()
|
||
@click.pass_context
|
||
def templates(ctx):
|
||
"""
|
||
📝 Listet alle verfügbaren Templates auf
|
||
"""
|
||
template_mgr = ctx.obj['templates']
|
||
config = ctx.obj['config']
|
||
|
||
templates = template_mgr.list_templates()
|
||
|
||
if not templates:
|
||
rprint("[yellow]⚠️ Keine Templates gefunden[/yellow]")
|
||
rprint("Verwende 'dotfiles init-templates' um Beispiel-Templates zu erstellen")
|
||
return
|
||
|
||
# Erstelle Tabelle
|
||
table = Table(title=f"Templates für {config.current_machine}")
|
||
table.add_column("Kategorie", style="cyan")
|
||
table.add_column("Template", style="green")
|
||
table.add_column("Typ", style="yellow")
|
||
table.add_column("Größe", justify="right")
|
||
|
||
for template in templates:
|
||
template_type = "🎨 Jinja2" if template['is_template'] else "📄 Statisch"
|
||
size = f"{template['size']} B"
|
||
|
||
table.add_row(
|
||
template['category'],
|
||
template['name'],
|
||
template_type,
|
||
size
|
||
)
|
||
|
||
console.print(table)
|
||
|
||
|
||
@cli.command()
|
||
@click.pass_context
|
||
def init_templates(ctx):
|
||
"""
|
||
🎨 Erstellt Beispiel-Templates
|
||
|
||
Generiert Standard-Templates für bash, git, vim etc.
|
||
"""
|
||
rprint("[blue]🎨 Erstelle Beispiel-Templates...[/blue]")
|
||
create_example_templates()
|
||
|
||
|
||
@cli.command()
|
||
@click.argument('template_path')
|
||
@click.pass_context
|
||
def render(ctx, template_path):
|
||
"""
|
||
🖨️ Rendert ein Template und zeigt das Ergebnis
|
||
|
||
TEMPLATE_PATH: Pfad zum Template (z.B. shell/bashrc.j2)
|
||
"""
|
||
template_mgr = ctx.obj['templates']
|
||
|
||
try:
|
||
content = template_mgr.render_template(template_path)
|
||
rprint(f"[green]📄 Gerendert: {template_path}[/green]\n")
|
||
print(content)
|
||
|
||
except Exception as e:
|
||
rprint(f"[red]❌ Fehler beim Rendern: {e}[/red]")
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.argument('source_file', type=click.Path(exists=True))
|
||
@click.argument('template_path')
|
||
@click.option('--extract-vars', is_flag=True, default=True,
|
||
help='Extrahiere automatisch Variablen')
|
||
@click.pass_context
|
||
def create_template(ctx, source_file, template_path, extract_vars):
|
||
"""
|
||
➕ Erstellt ein Template aus einer existierenden Datei
|
||
|
||
SOURCE_FILE: Pfad zur Quelldatei
|
||
TEMPLATE_PATH: Ziel-Template-Pfad (z.B. shell/bashrc.j2)
|
||
"""
|
||
template_mgr = ctx.obj['templates']
|
||
|
||
source_path = Path(source_file)
|
||
|
||
if not template_path.endswith('.j2'):
|
||
template_path += '.j2'
|
||
|
||
try:
|
||
template_mgr.create_template_from_file(
|
||
source_path,
|
||
template_path,
|
||
extract_variables=extract_vars
|
||
)
|
||
|
||
except Exception as e:
|
||
rprint(f"[red]❌ Fehler beim Erstellen: {e}[/red]")
|
||
sys.exit(1)
|
||
|
||
|
||
@cli.command()
|
||
@click.pass_context
|
||
def variables(ctx):
|
||
"""
|
||
🔧 Zeigt alle verfügbaren Template-Variablen
|
||
"""
|
||
config = ctx.obj['config']
|
||
variables = config.get_template_variables()
|
||
|
||
table = Table(title="Template-Variablen")
|
||
table.add_column("Variable", style="cyan")
|
||
table.add_column("Wert", style="green")
|
||
table.add_column("Typ", style="yellow")
|
||
|
||
for key, value in variables.items():
|
||
table.add_row(
|
||
f"{{{{ {key} }}}}",
|
||
str(value),
|
||
type(value).__name__
|
||
)
|
||
|
||
console.print(table)
|
||
|
||
|
||
@cli.command()
|
||
@click.pass_context
|
||
def info(ctx):
|
||
"""
|
||
ℹ️ Zeigt System-Informationen
|
||
"""
|
||
config = ctx.obj['config']
|
||
|
||
rprint("[bold blue]🐍 Dotfiles Management System v2.0[/bold blue]\n")
|
||
|
||
# Aktuelle Maschine
|
||
if config.current_machine:
|
||
machine_config = config.get_machine_config()
|
||
rprint(f"[green]🖥️ Maschine:[/green] {config.current_machine}")
|
||
rprint(f"[green]📝 Beschreibung:[/green] {machine_config.description}")
|
||
else:
|
||
rprint("[red]❌ Keine Maschine erkannt[/red]")
|
||
|
||
# Template-Statistiken
|
||
template_mgr = ctx.obj['templates']
|
||
templates = template_mgr.list_templates()
|
||
j2_templates = [t for t in templates if t['is_template']]
|
||
static_files = [t for t in templates if not t['is_template']]
|
||
|
||
rprint(f"\n[cyan]📊 Repository-Statistiken:[/cyan]")
|
||
rprint(f" 🎨 Jinja2-Templates: {len(j2_templates)}")
|
||
rprint(f" 📄 Statische Dateien: {len(static_files)}")
|
||
rprint(f" 📁 Gesamt: {len(templates)}")
|
||
|
||
# Verfügbare Maschinen
|
||
rprint(f"\n[cyan]🖥️ Verfügbare Maschinen:[/cyan]")
|
||
for name, machine in config.machines.items():
|
||
status = "✅ Aktiv" if name == config.current_machine else "⚪ Verfügbar"
|
||
rprint(f" {status} {name}: {machine.description}")
|
||
|
||
# Konfiguration
|
||
rprint(f"\n[cyan]⚙️ Konfiguration:[/cyan]")
|
||
rprint(f" 📄 Config-Datei: {config.config_path}")
|
||
rprint(f" 🔐 Verschlüsselung: {'✅ Aktiviert' if config.is_encryption_enabled() else '❌ Deaktiviert'}")
|
||
|
||
backup_settings = config.get_backup_settings()
|
||
rprint(f" 💾 Backups: {'✅ Aktiviert' if backup_settings.get('enabled') else '❌ Deaktiviert'}")
|
||
|
||
|
||
@cli.command()
|
||
@click.argument('machine_name')
|
||
@click.pass_context
|
||
def set_machine(ctx, machine_name):
|
||
"""
|
||
🖥️ Setzt die aktuelle Maschine
|
||
|
||
MACHINE_NAME: Name der Maschine (desktop, laptop, server)
|
||
"""
|
||
config = ctx.obj['config']
|
||
|
||
if machine_name not in config.machines:
|
||
rprint(f"[red]❌ Unbekannte Maschine: {machine_name}[/red]")
|
||
rprint("Verfügbare Maschinen:")
|
||
for name, machine in config.machines.items():
|
||
rprint(f" - {name}: {machine.description}")
|
||
sys.exit(1)
|
||
|
||
config.save_current_machine(machine_name)
|
||
config.current_machine = machine_name
|
||
|
||
machine_config = config.get_machine_config(machine_name)
|
||
rprint(f"[green]✅ Maschine gesetzt: {machine_name}[/green]")
|
||
rprint(f"[green]📝 Beschreibung: {machine_config.description}[/green]")
|
||
|
||
|
||
if __name__ == '__main__':
|
||
cli() |