From 843f0caf667f68281a3ce4f63a0b6d25805bc1fa Mon Sep 17 00:00:00 2001 From: Christian Guinard <28689358+christiangnrd@users.noreply.github.com> Date: Fri, 17 Jan 2025 15:36:30 -0400 Subject: [PATCH] Add `--prune-orphans` option in gc This is to remove julia folders in the juliaup folder whose path is not referenced in juliaup.json This will typically clean up direct download version that were somehow not removed. --- src/bin/juliaup.rs | 5 +++- src/cli.rs | 2 ++ src/command_gc.rs | 4 +-- src/command_remove.rs | 2 +- src/command_update.rs | 2 +- src/operations.rs | 65 +++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/bin/juliaup.rs b/src/bin/juliaup.rs index 33835acf..8bac947b 100644 --- a/src/bin/juliaup.rs +++ b/src/bin/juliaup.rs @@ -96,7 +96,10 @@ fn main() -> Result<()> { Juliaup::Remove { channel } => run_command_remove(&channel, &paths), Juliaup::Status {} => run_command_status(&paths), Juliaup::Update { channel } => run_command_update(channel, &paths), - Juliaup::Gc { prune_linked } => run_command_gc(prune_linked, &paths), + Juliaup::Gc { + prune_linked, + prune_orphans, + } => run_command_gc(prune_linked, prune_orphans, &paths), Juliaup::Link { channel, file, diff --git a/src/cli.rs b/src/cli.rs index 5d7bc76a..e3325a05 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -36,6 +36,8 @@ pub enum Juliaup { Gc { #[clap(long)] prune_linked: bool, + #[clap(long)] + prune_orphans: bool, }, #[clap(subcommand, name = "config")] /// Juliaup configuration diff --git a/src/command_gc.rs b/src/command_gc.rs index 19c12dd2..b9ee232e 100644 --- a/src/command_gc.rs +++ b/src/command_gc.rs @@ -3,11 +3,11 @@ use crate::global_paths::GlobalPaths; use crate::operations::garbage_collect_versions; use anyhow::{Context, Result}; -pub fn run_command_gc(prune_linked: bool, paths: &GlobalPaths) -> Result<()> { +pub fn run_command_gc(prune_linked: bool, prune_orphans: bool, paths: &GlobalPaths) -> Result<()> { let mut config_file = load_mut_config_db(paths) .with_context(|| "`gc` command failed to load configuration data.")?; - garbage_collect_versions(prune_linked, &mut config_file.data, paths)?; + garbage_collect_versions(prune_linked, prune_orphans, &mut config_file.data, paths)?; save_config_db(&mut config_file) .with_context(|| "`gc` command failed to save configuration db.")?; diff --git a/src/command_remove.rs b/src/command_remove.rs index e1ca12a0..5b42cde4 100644 --- a/src/command_remove.rs +++ b/src/command_remove.rs @@ -63,7 +63,7 @@ pub fn run_command_remove(channel: &str, paths: &GlobalPaths) -> Result<()> { #[cfg(not(windows))] remove_symlink(&format!("julia-{}", channel))?; - garbage_collect_versions(false, &mut config_file.data, paths)?; + garbage_collect_versions(false, false, &mut config_file.data, paths)?; save_config_db(&mut config_file).with_context(|| { format!( diff --git a/src/command_update.rs b/src/command_update.rs index d3cd3eb8..af0d6410 100644 --- a/src/command_update.rs +++ b/src/command_update.rs @@ -147,7 +147,7 @@ pub fn run_command_update(channel: Option, paths: &GlobalPaths) -> Resul } }; - garbage_collect_versions(false, &mut config_file.data, paths)?; + garbage_collect_versions(false, false, &mut config_file.data, paths)?; save_config_db(&mut config_file) .with_context(|| "`update` command failed to save configuration db.")?; diff --git a/src/operations.rs b/src/operations.rs index 343f071d..7fcd4957 100644 --- a/src/operations.rs +++ b/src/operations.rs @@ -22,6 +22,7 @@ use console::style; use flate2::read::GzDecoder; use indicatif::{ProgressBar, ProgressStyle}; use indoc::formatdoc; +use itertools::Itertools; use regex::Regex; use semver::Version; #[cfg(not(windows))] @@ -687,6 +688,7 @@ pub fn install_non_db_version( pub fn garbage_collect_versions( prune_linked: bool, + prune_orphans: bool, config_data: &mut JuliaupConfig, paths: &GlobalPaths, ) -> Result<()> { @@ -712,8 +714,65 @@ pub fn garbage_collect_versions( config_data.installed_versions.remove(&version_to_delete); } + // GC for DirectDownloadChannel channels + let jl_dirs: Vec<_> = std::fs::read_dir(&paths.juliauphome)? + .into_iter() + .filter_map_ok(|r| { + if r.path().is_dir() { + Some(r.path()) + } else { + None + } + }) + .filter_map_ok(|r| { + let dirname = r.file_name()?.to_str()?; + if dirname.starts_with("julia-") { + Some(dirname.to_owned()) + } else { + None + } + }) + .filter(|r| r.is_ok()) + .map(|r| r.unwrap()) // This is safe, since we only have the Ok variants + .collect(); + + if prune_orphans { + for jl_dir in jl_dirs { + if config_data + .installed_channels + .iter() + .all(|(_, detail)| match &detail { + JuliaupConfigChannel::SystemChannel { version } => { + let channel_path = &config_data.installed_versions[version] + .path + .replace("./", ""); + *channel_path != jl_dir + } + JuliaupConfigChannel::DirectDownloadChannel { + path, + url: _, + local_etag: _, + server_etag: _, + version: _, + } => { + let channel_path = path.replace("./", ""); + channel_path != jl_dir + } + JuliaupConfigChannel::LinkedChannel { + command: _, + args: _, + } => true, + }) + { + if std::fs::remove_dir_all(paths.juliauphome.join(&jl_dir)).is_err() { + eprintln!("WARNING: Failed to delete {}. You can try to delete at a later point by running `juliaup gc`.", &jl_dir) + } + } + } + } + if prune_linked { - let mut channels_to_uninstall: Vec = Vec::new(); + let mut linked_channels_to_uninstall: Vec = Vec::new(); for (installed_channel, detail) in &config_data.installed_channels { match &detail { JuliaupConfigChannel::LinkedChannel { @@ -721,13 +780,13 @@ pub fn garbage_collect_versions( args: _, } => { if !is_valid_julia_path(&PathBuf::from(cmd)) { - channels_to_uninstall.push(installed_channel.clone()); + linked_channels_to_uninstall.push(installed_channel.clone()); } } _ => (), } } - for channel in channels_to_uninstall { + for channel in linked_channels_to_uninstall { remove_symlink(&format!("julia-{}", &channel))?; config_data.installed_channels.remove(&channel); }