From bfcb81e90b2835137adebe1614bf3f2cc607f491 Mon Sep 17 00:00:00 2001 From: Maks Snegov Date: Sun, 11 Oct 2026 12:11:56 -0700 Subject: [PATCH] Add CLAUDE.md --- AGENTS.md | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 1 + 2 files changed, 135 insertions(+) create mode 100644 AGENTS.md create mode 120000 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..d51b6eb --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,134 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working +with code in this repository. + +## Project Overview + +cura-te-ipsum is a space-efficient incremental backup utility for Linux and +macOS. It creates hardlink-based snapshots similar to Time Machine/rsnapshot, +where unchanged files share inodes between backups to minimize storage. + +## Development Commands + +### Environment Setup +Project uses `uv` for Python version and dependency management: +```bash +uv sync --all-extras --dev +``` + +### Testing +```bash +uv run pytest # run all tests +uv run pytest -v # verbose output +uv run pytest tests/test_fs.py # run specific test file +``` + +### Installation +```bash +uv pip install . # install from source +``` + +### Running the Tool +```bash +cura-te-ipsum -b /path/to/backups /path/to/source +cura-te-ipsum -b /backups /src1 /src2 --dry-run --verbose +``` + +## Architecture + +### Core Modules + +**curateipsum/backup.py**: Main backup orchestration +- `initiate_backup()`: Main entry point - coordinates hardlinking from + previous backup and rsync from sources +- `cleanup_old_backups()`: Implements grandfather-father-son retention + (daily/weekly/monthly/yearly) with configurable thresholds +- Lock management (`set_backups_lock`, `release_backups_lock`): Prevents + concurrent backups using PID-based lock file +- Backup markers (`set_backup_marker`, `_is_backup`): Tracks completed vs + incomplete backups + +**curateipsum/fs.py**: Low-level filesystem operations +- `rsync()`: Pure Python rsync implementation - syncs source to destination + with delete support +- `rsync_ext()`: Wrapper for external rsync command +- `hardlink_dir()`: Recursively hardlinks entire directory tree (Python or + external cp) +- `nest_hardlink()`: Hardlinks individual files to `.backup_delta` directory + preserving structure +- `scantree()`: Recursive directory iterator +- `copy_direntry()`, `update_direntry()`, `rm_direntry()`: DirEntry + manipulation utilities + +**curateipsum/cli.py**: Command-line interface +- Argument parsing and validation +- Platform check (Linux/macOS only) +- Logging setup and execution flow + +### Key Data Structures + +**PseudoDirEntry**: Emulates os.DirEntry for paths that don't exist yet or +for consistency in handling both real and pseudo entries. Used extensively +in backup operations. + +**Actions enum**: Tracks filesystem changes during rsync (NOTHING, DELETE, +REWRITE, UPDATE_TIME, UPDATE_PERM, UPDATE_OWNER, CREATE, ERROR). + +### Backup Process Flow + +1. Acquire lock file (`.backups_lock`) with PID +2. Clean up old backups per retention policy +3. Hardlink entire previous backup directory to new timestamped directory +4. Remove backup markers from hardlinked copy +5. Delete `.backup_delta` directory from hardlinked copy +6. Rsync each source to new backup (overwrites changed files, breaking + hardlinks) +7. For each changed file, hardlink to `.backup_delta` directory +8. Set backup marker (`.backup_finished_TIMESTAMP`) +9. Release lock + +### Important Implementation Details + +**Retention policy**: `cleanup_old_backups()` implements threshold-based +retention where each threshold is inclusive. For example, `keep_weekly=1` +on Thursday keeps one backup from current week AND one from previous week. + +**Delta tracking**: Changed files are hardlinked to `.backup_delta/` within +each backup to track what changed in that specific backup snapshot. + +**Hardlink breakage**: When rsync overwrites a hardlinked file, it breaks +the hardlink, creating a new inode. This is how changed files get tracked +while unchanged files continue sharing inodes. + +**External tools**: Can use external rsync (--external-rsync) or GNU cp/gcp +for hardlinking (--external-hardlink) instead of Python implementations. +macOS requires `gcp` (GNU cp) for external hardlinking. + +**Dry run behavior**: Creates backup directory and performs operations but +removes directory at end without setting backup marker. + +## Platform Specifics + +- Linux: Uses standard `cp` command +- macOS: Requires `gcp` (GNU coreutils) for external hardlinking +- Only these platforms are supported (checked in cli.py) + +## Testing + +Tests use unittest.TestCase with mock.patch for datetime manipulation. +Focus is on backup cleanup logic with various retention scenarios. +Tests create temporary backup directories and verify correct backups are +kept/removed. + +## Version Management + +Version is automatically derived from git tags using `setuptools_scm`. +The version file `curateipsum/_version.py` is auto-generated during build +and should not be committed to version control. + +To create a new release, tag the commit: +```bash +git tag -a v0.0.4 -m "Release 0.0.4" +git push origin v0.0.4 +``` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file