Skip to content

Commit b822852

Browse files
authored
Limit how many local backups are kept (#5318)
1 parent 0cad29d commit b822852

10 files changed

Lines changed: 773 additions & 71 deletions

File tree

src/Languages/lang_en.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -984,5 +984,12 @@
984984
"Run whenever UniGetUI next starts": "Run whenever UniGetUI next starts",
985985
"Run within {0}": "Run within {0}",
986986
"Choose the days and hours when updates are checked and installed": "Choose the days and hours when updates are checked and installed",
987-
"The last attempt failed on {0}, see the log for details": "The last attempt failed on {0}, see the log for details"
987+
"The last attempt failed on {0}, see the log for details": "The last attempt failed on {0}, see the log for details",
988+
"Maximum number of local backups to keep": "Maximum number of local backups to keep",
989+
"The oldest backups are deleted once this limit is exceeded.": "The oldest backups are deleted once this limit is exceeded.",
990+
"Keep a separate file for each backup": "Keep a separate file for each backup",
991+
"Backup file names include the date and time each backup was created.": "Backup file names include the date and time each backup was created.",
992+
"Keep all backups": "Keep all backups",
993+
"Keep the last {0} backups": "Keep the last {0} backups",
994+
"Custom maximum number of backups": "Custom maximum number of backups"
988995
}

src/UniGetUI.Avalonia/Infrastructure/SettingsSearchIndex.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,9 @@ private sealed record Entry(string Title, string[] Keywords, Type PageType, stri
109109
new("Perform a local backup now", ["local backup now"], typeof(Backup), "BackupNowButton_LOCAL"),
110110
new("Change backup output directory", ["backup directory", "backup folder"], typeof(Backup), "BackupDirectoryCard"),
111111
new("Set a custom backup file name", ["backup file name"], typeof(Backup), "BackupFileNameCard"),
112-
new("Add a timestamp to the backup file names", ["backup timestamp"], typeof(Backup), "BackupTimestampCard"),
112+
new("Keep a separate file for each backup", ["backup timestamp", "timestamped file names", "separate backups"], typeof(Backup), "BackupTimestampCard"),
113+
new("Maximum number of local backups to keep", ["backup retention", "backup limit", "delete old backups"], typeof(Backup), "MaxBackupCountCard"),
114+
new("Custom maximum number of backups", ["custom backup count"], typeof(Backup), "MaxBackupCountCustomInput"),
113115
new("Package backup", ["backup"], typeof(Backup), null),
114116

115117
// ── Administrator ────────────────────────────────────────────────────

src/UniGetUI.Avalonia/ViewModels/Pages/SettingsPages/BackupViewModel.cs

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,23 @@ public partial class BackupViewModel : ViewModelBase, IDisposable
3232
];
3333

3434
/* ── Local backup ── */
35-
[ObservableProperty] private bool _isLocalBackupEnabled;
35+
[ObservableProperty, NotifyPropertyChangedFor(nameof(IsBackupRetentionAvailable))] private bool _isLocalBackupEnabled;
36+
[ObservableProperty, NotifyPropertyChangedFor(nameof(IsBackupRetentionAvailable))] private bool _isBackupTimestampingEnabled;
37+
[ObservableProperty] private bool _isCustomBackupCountSelected;
3638
[ObservableProperty] private string _backupDirectoryLabel = "";
3739

40+
public bool IsBackupRetentionAvailable => IsLocalBackupEnabled && IsBackupTimestampingEnabled;
41+
42+
public IReadOnlyList<(string Name, string Value)> MaxBackupCountItems { get; } =
43+
[
44+
(CoreTools.Translate("Keep all backups"), "0"),
45+
(CoreTools.Translate("Keep the last {0} backups", 5), "5"),
46+
(CoreTools.Translate("Keep the last {0} backups", 10), "10"),
47+
(CoreTools.Translate("Keep the last {0} backups", 25), "25"),
48+
(CoreTools.Translate("Keep the last {0} backups", 50), "50"),
49+
(CoreTools.Translate("Custom..."), "custom"),
50+
];
51+
3852
/* ── Cloud backup ── */
3953
[ObservableProperty] private bool _isLoggedIn;
4054
[ObservableProperty] private bool _isLoginButtonEnabled = true;
@@ -55,6 +69,7 @@ public BackupViewModel()
5569
{
5670
_lifetimeToken = _lifetimeCancellation.Token;
5771
_isLocalBackupEnabled = CoreSettings.Get(CoreSettings.K.EnablePackageBackup_LOCAL);
72+
_isBackupTimestampingEnabled = CoreSettings.Get(CoreSettings.K.EnableBackupTimestamping);
5873
RefreshDirectoryLabel();
5974

6075
GitHubAuthService.AuthStatusChanged += OnAuthStatusChanged;
@@ -87,6 +102,13 @@ private void EnableLocalBackupChanged()
87102
RestartRequired?.Invoke(this, EventArgs.Empty);
88103
}
89104

