Skip to content

fix(upgrade): prune old backups + add --no-backup flag#398

Open
ezequielmm wants to merge 2 commits intoGentleman-Programming:mainfrom
ezequielmm:fix/upgrade-backup-prune-and-skip-flag
Open

fix(upgrade): prune old backups + add --no-backup flag#398
ezequielmm wants to merge 2 commits intoGentleman-Programming:mainfrom
ezequielmm:fix/upgrade-backup-prune-and-skip-flag

Conversation

@ezequielmm
Copy link
Copy Markdown

Summary

The gentle-ai upgrade command silently accumulates pre-upgrade backup snapshots under ~/.gentle-ai/backups/ because the upgrade executor never invokes backup.Prune. Over many upgrades this fills the disk and breaks subsequent upgrades with no space left on device errors during snapshot creation.

This PR fixes that, and adds a --no-backup opt-out for users who want to bypass the backup subsystem for a single run.

Real-world impact (the report that motivated this PR): a user accumulated 24 upgrade backups totalling ~98 GB before discovering it. Each recent backup was ~15 GB because agent config roots like ~/.gemini/antigravity/ walk into runtime data not fully covered by backupExcludeSubdirs.

Closes #185. Refs #368.

Changes

Two semantic commits, intentionally split for clean bisect/revert:

1. fix(upgrade): prune old backups after pre-upgrade snapshot

  • internal/update/upgrade/executor.go::ExecuteWithOptions now invokes backup.Prune(backupRoot, backup.DefaultRetentionCount) after the snapshot block, mirroring exactly the pattern already used by internal/cli/run.go::prepareBackupStep.Run for install/sync.
  • Pruning runs even when the snapshot itself failed — that is the recovery path when the failure was caused by accumulated backups exhausting disk space.
  • Pinned backups are unaffected (existing Prune semantics).

2. feat(upgrade): add --no-backup flag

  • New SkipBackup bool field on ExecuteOptions (zero-value preserves original behavior — no breaking change for existing callers, including the TUI dispatcher).
  • app.runUpgrade recognizes --no-backup and routes through upgrade.ExecuteWithOptions directly. When set, both snapshot creation and retention pruning are skipped (~/.gentle-ai/backups/ is left entirely untouched).
  • The upgradeExecute test seam used by tuiUpgrade is unchanged.

Test plan

  • go build ./... — clean
  • go vet ./... — clean
  • go test ./... — full suite passes (all 39 packages green)
  • Manual: existing --dry-run path unchanged
  • Manual: --no-backup parses correctly alongside --dry-run and tool filters
  • Maintainer can verify against a stuffed ~/.gentle-ai/backups/ to see retention enforcement

Backward compatibility

  • Default behavior is preserved end-to-end. Users who do nothing get the same upgrade flow plus retention enforcement that was already in place for install/sync.
  • No new exported APIs beyond ExecuteOptions.SkipBackup. No dependency changes.

Out of scope (deliberately)

The upgrade executor created a pre-upgrade backup snapshot but never
invoked backup.Prune to enforce DefaultRetentionCount. Backups
accumulated indefinitely under ~/.gentle-ai/backups/, eventually
filling the disk and breaking subsequent upgrades with "no space left
on device" errors during snapshot creation.

Real-world impact: a user accumulated 24 upgrade backups totalling
~98 GB before discovering it. The most recent backups were ~15 GB each
because they include agent config roots that are not fully covered by
backupExcludeSubdirs (e.g. arbitrary files under ~/.gemini/antigravity/).

The retention.Prune function already exists and is invoked correctly
for install/sync via internal/cli/run.go (prepareBackupStep.Run). This
patch mirrors that behavior in the upgrade path: after creating the
snapshot, prune unpinned backups beyond DefaultRetentionCount=5.
Pinned backups are never deleted.

Pruning runs even when the snapshot itself fails — that is exactly
the recovery path when the failure was caused by accumulated backups
exhausting disk space.

Refs Gentleman-Programming#368 (upgrade backup failures), relates to Gentleman-Programming#185.
Add an opt-in escape hatch for users who do not want a pre-upgrade
backup snapshot for a given run. Default behavior is unchanged
(backup remains safe-by-default).

  gentle-ai upgrade --no-backup [tool...]

When --no-backup is set, ExecuteWithOptions skips both the snapshot
creation and retention pruning of the backup directory. The backups
directory is left entirely untouched. This satisfies the use cases
discussed in Gentleman-Programming#185:

  * Users on tight disk space who want to upgrade without the backup
    walk allocating GBs of duplicate config data.
  * Users who manage their config under version control / dotfile
    managers and treat the upgrade backup as redundant.
  * Recovery flows where ~/.gentle-ai/backups/ is full or unreadable
    and the user explicitly wants to bypass it for a single run.

Implementation notes:
  * SkipBackup is a new field on ExecuteOptions; zero value preserves
    the original behavior, so all existing callers (TUI included) are
    unaffected.
  * The CLI parser in app.runUpgrade now recognizes --no-backup and
    routes through upgrade.ExecuteWithOptions directly. The TUI
    dispatcher still uses the upgradeExecute test seam unchanged.
  * No new dependencies, no new exported APIs beyond the field.

Closes Gentleman-Programming#185.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hacer backup condicional según tipo de operación

2 participants