From ddd071f7e5fd205fd013d7801c09781737704cdf Mon Sep 17 00:00:00 2001 From: ARUNAVO RAY Date: Wed, 18 Mar 2026 15:05:00 +0530 Subject: [PATCH] fix: prevent excessive disk usage from repo backups (#235) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: prevent excessive disk usage from repo backups (#234) Legacy configs with backupBeforeSync: true but no explicit backupStrategy silently resolved to "always", creating full git bundles on every sync cycle. This caused repo-backups to grow to 17GB+ for users with many repositories. Changes: - Fix resolveBackupStrategy to map backupBeforeSync: true → "on-force-push" instead of "always", so legacy configs only backup when force-push is detected - Fix config mapper to always set backupStrategy explicitly ("on-force-push") preventing the backward-compat fallback from triggering - Lower default backupRetentionCount from 20 to 5 bundles per repo - Add time-based retention (backupRetentionDays, default 30 days) alongside count-based retention, with safety net to always keep at least 1 bundle - Add "high disk usage" warning on "Always Backup" UI option - Update docs and tests to reflect new defaults and behavior * fix: preserve legacy backupBeforeSync:false on UI round-trip and expose retention days P1: mapDbToUiConfig now checks backupBeforeSync === false before defaulting backupStrategy, preventing legacy "disabled" configs from silently becoming "on-force-push" after any auto-save round-trip. P3: Added "Snapshot retention days" input field to the backup settings UI, matching the documented setting in FORCE_PUSH_PROTECTION.md. --- docs/FORCE_PUSH_PROTECTION.md | 8 +++- src/components/config/ConfigTabs.tsx | 3 +- src/components/config/GitHubConfigForm.tsx | 30 +++++++++++-- src/lib/db/schema.ts | 3 +- src/lib/gitea-enhanced.test.ts | 2 +- src/lib/repo-backup.test.ts | 4 +- src/lib/repo-backup.ts | 51 +++++++++++++++++++--- src/lib/utils/config-defaults.ts | 3 +- src/lib/utils/config-mapper.ts | 12 +++-- src/types/config.ts | 1 + 10 files changed, 94 insertions(+), 23 deletions(-) diff --git a/docs/FORCE_PUSH_PROTECTION.md b/docs/FORCE_PUSH_PROTECTION.md index f15b1d2..b1e75d5 100644 --- a/docs/FORCE_PUSH_PROTECTION.md +++ b/docs/FORCE_PUSH_PROTECTION.md @@ -78,7 +78,11 @@ These appear when any non-disabled strategy is selected: ### Snapshot Retention Count -How many backup snapshots to keep per repository. Oldest snapshots are deleted when this limit is exceeded. Default: **20**. +How many backup snapshots to keep per repository. Oldest snapshots are deleted when this limit is exceeded. Default: **5**. + +### Snapshot Retention Days + +Maximum age (in days) for backup snapshots. Bundles older than this are deleted during retention enforcement, though at least one bundle is always kept. Set to `0` to disable time-based retention. Default: **30**. ### Snapshot Directory @@ -96,7 +100,7 @@ The old `backupBeforeSync` boolean is still recognized: | Old Setting | New Equivalent | |---|---| -| `backupBeforeSync: true` | `backupStrategy: "always"` | +| `backupBeforeSync: true` | `backupStrategy: "on-force-push"` | | `backupBeforeSync: false` | `backupStrategy: "disabled"` | | Neither set | `backupStrategy: "on-force-push"` (new default) | diff --git a/src/components/config/ConfigTabs.tsx b/src/components/config/ConfigTabs.tsx index 5fb7af9..9eb9877 100644 --- a/src/components/config/ConfigTabs.tsx +++ b/src/components/config/ConfigTabs.tsx @@ -51,7 +51,8 @@ export function ConfigTabs() { starredReposMode: 'dedicated-org', preserveOrgStructure: false, backupStrategy: "on-force-push", - backupRetentionCount: 20, + backupRetentionCount: 5, + backupRetentionDays: 30, backupDirectory: 'data/repo-backups', blockSyncOnBackupFailure: true, }, diff --git a/src/components/config/GitHubConfigForm.tsx b/src/components/config/GitHubConfigForm.tsx index c0cb441..d24bc4f 100644 --- a/src/components/config/GitHubConfigForm.tsx +++ b/src/components/config/GitHubConfigForm.tsx @@ -234,7 +234,7 @@ export function GitHubConfigForm({ { value: "always", label: "Always Backup", - desc: "Snapshot before every sync", + desc: "Snapshot before every sync (high disk usage)", }, { value: "on-force-push", @@ -272,7 +272,7 @@ export function GitHubConfigForm({ {(giteaConfig.backupStrategy ?? "on-force-push") !== "disabled" && ( <> -
+
+
+ + { + const newConfig = { + ...giteaConfig, + backupRetentionDays: Math.max(0, Number.parseInt(e.target.value, 10) || 0), + }; + setGiteaConfig(newConfig); + if (onGiteaAutoSave) onGiteaAutoSave(newConfig); + }} + className="w-full rounded-md border border-input bg-background px-3 py-2 text-sm shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" + /> +

0 = no time-based limit

+