105+
[RelayCommand]
106+
private void EnableBackupTimestampingChanged()
107+
{
108+
if (IsDisposed) return;
109+
IsBackupTimestampingEnabled = CoreSettings.Get(CoreSettings.K.EnableBackupTimestamping);
110+
}
111+
90112
private void RefreshDirectoryLabel()
91113
{
92114
if (IsDisposed) return;
@@ -120,28 +142,9 @@ public static async Task<bool> DoLocalBackupStatic()
120142
?? [];
121143
string backupContents = await PackageBundlesPage.CreateBundle(packages);
122144

123-
string dirName = CoreSettings.GetValue(CoreSettings.K.ChangeBackupOutputDirectory);
124-
if (string.IsNullOrEmpty(dirName))
125-
dirName = CoreData.UniGetUI_DefaultBackupDirectory;
126-
127-
if (!Directory.Exists(dirName))
128-
Directory.CreateDirectory(dirName);
129-
130-
string fileName = CoreSettings.GetValue(CoreSettings.K.ChangeBackupFileName);
131-
if (string.IsNullOrEmpty(fileName))
132-
fileName = CoreTools.Translate(
133-
"{pcName} installed packages",
134-
new Dictionary<string, object?> { { "pcName", Environment.MachineName } }
135-
);
136-
137-
if (CoreSettings.Get(CoreSettings.K.EnableBackupTimestamping))
138-
fileName += " " + DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss");
139-
140-
fileName += ".ubundle";
141-
142-
string filePath = Path.Combine(dirName, fileName);
143-
await File.WriteAllTextAsync(filePath, backupContents);
145+
string filePath = await LocalBackupManager.SaveBackupAsync(backupContents);
144146
Logger.ImportantInfo("Local backup saved to " + filePath);
147+
await Task.Run(LocalBackupManager.ApplyRetentionLimit);
145148
return true;
146149
}
147150
catch (Exception ex)

src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Backup.axaml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,31 @@
187187

188188
<settings:CheckboxCard x:Name="BackupTimestampCard"
189189
SettingName="EnableBackupTimestamping"
190-
Text="{t:Translate Add a timestamp to the backup file names}"
191-
CornerRadius="0,0,8,8"
190+
Text="{t:Translate Keep a separate file for each backup}"
191+
Description="{t:Translate Text='Backup file names include the date and time each backup was created.'}"
192+
StateChangedCommand="{Binding EnableBackupTimestampingChangedCommand}"
193+
CornerRadius="0"
192194
BorderThickness="1,0,1,1"
193195
IsEnabled="{Binding IsLocalBackupEnabled}"/>
194196

197+
<settings:ComboboxCard x:Name="MaxBackupCountCard"
198+
SettingName="MaxLocalBackupCount"
199+
Text="{t:Translate Maximum number of local backups to keep}"
200+
Description="{t:Translate Text='The oldest backups are deleted once this limit is exceeded.'}"
201+
BorderThickness="1,0,1,1"
202+
CornerRadius="0,0,8,8"
203+
IsEnabled="{Binding IsBackupRetentionAvailable}"/>
204+
205+
<settings:TextboxCard x:Name="MaxBackupCountCustomInput"
206+
SettingName="MaxLocalBackupCountCustom"
207+
Text="{t:Translate Custom maximum number of backups}"
208+
Placeholder="{t:Translate e.g. 10}"
209+
IsNumericOnly="True"
210+
IsVisible="{Binding IsCustomBackupCountSelected}"
211+
BorderThickness="1,0,1,1"
212+
CornerRadius="0,0,8,8"
213+
IsEnabled="{Binding IsBackupRetentionAvailable}"/>
214+
195215
<settings:TranslatedTextBlock Text="Related settings"
196216
FontSize="14"
197217
FontWeight="SemiBold"

