Add version rollback support for AppImage updates

This commit is contained in:
lashman
2026-02-27 23:38:36 +02:00
parent e7a056c75a
commit f5685e03e3
3 changed files with 76 additions and 0 deletions

View File

@@ -1744,6 +1744,24 @@ impl Database {
Ok(self.conn.last_insert_rowid())
}
// --- Version rollback ---
pub fn set_previous_version(&self, id: i64, path: Option<&str>) -> SqlResult<()> {
self.conn.execute(
"UPDATE appimages SET previous_version_path = ?2 WHERE id = ?1",
params![id, path],
)?;
Ok(())
}
pub fn get_previous_version(&self, id: i64) -> SqlResult<Option<String>> {
self.conn.query_row(
"SELECT previous_version_path FROM appimages WHERE id = ?1",
params![id],
|row| row.get(0),
)
}
// --- System modification tracking ---
pub fn register_modification(

View File

@@ -989,6 +989,56 @@ fn build_system_tab(record: &AppImageRecord, db: &Rc<Database>, toast_overlay: &
}
inner.append(&integration_group);
// Version Rollback group
if let Some(ref prev_path) = record.previous_version_path {
let prev = std::path::Path::new(prev_path);
if prev.exists() {
let rollback_group = adw::PreferencesGroup::builder()
.title("Version Rollback")
.description("A previous version is available from the last update.")
.build();
let rollback_row = adw::ActionRow::builder()
.title("Previous version available")
.subtitle(prev_path.as_str())
.build();
let rollback_btn = gtk::Button::builder()
.label("Rollback")
.valign(gtk::Align::Center)
.css_classes(["destructive-action"])
.build();
rollback_row.add_suffix(&rollback_btn);
let current_path = record.path.clone();
let prev_path_owned = prev_path.clone();
let record_id_rb = record.id;
let db_rb = db.clone();
let toast_rb = toast_overlay.clone();
rollback_btn.connect_clicked(move |btn| {
let current = std::path::Path::new(&current_path);
let prev = std::path::Path::new(&prev_path_owned);
let temp_path = current.with_extension("AppImage.rollback-tmp");
// Swap: current -> tmp, prev -> current, tmp -> prev
let result = std::fs::rename(current, &temp_path)
.and_then(|_| std::fs::rename(prev, current))
.and_then(|_| std::fs::rename(&temp_path, prev));
match result {
Ok(()) => {
db_rb.set_previous_version(record_id_rb, Some(&prev_path_owned)).ok();
toast_rb.add_toast(adw::Toast::new("Rolled back to previous version"));
btn.set_sensitive(false);
}
Err(e) => {
log::error!("Rollback failed: {}", e);
toast_rb.add_toast(adw::Toast::new("Rollback failed"));
}
}
});
rollback_group.add(&rollback_row);
inner.append(&rollback_group);
}
}
// Runtime Compatibility group
let compat_group = adw::PreferencesGroup::builder()
.title("Compatibility")

View File

@@ -184,6 +184,14 @@ fn start_update(
dialog.set_body(&success_body);
dialog.set_response_enabled("update", false);
// Save previous version path for rollback
if let Some(ref old_path) = applied.old_path_backup {
db_ref.set_previous_version(
record_id,
Some(&old_path.to_string_lossy()),
).ok();
}
// Handle old version cleanup
if let Some(old_path) = applied.old_path_backup {
handle_old_version_cleanup(&dialog, old_path);