# Rotation Logic ## How the Grandfather-Father-Son Scheme Works ### The Naming Convention Backups follow this pattern: ``` solaria-deep-backup_YYYYMMDD_HHMMSS.tar.gz ``` Examples: - `solaria-deep-backup_20260215_020001.tar.gz` (Feb 15, 2026, 2:00 AM) - `solaria-deep-backup_20260208_020001.tar.gz` (Feb 8, 2026, 2:00 AM) - `solaria-deep-backup_20260101_000001.tar.gz` (Jan 1, 2026, 12:00 AM) ### Extraction From the filename, we extract: - **Date**: `YYYYMMDD` (first 8 digits) - **Week**: `YYYYW##` (ISO week number) - **Month**: `YYYYMM` (first 6 digits) - **Year**: `YYYY` (first 4 digits) ### The Algorithm ``` 1. SCAN Find all backup files in the directory Extract date components for each 2. CATEGORIZE For each backup, mark its: - Daily bucket (exact date) - Weekly bucket (ISO week) - Monthly bucket (year-month) - Yearly bucket (year) 3. ROTATE For each bucket type: a. Identify all items b. Sort by date (newest first) c. Keep N newest d. Mark others for deletion 4. VERIFY Dry-run first (report only) --apply flag executes deletions ``` ### Code Structure ```bash #!/bin/bash # solaria-backup-rotate.sh # Configuration MAX_DAILIES=7 MAX_WEEKLIES=4 MAX_MONTHLIES=12 MAX_YEARLIES=5 # Phase 1: Yearly # Keep newest backup of each year # Delete older year backups beyond MAX_YEARLIES # Phase 2: Monthly # For each month, keep only newest # Delete older month backups # Phase 3: Weekly # For each week, keep only newest # Delete older week backups # Phase 4: Daily # Keep last 7 days # (Already handled by daily rotation logic) ``` ### Safety Features 1. **Dry-Run Default**: Running without `--apply` shows what would happen 2. **Chunk Awareness**: Handles split archives (`.part_XXX`) 3. **Error Handling**: Fails safely on invalid directories 4. **Logging**: All operations logged with timestamps ### Example Output ``` [2026-02-15 06:57:47] ROTATE: Starting rotation in: /path/to/backups [2026-02-15 06:57:47] ROTATE: Found: solaria-deep-backup_20260215_060000.tar.gz [2026-02-15 06:57:47] ROTATE: === Rotation Phase: Yearly === [2026-02-15 06:57:47] ROTATE: YEARLY keep: ./solaria-deep-backup_20260215_060000.tar.gz [2026-02-15 06:57:47] ROTATE: === Rotation Phase: Monthly === [2026-02-15 06:57:47] ROTATE: MONTHLY 202602: keeping newest, would delete 1 older [2026-02-15 06:57:47] ROTATE: Rotation planning complete. Run with --apply to execute. ``` ### What Gets Deleted Suppose we have: ``` 2026-02-15 (newest daily) 2026-02-14 2026-02-13 2026-02-12 2026-02-11 2026-02-10 2026-02-09 2026-02-08 (DELETE - beyond 7 days) 2026-02-02 (DELETE - older week, not newest) 2026-02-01 (DELETE - older month, not newest) 2025-01-15 (DELETE - older year, not newest) ``` After rotation: ``` 2026-02-15 (kept - newest daily) 2026-02-14 (kept - within 7 days) 2026-02-13 (kept - within 7 days) 2026-02-12 (kept - within 7 days) 2026-02-11 (kept - within 7 days) 2026-02-10 (kept - within 7 days) 2026-02-09 (kept - within 7 days) 2026-02-08 (DELETED) 2026-02-02 (DELETED) 2026-02-01 (DELETED) 2025-01-15 (DELETED) ``` ## Testing The test suite covers: - Empty directories - Single backups - Multiple backups same day - Mixed ages (daily rotation) - Weekly promotion logic - Monthly promotion logic - Yearly promotion logic - Month/year boundary cases - Chunk file handling - Error conditions ```bash cd tests ./test-backup-rotate.sh # Expected: 13 passed, 0 failed ``` ## Integration ### With solaria-deep-backup.sh ```bash # At end of backup script /path/to/solaria-backup-rotate.sh /path/to/backups --apply ``` ### With Cron ```bash # Daily at 2 AM 0 2 * * * /path/to/solaria-backup-rotate.sh /path/to/backups --apply >> /var/log/backup.log 2>&1 ```