From 436a1d78bc984f3ab0e4edb97079a5592696a2d2 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 7 Jul 2025 23:41:56 +0700 Subject: [PATCH] On first run, automatically download the latest version --- qt/launcher/src/main.rs | 196 ++++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 96 deletions(-) diff --git a/qt/launcher/src/main.rs b/qt/launcher/src/main.rs index 2eb455988..8e52e25af 100644 --- a/qt/launcher/src/main.rs +++ b/qt/launcher/src/main.rs @@ -113,7 +113,6 @@ fn run() -> Result<()> { // Create install directory and copy project files in create_dir_all(&state.uv_install_root)?; - copy_if_newer(&state.dist_pyproject_path, &state.user_pyproject_path)?; copy_if_newer( &state.dist_python_version_path, @@ -145,7 +144,12 @@ fn run() -> Result<()> { check_versions(&mut state); - main_menu_loop(&state)?; + let first_run = !state.uv_install_root.join(".venv").exists(); + if first_run { + handle_version_install_or_update(&state, MainMenuChoice::Latest)?; + } else { + main_menu_loop(&state)?; + } // Write marker file to indicate we've completed the sync process write_sync_marker(&state.sync_complete_marker)?; @@ -225,6 +229,98 @@ fn check_versions(state: &mut State) { } } +fn handle_version_install_or_update(state: &State, choice: MainMenuChoice) -> Result<()> { + update_pyproject_for_version( + choice.clone(), + state.dist_pyproject_path.clone(), + state.user_pyproject_path.clone(), + state.dist_python_version_path.clone(), + 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); + + println!("\x1B[1mUpdating Anki...\x1B[0m\n"); + + let python_version_trimmed = if state.user_python_version_path.exists() { + let python_version = read_file(&state.user_python_version_path)?; + let python_version_str = + String::from_utf8(python_version).context("Invalid UTF-8 in .python-version")?; + Some(python_version_str.trim().to_string()) + } else { + None + }; + + // `uv sync` sometimes does not pull in Python automatically + // This might be system/platform specific and/or a uv bug. + let mut command = Command::new(&state.uv_path); + command + .current_dir(&state.uv_install_root) + .env("UV_CACHE_DIR", &state.uv_cache_dir) + .env("UV_PYTHON_INSTALL_DIR", &state.uv_python_install_dir) + .args(["python", "install", "--managed-python"]); + + // Add python version if .python-version file exists + if let Some(version) = &python_version_trimmed { + command.args([version]); + } + + command.ensure_success().context("Python install failed")?; + + // Sync the venv + let mut command = Command::new(&state.uv_path); + command + .current_dir(&state.uv_install_root) + .env("UV_CACHE_DIR", &state.uv_cache_dir) + .env("UV_PYTHON_INSTALL_DIR", &state.uv_python_install_dir) + .args(["sync", "--upgrade", "--managed-python"]); + + // Add python version if .python-version file exists + if let Some(version) = &python_version_trimmed { + command.args(["--python", version]); + } + + // Set UV_PRERELEASE=allow if beta mode is enabled + if state.prerelease_marker.exists() { + command.env("UV_PRERELEASE", "allow"); + } + + if state.no_cache_marker.exists() { + command.env("UV_NO_CACHE", "1"); + } + + match command.ensure_success() { + Ok(_) => { + // Sync succeeded + 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}"); + } + } + + Ok(()) + } + Err(e) => { + // If sync fails due to things like a missing wheel on pypi, + // we need to remove the lockfile or uv will cache the bad result. + let _ = remove_file(&state.uv_lock_path); + println!("Install failed: {e:#}"); + println!(); + Err(e.into()) + } + } +} + fn main_menu_loop(state: &State) -> Result<()> { loop { let menu_choice = get_main_menu_choice(state)?; @@ -270,102 +366,10 @@ fn main_menu_loop(state: &State) -> Result<()> { continue; } choice @ (MainMenuChoice::Latest | MainMenuChoice::Version(_)) => { - // For other choices, update project files and sync - update_pyproject_for_version( - choice.clone(), - state.dist_pyproject_path.clone(), - state.user_pyproject_path.clone(), - state.dist_python_version_path.clone(), - 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); - - println!("\x1B[1mUpdating Anki...\x1B[0m\n"); - - let python_version_trimmed = if state.user_python_version_path.exists() { - let python_version = read_file(&state.user_python_version_path)?; - let python_version_str = String::from_utf8(python_version) - .context("Invalid UTF-8 in .python-version")?; - Some(python_version_str.trim().to_string()) - } else { - None - }; - - // `uv sync` does not pull in Python automatically, unlike `uv run`. - // This might be system/platform specific and/or a uv bug. - let mut command = Command::new(&state.uv_path); - command - .current_dir(&state.uv_install_root) - .env("UV_CACHE_DIR", &state.uv_cache_dir) - .env("UV_PYTHON_INSTALL_DIR", &state.uv_python_install_dir) - .args(["python", "install", "--managed-python"]); - - // Add python version if .python-version file exists - if let Some(version) = &python_version_trimmed { - command.args([version]); - } - - if let Err(e) = command.ensure_success() { - println!("Python install failed: {e:#}"); - println!(); + if handle_version_install_or_update(state, choice.clone()).is_err() { continue; } - - // Sync the venv - let mut command = Command::new(&state.uv_path); - command - .current_dir(&state.uv_install_root) - .env("UV_CACHE_DIR", &state.uv_cache_dir) - .env("UV_PYTHON_INSTALL_DIR", &state.uv_python_install_dir) - .args(["sync", "--upgrade", "--managed-python"]); - - // Add python version if .python-version file exists - if let Some(version) = &python_version_trimmed { - command.args(["--python", version]); - } - - // Set UV_PRERELEASE=allow if beta mode is enabled - if state.prerelease_marker.exists() { - command.env("UV_PRERELEASE", "allow"); - } - - if state.no_cache_marker.exists() { - command.env("UV_NO_CACHE", "1"); - } - - match command.ensure_success() { - Ok(_) => { - // Sync succeeded - 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) => { - // If sync fails due to things like a missing wheel on pypi, - // we need to remove the lockfile or uv will cache the bad result. - let _ = remove_file(&state.uv_lock_path); - println!("Install failed: {e:#}"); - println!(); - continue; - } - } + break; } } }