refactor(archive): streamline repository archiving logic

- Simplify archive directory resolution using catalog state
- Replace redundant directory checks with repository model methods
- Remove unused imports and legacy functions for cleaner code
- Improve error handling for missing archive categories
This commit is contained in:
2025-05-11 14:38:06 +02:00
parent eb5bf52f2f
commit d3ee7ee63d

View File

@@ -1,29 +1,21 @@
import shutil
import tarfile
from datetime import datetime, timedelta
from datetime import datetime
from pathlib import Path
from typer import Typer, Option
from rich.console import Console
from rich.table import Table
import subprocess
import zstandard
import uuid
from repocat.config import load_config
from repocat.cli.clean import is_git_dirty, is_git_unpushed, get_dir_size
from repocat.models.catalog import RepoCatalogState
app = Typer()
console = Console()
def get_archive_target_dir():
config = load_config()
for cat in config.categories:
if cat.is_archive:
return config.base_dir / cat.subdir
raise ValueError("Keine Archiv-Kategorie (is_archive: true) in der Konfiguration gefunden.")
def archive_repo(source: Path, target_dir: Path, dry_run: bool):
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"
@@ -33,7 +25,7 @@ def archive_repo(source: Path, target_dir: Path, dry_run: bool):
raise FileExistsError(f"Archiv existiert bereits: {archive_path}")
if dry_run:
return archive_path # Nur anzeigen
return archive_path
with open(archive_path, "wb") as f:
cctx = zstandard.ZstdCompressor(level=20)
@@ -44,6 +36,7 @@ def archive_repo(source: Path, target_dir: Path, dry_run: bool):
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."),
@@ -51,11 +44,16 @@ def run(
category: list[str] = Option(None, "--category", "-c", help="Nur bestimmte Kategorien prüfen."),
):
config = load_config()
now = datetime.now()
archive_path = get_archive_target_dir()
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)
base = config.base_dir
table = Table(title="Archivierung")
table.add_column("Kategorie")
table.add_column("Repository")
@@ -66,44 +64,33 @@ def run(
archived = 0
skipped = 0
for cat in config.categories:
if cat.is_archive or cat.name == "review" or cat.volatile:
for cat in catalog.categories:
if cat.config.is_archive or cat.config.name == "review" or cat.config.volatile:
continue
if category and cat.name not in category:
if category and cat.config.name not in category:
continue
source_dir = base / cat.subdir
if not source_dir.exists():
continue
limit_days = cat.config.days if cat.config.days is not None else older_than
for repo in sorted(source_dir.iterdir()):
if not repo.is_dir():
continue
mtime = datetime.fromtimestamp(repo.stat().st_mtime)
age_days = (now - mtime).days
size_mb = get_dir_size(repo) / 1024 / 1024
limit_days = cat.days if cat.days is not None else older_than
if age_days < limit_days:
table.add_row(cat.name, repo.name, str(age_days), f"{size_mb:.1f}", "[blue]Zu jung[/]")
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").exists():
if is_git_dirty(repo) or is_git_unpushed(repo):
table.add_row(cat.name, repo.name, str(age_days), f"{size_mb:.1f}", "[blue]Geschützt (git)[/]")
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, archive_path, dry_run)
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.name, repo.name, str(age_days), f"{size_mb:.1f}", action)
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")