src/UniGetUI.Avalonia/Views/Pages/SettingsPages/Backup.axaml.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using Avalonia.Controls;
22
using UniGetUI.Avalonia.ViewModels.Pages.SettingsPages;
33
using UniGetUI.Core.Tools;
4+
using CoreSettings = global::UniGetUI.Core.SettingsEngine.Settings;
5+
using CornerRadius = global::Avalonia.CornerRadius;
46

57
namespace UniGetUI.Avalonia.Views.Pages.SettingsPages;
68

@@ -22,6 +24,22 @@ public Backup()
2224

2325
_viewModel.RestartRequired += OnRestartRequired;
2426
_viewModel.NavigationRequested += OnNavigationRequested;
27+
28+
foreach (var (name, val) in _viewModel.MaxBackupCountItems)
29+
MaxBackupCountCard.AddItem(name, val, false);
30+
MaxBackupCountCard.ShowAddedItems();
31+
32+
MaxBackupCountCard.ValueChanged += (_, _) => RefreshMaxBackupCountLayout();
33+
RefreshMaxBackupCountLayout();
34+
}
35+
36+
private void RefreshMaxBackupCountLayout()
37+
{
38+
bool isCustom = CoreSettings.GetValue(CoreSettings.K.MaxLocalBackupCount) == "custom";
39+
_viewModel.IsCustomBackupCountSelected = isCustom;
40+
MaxBackupCountCard.CornerRadius = isCustom
41+
? new CornerRadius(0)
42+
: new CornerRadius(0, 0, 8, 8);
2543
}
2644

2745
private void OnRestartRequired(object? sender, EventArgs e) => RestartRequired?.Invoke(sender, e);

src/UniGetUI.Core.Settings/SettingsEngine_ImportExport.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ string entry in Directory.EnumerateFiles(CoreData.UniGetUIUserConfigurationDirec
4040
"MaintenanceTaskLastRun.json",
4141
"MaintenanceTaskLastFailure.json",
4242
"MaintenanceSchedules.invalid",
43+
"KnownLocalBackupNames.json",
4344
"TelemetryClientToken",
4445
"CurrentSessionToken",
4546
}.Contains(Path.GetFileName(entry))
@@ -68,6 +69,7 @@ public static void ImportFromString_JSON(string jsonContent)
6869
"MaintenanceTaskLastRun.json",
6970
"MaintenanceTaskLastFailure.json",
7071
"MaintenanceSchedules.invalid",
72+
"KnownLocalBackupNames.json",
7173
"TelemetryClientToken",
7274
"CurrentSessionToken",
7375
}.Contains(entry.Key)

src/UniGetUI.Core.Settings/SettingsEngine_Names.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ public enum K
6060
WindowGeometry,
6161
IgnoredUpdatesWindowSize,
6262
ChangeBackupFileName,
63+
MaxLocalBackupCount,
64+
MaxLocalBackupCountCustom,
65+
KnownLocalBackupNames,
6366
CurrentSessionToken,
6467
IgnoredPackageUpdates,
6568
DisabledManagers,
@@ -184,6 +187,9 @@ public static string ResolveKey(K key)
184187
K.WindowGeometry => "WindowGeometry",
185188
K.IgnoredUpdatesWindowSize => "IgnoredUpdatesWindowSize",
186189
K.ChangeBackupFileName => "ChangeBackupFileName",
190+
K.MaxLocalBackupCount => "MaxLocalBackupCount",
191+
K.MaxLocalBackupCountCustom => "MaxLocalBackupCountCustom",
192+
K.KnownLocalBackupNames => "KnownLocalBackupNames",
187193
K.CurrentSessionToken => "CurrentSessionToken",
188194
K.IgnoredPackageUpdates => "IgnoredPackageUpdates",
189195
K.DisabledManagers => "DisabledManagers",

0 commit comments

Comments
 (0)