Files
dotfiles/dotfiles.py

309 lines
9.0 KiB
Python
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()