feat(clean): add snapshot support for volatile repo cleanup
Some checks failed
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Failing after 6s
Build and Publish nightly package / build-and-publish (push) Successful in 33s
Some checks failed
Auto Changelog & Release / detect-version-change (push) Successful in 3s
Auto Changelog & Release / release (push) Has been skipped
Auto Changelog & Release / changelog-only (push) Failing after 6s
Build and Publish nightly package / build-and-publish (push) Successful in 33s
This commit is contained in:
@@ -2,6 +2,8 @@ from typer import Typer, Option
|
||||
from rich.console import Console
|
||||
from rich.table import Table
|
||||
import shutil
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from repocat.config import load_config
|
||||
from repocat.models.catalog import RepoCatalogState
|
||||
@@ -23,7 +25,16 @@ def clean_volatile(
|
||||
"""
|
||||
Löscht **bedingungslos** alle Repositories aus `volatile`-Kategorien (z.B. `scratches`).
|
||||
"""
|
||||
catalog = RepoCatalogState.from_config(load_config())
|
||||
config = load_config()
|
||||
catalog = RepoCatalogState.from_config(config)
|
||||
|
||||
# Snapshot configuration
|
||||
use_snapshots = config.defaults.snapshots if config.defaults else False
|
||||
snapshot_count = config.defaults.snapshot_count if config.defaults else 5
|
||||
|
||||
snapshot_root = config.base_dir / ".snapshots"
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
current_snapshot_dir = snapshot_root / timestamp
|
||||
|
||||
table = Table(title="Clean – Volatile (Zerstörung)")
|
||||
table.add_column("Kategorie")
|
||||
@@ -33,6 +44,7 @@ def clean_volatile(
|
||||
|
||||
deleted = 0
|
||||
skipped = 0
|
||||
snapshotted_categories = set()
|
||||
|
||||
for cat in catalog.categories:
|
||||
if not cat.config.volatile:
|
||||
@@ -40,23 +52,78 @@ def clean_volatile(
|
||||
if category and cat.config.name not in category:
|
||||
continue
|
||||
|
||||
for repo in cat.repos:
|
||||
size_mb = repo.size_mb
|
||||
# If snapshots are enabled, we handle the whole category at once
|
||||
if use_snapshots:
|
||||
if not cat.path.exists():
|
||||
continue
|
||||
|
||||
if dry_run:
|
||||
action = "[yellow]Würde löschen[/]"
|
||||
action = f"[yellow]Würde verschieben nach .snapshots/{timestamp}[/]"
|
||||
else:
|
||||
try:
|
||||
shutil.rmtree(repo.path)
|
||||
action = "[red]Gelöscht[/]"
|
||||
deleted += 1
|
||||
except Exception as e:
|
||||
action = f"[red]Fehler: {e}[/]"
|
||||
# Create snapshot dir only if we actually have something to move
|
||||
if not current_snapshot_dir.exists():
|
||||
current_snapshot_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
table.add_row(cat.config.name, repo.name, f"{size_mb:.1f}", action)
|
||||
target_dir = current_snapshot_dir / cat.config.subdir
|
||||
# Ensure parent of target exists (if subdir is nested)
|
||||
target_dir.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Move the whole category folder
|
||||
# Note: shutil.move(src, dst) where dst does not exist moves src to dst.
|
||||
# If dst exists, it moves src inside dst.
|
||||
# We want to rename cat.path to target_dir.
|
||||
shutil.move(str(cat.path), str(target_dir))
|
||||
|
||||
# Recreate the empty category folder
|
||||
cat.path.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
action = f"[green]Verschoben nach .snapshots/{timestamp}[/]"
|
||||
snapshotted_categories.add(cat.config.name)
|
||||
except Exception as e:
|
||||
action = f"[red]Fehler beim Verschieben: {e}[/]"
|
||||
|
||||
# Add rows for all repos in this category to show what happened to them
|
||||
if cat.repos:
|
||||
for repo in cat.repos:
|
||||
table.add_row(cat.config.name, repo.name, f"{repo.size_mb:.1f}", action)
|
||||
deleted += 1 # Count as "processed/removed from volatile"
|
||||
else:
|
||||
# If no repos but folder existed (and maybe had other files), show a generic row
|
||||
table.add_row(cat.config.name, "(Alle Dateien)", "-", action)
|
||||
|
||||
else:
|
||||
# Old behavior: Delete individual repos
|
||||
for repo in cat.repos:
|
||||
size_mb = repo.size_mb
|
||||
|
||||
if dry_run:
|
||||
action = "[yellow]Würde löschen[/]"
|
||||
else:
|
||||
try:
|
||||
shutil.rmtree(repo.path)
|
||||
action = "[red]Gelöscht[/]"
|
||||
deleted += 1
|
||||
except Exception as e:
|
||||
action = f"[red]Fehler: {e}[/]"
|
||||
|
||||
table.add_row(cat.config.name, repo.name, f"{size_mb:.1f}", action)
|
||||
|
||||
console.print(table)
|
||||
console.print(f"\n[green]✓ Volatile-Clean abgeschlossen[/] – {deleted} gelöscht, {skipped} übersprungen")
|
||||
|
||||
# Prune old snapshots
|
||||
if use_snapshots and not dry_run and snapshot_root.exists():
|
||||
try:
|
||||
snapshots = sorted([p for p in snapshot_root.iterdir() if p.is_dir()], key=lambda p: p.name)
|
||||
if len(snapshots) > snapshot_count:
|
||||
to_delete = snapshots[:-snapshot_count]
|
||||
for snap in to_delete:
|
||||
shutil.rmtree(snap)
|
||||
console.print(f"[dim]Alte Snapshots bereinigt: {len(to_delete)} gelöscht[/]")
|
||||
except Exception as e:
|
||||
console.print(f"[red]Fehler beim Bereinigen der Snapshots: {e}[/]")
|
||||
|
||||
console.print(f"\n[green]✓ Volatile-Clean abgeschlossen[/] – {deleted} Repositories verarbeitet")
|
||||
|
||||
|
||||
@app.command()
|
||||
|
||||
@@ -13,6 +13,8 @@ class RepoCategory(BaseModel):
|
||||
class RepoCatDefaults(BaseModel):
|
||||
dry_run: bool = True
|
||||
auto_create_directories: bool = True
|
||||
snapshots: bool = False
|
||||
snapshot_count: int = 5
|
||||
|
||||
class RepoCatConfig(BaseModel):
|
||||
base_dir: Path = Field(default=Path("~/Repositories").expanduser())
|
||||
|
||||
Reference in New Issue
Block a user