From e76c8726a2db397d3c20a30a4d90cd0b40098d4e Mon Sep 17 00:00:00 2001 From: Marco D'Aleo Date: Wed, 12 Nov 2025 17:34:27 +0000 Subject: [PATCH] Add automatic prune of trash older than RESRM_TRASH_LIFE value, update README, package version bump --- README.md | 22 +++++++++++++++++----- pyproject.toml | 2 +- src/resrm/core.py | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 90ff59a..d939c4d 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,23 @@ It moves files to a per-user _trash_ instead of permanently deleting them, while ## Features -- Move files and directories to a **trash folder** instead of permanent deletion -- Restore deleted files by **short ID or exact basename** -- Empty trash safely -- Supports `-r`, `-f`, `-i`, `--skip-trash` options -- Works with `sudo` for root-owned files +- Move files and directories to a **Trash folder** instead of permanent deletion +- Restore deleted files by **short ID or exact basename** +- Empty trash safely +- Supports `-r`, `-f`, `-i`, `--skip-trash` options +- Works with `sudo` for root-owned files +- Automatically prunes Trash entries older than `$RESRM_TRASH_LIFE` days (default **7**, minimum **1**) + > Note: if you need immediate deletion, use the regular `rm` command instead. + +--- + +## Configuration + +To control how long trashed files are kept, add this line to your shell configuration (e.g. `~/.bashrc`): + +```bash +export RESRM_TRASH_LIFE=10 +``` --- diff --git a/pyproject.toml b/pyproject.toml index 76ce6dd..bb5878e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "resrm" -version = "0.2.1" +version = "0.3.0" description = "drop-in replacement for rm with undo/restore built-in." authors = ["Marco D'Aleo "] license = "GPL-3.0-or-later" diff --git a/src/resrm/core.py b/src/resrm/core.py index 5201a9a..96312d4 100644 --- a/src/resrm/core.py +++ b/src/resrm/core.py @@ -53,6 +53,42 @@ def get_trash_paths() -> tuple[Path, Path]: TRASH_DIR, META_FILE = get_trash_paths() DATEFMT = "%Y-%m-%d %H:%M" +def prune_old_trash(): + """Remove trash entries older than RESRM_TRASH_LIFE days (default 7).""" + try: + life_days = int(os.environ.get("RESRM_TRASH_LIFE", "7")) + except ValueError: + life_days = 7 + + if life_days < 1: + life_days = 1 + + cutoff = datetime.datetime.now() - datetime.timedelta(days=life_days) + removed = 0 + + for entry in list(meta): # make copy since we'll modify meta + try: + ts = datetime.datetime.fromisoformat(entry["timestamp"]) + except Exception: + continue # skip malformed entries + + if ts < cutoff: + f = TRASH_DIR / entry["id"] + try: + if f.exists(): + if f.is_dir(): + shutil.rmtree(f, ignore_errors=True) + else: + f.unlink(missing_ok=True) + meta.remove(entry) + removed += 1 + except Exception as e: + print(f"Failed to prune {f}: {e}") + + if removed > 0: + save_meta(meta) + print(f"Pruned {removed} trash entr{'y' if removed == 1 else 'ies'} older than {life_days} da{'y' if life_days == 1 else 'ys'}.") + def load_meta() -> List[Dict]: if META_FILE.exists(): try: @@ -304,6 +340,7 @@ def move_to_trash(path: Path, interactive: bool, force: bool, skip_trash: bool): def main(argv: Optional[List[str]] = None): if argv is None: argv = sys.argv[1:] + prune_old_trash() parser = argparse.ArgumentParser(add_help=False) parser.add_argument("paths", nargs="*", help="files to remove") parser.add_argument("-r", action="store_true", help="recursive") -- 2.49.1