From 3d7dc32777373284fb9e8ebf8d062b5d7adda4c2 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Sun, 29 Jun 2025 15:51:15 +0700 Subject: [PATCH] Add a 'revert to previous version' action + more - Show current version - Ensure uv builds switch to python 3.13 --- qt/launcher/src/main.rs | 134 +++++++++++++++++++++++++++++++--------- 1 file changed, 105 insertions(+), 29 deletions(-) diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index 396b7b0ba..0d0b26d45 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -10,6 +10,7 @@ use std::process::Command; use std::time::SystemTime; use std::time::UNIX_EPOCH; +use anki_io::copy_file; use anki_io::copy_if_newer; use anki_io::create_dir_all; use anki_io::modified_time; @@ -29,10 +30,8 @@ use crate::platform::respawn_launcher; mod platform; -// todo: -c appearing as app name now - struct State { - has_existing_install: bool, + current_version: Option, prerelease_marker: std::path::PathBuf, uv_install_root: std::path::PathBuf, uv_cache_dir: std::path::PathBuf, @@ -46,6 +45,7 @@ struct State { dist_python_version_path: std::path::PathBuf, uv_lock_path: std::path::PathBuf, sync_complete_marker: std::path::PathBuf, + previous_version: Option, } #[derive(Debug, Clone)] @@ -83,8 +83,8 @@ fn run() -> Result<()> { let (exe_dir, resources_dir) = get_exe_and_resources_dirs()?; - let state = State { - has_existing_install: uv_install_root.join(".sync_complete").exists(), + let mut state = State { + current_version: None, prerelease_marker: uv_install_root.join("prerelease"), uv_install_root: uv_install_root.clone(), uv_cache_dir: uv_install_root.join("cache"), @@ -98,8 +98,11 @@ fn run() -> Result<()> { dist_python_version_path: resources_dir.join(".python-version"), uv_lock_path: uv_install_root.join("uv.lock"), sync_complete_marker: uv_install_root.join(".sync_complete"), + previous_version: None, }; + check_versions(&mut state); + // Check for uninstall request from Windows uninstaller if std::env::var("ANKI_LAUNCHER_UNINSTALL").is_ok() { ensure_terminal_shown()?; @@ -171,13 +174,60 @@ fn run() -> Result<()> { Ok(()) } +fn extract_aqt_version( + uv_path: &std::path::Path, + uv_install_root: &std::path::Path, +) -> Option { + let output = Command::new(uv_path) + .current_dir(uv_install_root) + .args(["pip", "show", "aqt"]) + .output() + .ok()?; + + if !output.status.success() { + return None; + } + + let stdout = String::from_utf8(output.stdout).ok()?; + for line in stdout.lines() { + if let Some(version) = line.strip_prefix("Version: ") { + return Some(version.trim().to_string()); + } + } + None +} + +fn check_versions(state: &mut State) { + // If sync_complete_marker is missing, do nothing + if !state.sync_complete_marker.exists() { + return; + } + + // Determine current version by invoking uv pip show aqt + match extract_aqt_version(&state.uv_path, &state.uv_install_root) { + Some(version) => { + state.current_version = Some(version); + } + None => { + println!("Warning: Could not determine current Anki version"); + } + } + + // Read previous version from "previous-version" file + let previous_version_path = state.uv_install_root.join("previous-version"); + if let Ok(content) = read_file(&previous_version_path) { + if let Ok(version_str) = String::from_utf8(content) { + let version = version_str.trim().to_string(); + if !version.is_empty() { + state.previous_version = Some(version); + } + } + } +} + fn main_menu_loop(state: &State) -> Result<()> { loop { - let menu_choice = get_main_menu_choice( - state.has_existing_install, - &state.prerelease_marker, - &state.no_cache_marker, - ); + let menu_choice = get_main_menu_choice(state); match menu_choice { MainMenuChoice::Quit => std::process::exit(0), @@ -229,6 +279,10 @@ fn main_menu_loop(state: &State) -> Result<()> { state.user_python_version_path.clone(), )?; + // Extract current version before syncing (but don't write to file yet) + let previous_version_to_save = + extract_aqt_version(&state.uv_path, &state.uv_install_root); + // Remove sync marker before attempting sync let _ = remove_file(&state.sync_complete_marker); @@ -266,6 +320,16 @@ fn main_menu_loop(state: &State) -> Result<()> { if matches!(&choice, MainMenuChoice::Version(VersionKind::PyOxidizer(_))) { inject_helper_addon(&state.uv_install_root)?; } + + // Now that sync succeeded, save the previous version + if let Some(current_version) = previous_version_to_save { + let previous_version_path = + state.uv_install_root.join("previous-version"); + if let Err(e) = write_file(&previous_version_path, ¤t_version) { + println!("Warning: Could not save previous version: {}", e); + } + } + break; } Err(e) => { @@ -292,32 +356,33 @@ fn write_sync_marker(sync_complete_marker: &std::path::Path) -> Result<()> { Ok(()) } -fn get_main_menu_choice( - has_existing_install: bool, - prerelease_marker: &std::path::Path, - no_cache_marker: &std::path::Path, -) -> MainMenuChoice { +fn get_main_menu_choice(state: &State) -> MainMenuChoice { loop { println!("1) Latest Anki (just press enter)"); println!("2) Choose a version"); - if has_existing_install { - println!("3) Keep existing version"); + if let Some(current_version) = &state.current_version { + println!("3) Keep existing version ({current_version})"); + } + if let Some(prev_version) = &state.previous_version { + if state.current_version.as_ref() != Some(prev_version) { + println!("4) Revert to previous version ({prev_version})"); + } } println!(); - let betas_enabled = prerelease_marker.exists(); + let betas_enabled = state.prerelease_marker.exists(); println!( - "4) Allow betas: {}", + "5) Allow betas: {}", if betas_enabled { "on" } else { "off" } ); - let cache_enabled = !no_cache_marker.exists(); + let cache_enabled = !state.no_cache_marker.exists(); println!( - "5) Cache downloads: {}", + "6) Cache downloads: {}", if cache_enabled { "on" } else { "off" } ); println!(); - println!("6) Uninstall"); - println!("7) Quit"); + println!("7) Uninstall"); + println!("8) Quit"); print!("> "); let _ = stdout().flush(); @@ -331,17 +396,28 @@ fn get_main_menu_choice( "" | "1" => MainMenuChoice::Latest, "2" => MainMenuChoice::Version(get_version_kind()), "3" => { - if has_existing_install { + if state.current_version.is_some() { MainMenuChoice::KeepExisting } else { println!("Invalid input. Please try again.\n"); continue; } } - "4" => MainMenuChoice::ToggleBetas, - "5" => MainMenuChoice::ToggleCache, - "6" => MainMenuChoice::Uninstall, - "7" => MainMenuChoice::Quit, + "4" => { + if let Some(prev_version) = &state.previous_version { + if state.current_version.as_ref() != Some(prev_version) { + if let Some(version_kind) = parse_version_kind(prev_version) { + return MainMenuChoice::Version(version_kind); + } + } + } + println!("Invalid input. Please try again.\n"); + continue; + } + "5" => MainMenuChoice::ToggleBetas, + "6" => MainMenuChoice::ToggleCache, + "7" => MainMenuChoice::Uninstall, + "8" => MainMenuChoice::Quit, _ => { println!("Invalid input. Please try again."); continue; @@ -439,7 +515,7 @@ fn update_pyproject_for_version( write_file(&user_python_version_path, "3.9")?; } VersionKind::Uv(_) => { - copy_if_newer(&dist_python_version_path, &user_python_version_path)?; + copy_file(&dist_python_version_path, &user_python_version_path)?; } } }