99 lines
3.5 KiB
Python
99 lines
3.5 KiB
Python
import shutil
|
|
import tarfile
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typer import Typer, Option
|
|
from rich.console import Console
|
|
from rich.table import Table
|
|
import zstandard
|
|
import uuid
|
|
|
|
from repocat.config import load_config
|
|
from repocat.models.catalog import RepoCatalogState
|
|
|
|
app = Typer()
|
|
console = Console()
|
|
|
|
|
|
def archive_repo(source: Path, target_dir: Path, dry_run: bool) -> Path:
|
|
today = datetime.today().strftime("%Y.%m.%d")
|
|
unique = uuid.uuid4().hex[:8]
|
|
archive_name = f"{today} - {source.name} - {unique}.tar.zst"
|
|
archive_path = target_dir / archive_name
|
|
|
|
if archive_path.exists():
|
|
raise FileExistsError(f"Archiv existiert bereits: {archive_path}")
|
|
|
|
if dry_run:
|
|
return archive_path
|
|
|
|
with open(archive_path, "wb") as f:
|
|
cctx = zstandard.ZstdCompressor(level=20, threads=-1)
|
|
with cctx.stream_writer(f) as compressor:
|
|
with tarfile.open(fileobj=compressor, mode="w|") as tar:
|
|
tar.add(source, arcname=source.name)
|
|
|
|
shutil.rmtree(source)
|
|
return archive_path
|
|
|
|
|
|
@app.command("run")
|
|
def run(
|
|
older_than: int = Option(30, "--older-than", help="Nur Repositories älter als X Tage archivieren."),
|
|
dry_run: bool = Option(False, "--dry-run", help="Keine Änderungen vornehmen."),
|
|
category: list[str] = Option(None, "--category", "-c", help="Nur bestimmte Kategorien prüfen."),
|
|
):
|
|
config = load_config()
|
|
catalog = RepoCatalogState.from_config(config)
|
|
|
|
archive_cat = next((c for c in catalog.categories if c.config.is_archive), None)
|
|
if not archive_cat:
|
|
console.print("[red]Keine Archiv-Kategorie in der Konfiguration gefunden.[/]")
|
|
raise SystemExit(1)
|
|
|
|
archive_path = archive_cat.path
|
|
archive_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
table = Table(title="Archivierung")
|
|
table.add_column("Kategorie")
|
|
table.add_column("Repository")
|
|
table.add_column("Alter", justify="right")
|
|
table.add_column("Größe (MB)", justify="right")
|
|
table.add_column("Aktion")
|
|
|
|
archived = 0
|
|
skipped = 0
|
|
|
|
for cat in catalog.categories:
|
|
if cat.config.is_archive or cat.config.name == "review" or cat.config.volatile:
|
|
continue
|
|
if category and cat.config.name not in category:
|
|
continue
|
|
|
|
if cat.config.days == 0 or cat.config.days is None:
|
|
continue
|
|
|
|
limit_days = cat.config.days if cat.config.days is not None else older_than
|
|
|
|
for repo in cat.repos:
|
|
if not repo.is_expired(limit_days):
|
|
table.add_row(cat.config.name, repo.name, str(repo.age_days), f"{repo.size_mb:.1f}", "[blue]Zu jung[/]")
|
|
skipped += 1
|
|
continue
|
|
|
|
if repo.git and repo.git.is_protected:
|
|
table.add_row(cat.config.name, repo.name, str(repo.age_days), f"{repo.size_mb:.1f}", "[blue]Geschützt (git)[/]")
|
|
skipped += 1
|
|
continue
|
|
|
|
try:
|
|
archive_file = archive_repo(repo.path, archive_path, dry_run)
|
|
action = "[yellow]Würde archivieren[/]" if dry_run else f"[green]Archiviert →[/] {archive_file.name}"
|
|
archived += 1
|
|
except Exception as e:
|
|
action = f"[red]Fehler: {e}[/]"
|
|
|
|
table.add_row(cat.config.name, repo.name, str(repo.age_days), f"{repo.size_mb:.1f}", action)
|
|
|
|
console.print(table)
|
|
console.print(f"\n[green]✓ Archivierung abgeschlossen[/] – {archived} archiviert, {skipped} übersprungen") |