From 540e234c96499af403582d51f13ac34357b0fb46 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Fri, 3 May 2024 15:19:02 +0200 Subject: [PATCH 1/7] wip: initial clean command addition --- src/cli/clean.rs | 151 +++++++++++++++++++++++++++++++++++++++++++++++ src/cli/mod.rs | 3 + 2 files changed, 154 insertions(+) create mode 100644 src/cli/clean.rs diff --git a/src/cli/clean.rs b/src/cli/clean.rs new file mode 100644 index 000000000..2ab833ea1 --- /dev/null +++ b/src/cli/clean.rs @@ -0,0 +1,151 @@ +/// Command to clean the parts of your system which are touched by pixi. +use crate::{config, EnvironmentName, Project}; +use std::str::FromStr; +use std::time::Duration; + +use crate::progress::{global_multi_progress, long_running_progress_style}; +use clap::Parser; +use indicatif::ProgressBar; +use itertools::Itertools; +use miette::IntoDiagnostic; + +#[derive(Parser, Debug)] +pub enum Command { + /// Add a command to the project + #[clap(name = "env")] + Environment(EnvironmentArgs), + + #[clap(name = "cache")] + Cache(CacheArgs), +} + +#[derive(Parser, Debug)] +pub struct Args { + #[command(subcommand)] + command: Option, +} +/// Clean the parts of your system which are touched by pixi. +#[derive(Parser, Debug)] +pub struct EnvironmentArgs { + /// The path to 'pixi.toml' or 'pyproject.toml' + #[arg(long)] + pub manifest_path: Option, + + #[arg(long, short)] + pub environment: Option, +} + +/// Clean the parts of your system which are touched by pixi. +#[derive(Parser, Debug)] +pub struct CacheArgs { + pub all: bool, + pub pypi: bool, + pub conda: bool, +} + +pub async fn execute(args: Args) -> miette::Result<()> { + let command = args + .command + .unwrap_or(Command::Environment(EnvironmentArgs { + manifest_path: None, + environment: None, + })); + match command { + Command::Environment(args) => { + let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; // Extract the passed in environment name. + + let explicit_environment = args + .environment + .map(|n| EnvironmentName::from_str(n.as_str())) + .transpose()? + .map(|n| { + project + .environment(&n) + .ok_or_else(|| miette::miette!("unknown environment '{n}'")) + }) + .transpose()?; + + if let Some(explicit_env) = explicit_environment { + let pb = global_multi_progress().add(ProgressBar::new_spinner()); + pb.enable_steady_tick(Duration::from_millis(100)); + pb.set_style(long_running_progress_style()); + let message = format!( + "environment: {} from {}", + explicit_env.name().fancy_display(), + explicit_env.dir().display() + ); + pb.set_message(format!( + "{} {}", + console::style("Removing").green(), + message + )); + + // Ignore errors + let _ = std::fs::remove_dir_all(explicit_env.dir()); + + pb.finish_with_message(format!( + "{} {}", + console::style("removed").green(), + message + )); + } else { + let pb = global_multi_progress().add(ProgressBar::new_spinner()); + pb.enable_steady_tick(Duration::from_millis(100)); + pb.set_style(long_running_progress_style()); + let message = format!( + "all environments in {}", + project.environments_dir().display() + ); + pb.set_message(format!( + "{} {}", + console::style("Removing").green(), + message + )); + + // Ignore errors + let _ = std::fs::remove_dir_all(project.environments_dir()).into_diagnostic(); + + pb.finish_with_message(format!( + "{} {}", + console::style("removed").green(), + message + )); + } + + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); + } + Command::Cache(args) => { + let mut dirs = vec![]; + if args.all { + dirs.push(config::get_cache_dir()?); + } else if args.pypi { + dirs.push(config::get_cache_dir()?.join("pypi")); + } else if args.conda { + dirs.push(config::get_cache_dir()?.join("pkgs")); + } + + let pb = global_multi_progress().add(ProgressBar::new_spinner()); + pb.enable_steady_tick(Duration::from_millis(100)); + pb.set_style(long_running_progress_style()); + let message = format!( + "cache from {}", + dirs.iter() + .map(|dir| dir.to_string_lossy().to_string()) + .join(", ") + ); + pb.set_message(format!( + "{} {}", + console::style("Removing").green(), + message + )); + + // Ignore errors + for dir in dirs { + let _ = std::fs::remove_dir_all(dir).into_diagnostic(); + } + pb.finish_with_message(format!("{} {}", console::style("removed").green(), message)); + } + } + + Ok(()) +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a232983ff..0634576ff 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -11,6 +11,7 @@ use tracing_subscriber::prelude::__tracing_subscriber_SubscriberExt; use tracing_subscriber::{filter::LevelFilter, util::SubscriberInitExt, EnvFilter}; pub mod add; +pub mod clean; pub mod completion; pub mod global; pub mod info; @@ -63,6 +64,7 @@ pub enum Command { Init(init::Args), #[clap(visible_alias = "a")] Add(add::Args), + Clean(clean::Args), #[clap(visible_alias = "r")] Run(run::Args), #[clap(visible_alias = "s")] @@ -231,6 +233,7 @@ pub async fn execute_command(command: Command) -> miette::Result<()> { Command::Completion(cmd) => completion::execute(cmd), Command::Init(cmd) => init::execute(cmd).await, Command::Add(cmd) => add::execute(cmd).await, + Command::Clean(cmd) => clean::execute(cmd).await, Command::Run(cmd) => run::execute(cmd).await, Command::Global(cmd) => global::execute(cmd).await, Command::Auth(cmd) => rattler::cli::auth::execute(cmd).await.into_diagnostic(), From a0d9e3b754b8c68332b089f203bc61d8d21933ac Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Sun, 5 May 2024 22:03:17 +0200 Subject: [PATCH 2/7] cleanup --- src/cli/clean.rs | 71 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/cli/clean.rs b/src/cli/clean.rs index 2ab833ea1..58f1f734d 100644 --- a/src/cli/clean.rs +++ b/src/cli/clean.rs @@ -7,7 +7,6 @@ use crate::progress::{global_multi_progress, long_running_progress_style}; use clap::Parser; use indicatif::ProgressBar; use itertools::Itertools; -use miette::IntoDiagnostic; #[derive(Parser, Debug)] pub enum Command { @@ -38,8 +37,11 @@ pub struct EnvironmentArgs { /// Clean the parts of your system which are touched by pixi. #[derive(Parser, Debug)] pub struct CacheArgs { + #[arg(long)] pub all: bool, + #[arg(long)] pub pypi: bool, + #[arg(long)] pub conda: bool, } @@ -59,9 +61,15 @@ pub async fn execute(args: Args) -> miette::Result<()> { .map(|n| EnvironmentName::from_str(n.as_str())) .transpose()? .map(|n| { - project - .environment(&n) - .ok_or_else(|| miette::miette!("unknown environment '{n}'")) + project.environment(&n).ok_or_else(|| { + miette::miette!( + "unknown environment '{n}' in {}", + project + .manifest_path() + .to_str() + .expect("expected to have a manifest_path") + ) + }) }) .transpose()?; @@ -70,10 +78,19 @@ pub async fn execute(args: Args) -> miette::Result<()> { pb.enable_steady_tick(Duration::from_millis(100)); pb.set_style(long_running_progress_style()); let message = format!( - "environment: {} from {}", + "environment: '{}' from '{}'", explicit_env.name().fancy_display(), explicit_env.dir().display() ); + + if !explicit_env.dir().exists() { + pb.finish_with_message( + console::style(format!("{} does not exist", message)) + .yellow() + .to_string(), + ); + return Ok(()); + } pb.set_message(format!( "{} {}", console::style("Removing").green(), @@ -81,7 +98,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { )); // Ignore errors - let _ = std::fs::remove_dir_all(explicit_env.dir()); + let _ = tokio::fs::remove_dir(explicit_env.dir()).await; pb.finish_with_message(format!( "{} {}", @@ -93,9 +110,19 @@ pub async fn execute(args: Args) -> miette::Result<()> { pb.enable_steady_tick(Duration::from_millis(100)); pb.set_style(long_running_progress_style()); let message = format!( - "all environments in {}", + "all environments in '{}'", project.environments_dir().display() ); + + if !project.environments_dir().exists() { + pb.finish_with_message( + console::style(format!("{} does not exist", message)) + .yellow() + .to_string(), + ); + return Ok(()); + } + pb.set_message(format!( "{} {}", console::style("Removing").green(), @@ -103,7 +130,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { )); // Ignore errors - let _ = std::fs::remove_dir_all(project.environments_dir()).into_diagnostic(); + let _ = tokio::fs::remove_dir_all(project.environments_dir()).await; pb.finish_with_message(format!( "{} {}", @@ -116,19 +143,22 @@ pub async fn execute(args: Args) -> miette::Result<()> { } Command::Cache(args) => { let mut dirs = vec![]; - if args.all { - dirs.push(config::get_cache_dir()?); - } else if args.pypi { + + if args.pypi { dirs.push(config::get_cache_dir()?.join("pypi")); - } else if args.conda { + } + if args.conda { dirs.push(config::get_cache_dir()?.join("pkgs")); } + if args.all || dirs.is_empty() { + dirs.push(config::get_cache_dir()?); + } let pb = global_multi_progress().add(ProgressBar::new_spinner()); pb.enable_steady_tick(Duration::from_millis(100)); pb.set_style(long_running_progress_style()); let message = format!( - "cache from {}", + "cache from '{}'", dirs.iter() .map(|dir| dir.to_string_lossy().to_string()) .join(", ") @@ -141,9 +171,20 @@ pub async fn execute(args: Args) -> miette::Result<()> { // Ignore errors for dir in dirs { - let _ = std::fs::remove_dir_all(dir).into_diagnostic(); + if dir.exists() { + let _ = tokio::fs::remove_dir_all(&dir).await; + pb.finish_with_message(format!( + "{} {}", + console::style("removed").green(), + message + )); + } + pb.finish_with_message( + console::style(format!("{} was already removed", message)) + .yellow() + .to_string(), + ); } - pb.finish_with_message(format!("{} {}", console::style("removed").green(), message)); } } From 543d24a751f1363bf2c5355b9b3626edf4bb2dff Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Mon, 6 May 2024 13:59:28 +0200 Subject: [PATCH 3/7] misc: simplify CLI and cleanup the code more --- src/cli/clean.rs | 206 +++++++++++++++++---------------------------- src/project/mod.rs | 8 +- 2 files changed, 83 insertions(+), 131 deletions(-) diff --git a/src/cli/clean.rs b/src/cli/clean.rs index 58f1f734d..97e5f9666 100644 --- a/src/cli/clean.rs +++ b/src/cli/clean.rs @@ -1,59 +1,55 @@ /// Command to clean the parts of your system which are touched by pixi. use crate::{config, EnvironmentName, Project}; +use std::path::PathBuf; use std::str::FromStr; use std::time::Duration; use crate::progress::{global_multi_progress, long_running_progress_style}; use clap::Parser; use indicatif::ProgressBar; -use itertools::Itertools; #[derive(Parser, Debug)] pub enum Command { - /// Add a command to the project - #[clap(name = "env")] - Environment(EnvironmentArgs), - #[clap(name = "cache")] Cache(CacheArgs), } +/// Clean the parts of your system which are touched by pixi. +/// Defaults to cleaning the environments and task cache. +/// Use the `cache` subcommand to clean the cache. #[derive(Parser, Debug)] pub struct Args { #[command(subcommand)] command: Option, -} -/// Clean the parts of your system which are touched by pixi. -#[derive(Parser, Debug)] -pub struct EnvironmentArgs { /// The path to 'pixi.toml' or 'pyproject.toml' #[arg(long)] - pub manifest_path: Option, + pub manifest_path: Option, - #[arg(long, short)] + /// The environment directory to remove. + #[arg(long, short, conflicts_with = "command")] pub environment: Option, } -/// Clean the parts of your system which are touched by pixi. +/// Clean the cache of your system which are touched by pixi. #[derive(Parser, Debug)] pub struct CacheArgs { - #[arg(long)] - pub all: bool, + /// Clean only the pypi related cache. #[arg(long)] pub pypi: bool, + + /// Clean only the conda related cache. #[arg(long)] pub conda: bool, + // TODO: Would be amazing to have a --unused flag to clean only the unused cache. + // By searching the inode count of the packages and removing based on that. + // #[arg(long)] + // pub unused: bool, } pub async fn execute(args: Args) -> miette::Result<()> { - let command = args - .command - .unwrap_or(Command::Environment(EnvironmentArgs { - manifest_path: None, - environment: None, - })); - match command { - Command::Environment(args) => { + match args.command { + Some(Command::Cache(args)) => clean_cache(args).await?, + None => { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; // Extract the passed in environment name. let explicit_environment = args @@ -74,119 +70,75 @@ pub async fn execute(args: Args) -> miette::Result<()> { .transpose()?; if let Some(explicit_env) = explicit_environment { - let pb = global_multi_progress().add(ProgressBar::new_spinner()); - pb.enable_steady_tick(Duration::from_millis(100)); - pb.set_style(long_running_progress_style()); - let message = format!( - "environment: '{}' from '{}'", - explicit_env.name().fancy_display(), - explicit_env.dir().display() - ); - - if !explicit_env.dir().exists() { - pb.finish_with_message( - console::style(format!("{} does not exist", message)) - .yellow() - .to_string(), - ); - return Ok(()); - } - pb.set_message(format!( - "{} {}", - console::style("Removing").green(), - message - )); - - // Ignore errors - let _ = tokio::fs::remove_dir(explicit_env.dir()).await; - - pb.finish_with_message(format!( - "{} {}", - console::style("removed").green(), - message - )); + remove_folder_with_progress(explicit_env.dir(), true).await?; + tracing::info!("Skipping removal of task cache and solve group environments for explicit environment '{:?}'", explicit_env.name()); } else { - let pb = global_multi_progress().add(ProgressBar::new_spinner()); - pb.enable_steady_tick(Duration::from_millis(100)); - pb.set_style(long_running_progress_style()); - let message = format!( - "all environments in '{}'", - project.environments_dir().display() - ); - - if !project.environments_dir().exists() { - pb.finish_with_message( - console::style(format!("{} does not exist", message)) - .yellow() - .to_string(), - ); - return Ok(()); - } - - pb.set_message(format!( - "{} {}", - console::style("Removing").green(), - message - )); - - // Ignore errors - let _ = tokio::fs::remove_dir_all(project.environments_dir()).await; - - pb.finish_with_message(format!( - "{} {}", - console::style("removed").green(), - message - )); + // Remove all pixi related work from the project. + remove_folder_with_progress(project.environments_dir(), true).await?; + remove_folder_with_progress(project.solve_group_environments_dir(), false).await?; + remove_folder_with_progress(project.task_cache_folder(), false).await?; } - Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()) } - Command::Cache(args) => { - let mut dirs = vec![]; + } + Ok(()) +} - if args.pypi { - dirs.push(config::get_cache_dir()?.join("pypi")); - } - if args.conda { - dirs.push(config::get_cache_dir()?.join("pkgs")); - } - if args.all || dirs.is_empty() { - dirs.push(config::get_cache_dir()?); - } +/// Clean the pixi cache folders. +async fn clean_cache(args: CacheArgs) -> miette::Result<()> { + let cache_dir = config::get_cache_dir()?; + let mut dirs = vec![]; + if args.pypi { + dirs.push(cache_dir.join("pypi")); + } + if args.conda { + dirs.push(cache_dir.join("pkgs")); + } + if dirs.is_empty() { + tracing::info!( + "No cache directories selected, so cleaning all cache directories. in {:?}", + cache_dir + ); + dirs.push(cache_dir); + } - let pb = global_multi_progress().add(ProgressBar::new_spinner()); - pb.enable_steady_tick(Duration::from_millis(100)); - pb.set_style(long_running_progress_style()); - let message = format!( - "cache from '{}'", - dirs.iter() - .map(|dir| dir.to_string_lossy().to_string()) - .join(", ") - ); - pb.set_message(format!( - "{} {}", - console::style("Removing").green(), - message - )); + // Ignore errors + for dir in dirs { + remove_folder_with_progress(dir, false).await?; + } + Ok(()) +} - // Ignore errors - for dir in dirs { - if dir.exists() { - let _ = tokio::fs::remove_dir_all(&dir).await; - pb.finish_with_message(format!( - "{} {}", - console::style("removed").green(), - message - )); - } - pb.finish_with_message( - console::style(format!("{} was already removed", message)) - .yellow() - .to_string(), - ); - } +async fn remove_folder_with_progress( + folder: PathBuf, + warning_non_existent: bool, +) -> miette::Result<()> { + if !folder.exists() { + if warning_non_existent { + eprintln!( + "{}", + console::style(format!("Folder {:?} does not exist", &folder)).yellow() + ); } + return Ok(()); } - + let pb = global_multi_progress().add(ProgressBar::new_spinner()); + pb.enable_steady_tick(Duration::from_millis(100)); + pb.set_style(long_running_progress_style()); + pb.set_message(format!( + "{} {}", + console::style("Removing").green(), + folder.clone().display() + )); + + // Ignore errors + let _ = tokio::fs::remove_dir_all(&folder).await; + + pb.finish_with_message(format!( + "{} {}", + console::style("removed").green(), + folder.display() + )); Ok(()) } diff --git a/src/project/mod.rs b/src/project/mod.rs index df819a41d..d88527ee1 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -281,17 +281,17 @@ impl Project { &self.root } - /// Returns the pixi directory + /// Returns the pixi directory of the project [consts::PIXI_DIR] pub fn pixi_dir(&self) -> PathBuf { self.root.join(consts::PIXI_DIR) } - /// Returns the environment directory + /// Returns the environment directory of the project [consts::ENVIRONMENTS_DIR] pub fn environments_dir(&self) -> PathBuf { self.pixi_dir().join(consts::ENVIRONMENTS_DIR) } - /// Returns the solve group directory + /// Returns the solve group directory of the project [consts::SOLVE_GROUP_ENVIRONMENTS_DIR] pub fn solve_group_environments_dir(&self) -> PathBuf { self.pixi_dir().join(consts::SOLVE_GROUP_ENVIRONMENTS_DIR) } @@ -301,7 +301,7 @@ impl Project { self.manifest.path.clone() } - /// Returns the path to the lock file of the project + /// Returns the path to the lock file of the project [consts::PROJECT_LOCK_FILE] pub fn lock_file_path(&self) -> PathBuf { self.root.join(consts::PROJECT_LOCK_FILE) } From 259a7ff54ff3e1c8574bef2431dfbf11cfb3453b Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Tue, 7 May 2024 16:59:31 +0200 Subject: [PATCH 4/7] fix: test and log faulty removals --- src/cli/clean.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cli/clean.rs b/src/cli/clean.rs index 97e5f9666..199a39fe7 100644 --- a/src/cli/clean.rs +++ b/src/cli/clean.rs @@ -9,6 +9,7 @@ use clap::Parser; use indicatif::ProgressBar; #[derive(Parser, Debug)] +#[clap(group(clap::ArgGroup::new("command")))] pub enum Command { #[clap(name = "cache")] Cache(CacheArgs), @@ -133,7 +134,10 @@ async fn remove_folder_with_progress( )); // Ignore errors - let _ = tokio::fs::remove_dir_all(&folder).await; + let result = tokio::fs::remove_dir_all(&folder).await; + if let Err(e) = result { + tracing::info!("Failed to remove folder {:?}: {}", folder, e); + } pb.finish_with_message(format!( "{} {}", From f5082fc330274eadfea30f6e9b646894279525a4 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Sat, 8 Jun 2024 23:52:30 +0200 Subject: [PATCH 5/7] fix: support detatched envs --- src/cli/clean.rs | 42 ++++++++++++++----- src/consts.rs | 1 + .../resolve/uv_resolution_context.rs | 4 +- src/project/mod.rs | 29 +++++++++---- 4 files changed, 56 insertions(+), 20 deletions(-) diff --git a/src/cli/clean.rs b/src/cli/clean.rs index 199a39fe7..39c5c54d3 100644 --- a/src/cli/clean.rs +++ b/src/cli/clean.rs @@ -1,5 +1,5 @@ /// Command to clean the parts of your system which are touched by pixi. -use crate::{config, EnvironmentName, Project}; +use crate::{config, consts, EnvironmentName, Project}; use std::path::PathBuf; use std::str::FromStr; use std::time::Duration; @@ -7,6 +7,7 @@ use std::time::Duration; use crate::progress::{global_multi_progress, long_running_progress_style}; use clap::Parser; use indicatif::ProgressBar; +use miette::IntoDiagnostic; #[derive(Parser, Debug)] #[clap(group(clap::ArgGroup::new("command")))] @@ -41,6 +42,10 @@ pub struct CacheArgs { /// Clean only the conda related cache. #[arg(long)] pub conda: bool, + + /// Answer yes to all questions. + #[arg(long)] + pub yes: bool, // TODO: Would be amazing to have a --unused flag to clean only the unused cache. // By searching the inode count of the packages and removing based on that. // #[arg(long)] @@ -75,6 +80,17 @@ pub async fn execute(args: Args) -> miette::Result<()> { tracing::info!("Skipping removal of task cache and solve group environments for explicit environment '{:?}'", explicit_env.name()); } else { // Remove all pixi related work from the project. + if !project.environments_dir().starts_with(project.pixi_dir()) + && project.default_environments_dir().exists() + { + remove_folder_with_progress(project.default_environments_dir(), false).await?; + remove_folder_with_progress( + project.default_solve_group_environments_dir(), + false, + ) + .await?; + remove_folder_with_progress(project.task_cache_folder(), false).await?; + } remove_folder_with_progress(project.environments_dir(), true).await?; remove_folder_with_progress(project.solve_group_environments_dir(), false).await?; remove_folder_with_progress(project.task_cache_folder(), false).await?; @@ -90,23 +106,29 @@ pub async fn execute(args: Args) -> miette::Result<()> { async fn clean_cache(args: CacheArgs) -> miette::Result<()> { let cache_dir = config::get_cache_dir()?; let mut dirs = vec![]; + if args.pypi { - dirs.push(cache_dir.join("pypi")); + dirs.push(cache_dir.join(consts::PYPI_CACHE_DIR)); } if args.conda { dirs.push(cache_dir.join("pkgs")); } + if dirs.is_empty() && (args.yes || dialoguer::Confirm::new() + .with_prompt("No cache types specified using the flags.\nDo you really want to remove all cache directories from your machine?") + .interact_opt() + .into_diagnostic()? + .unwrap_or(false)) + { + dirs.push(cache_dir); + } + if dirs.is_empty() { - tracing::info!( - "No cache directories selected, so cleaning all cache directories. in {:?}", - cache_dir - ); - dirs.push(cache_dir); + eprintln!("{}", console::style("Nothing to remove.").green()); + return Ok(()); } - // Ignore errors for dir in dirs { - remove_folder_with_progress(dir, false).await?; + remove_folder_with_progress(dir, true).await?; } Ok(()) } @@ -119,7 +141,7 @@ async fn remove_folder_with_progress( if warning_non_existent { eprintln!( "{}", - console::style(format!("Folder {:?} does not exist", &folder)).yellow() + console::style(format!("Folder {:?} was already clean.", &folder)).yellow() ); } return Ok(()); diff --git a/src/consts.rs b/src/consts.rs index c19e29104..52f5a41e4 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -14,6 +14,7 @@ pub const SOLVE_GROUP_ENVIRONMENTS_DIR: &str = "solve-group-envs"; pub const PYPI_DEPENDENCIES: &str = "pypi-dependencies"; pub const TASK_CACHE_DIR: &str = "task-cache-v0"; pub const PIXI_UV_INSTALLER: &str = "uv-pixi"; +pub const PYPI_CACHE_DIR: &str = "uv-cache"; pub const CONDA_INSTALLER: &str = "conda"; pub const ONE_TIME_MESSAGES_DIR: &str = "one-time-messages"; diff --git a/src/lock_file/resolve/uv_resolution_context.rs b/src/lock_file/resolve/uv_resolution_context.rs index d9703c612..f470b22e1 100644 --- a/src/lock_file/resolve/uv_resolution_context.rs +++ b/src/lock_file/resolve/uv_resolution_context.rs @@ -7,7 +7,7 @@ use uv_types::{HashStrategy, InFlight}; use crate::{ config::{self, get_cache_dir}, - Project, + consts, Project, }; /// Objects that are needed for resolutions which can be shared between different resolutions. @@ -25,7 +25,7 @@ pub struct UvResolutionContext { impl UvResolutionContext { pub fn from_project(project: &Project) -> miette::Result { - let uv_cache = get_cache_dir()?.join("uv-cache"); + let uv_cache = get_cache_dir()?.join(consts::PYPI_CACHE_DIR); if !uv_cache.exists() { std::fs::create_dir_all(&uv_cache) .into_diagnostic() diff --git a/src/project/mod.rs b/src/project/mod.rs index cf8229881..0a533c76c 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -323,9 +323,14 @@ impl Project { } } + /// Returns the default environment directory without interacting with config. + pub fn default_environments_dir(&self) -> PathBuf { + self.pixi_dir().join(consts::ENVIRONMENTS_DIR) + } + /// Returns the environment directory pub fn environments_dir(&self) -> PathBuf { - let default_envs_dir = self.pixi_dir().join(consts::ENVIRONMENTS_DIR); + let default_envs_dir = self.default_environments_dir(); // Early out if detached-environments is not set if self.config().detached_environments().is_false() { @@ -367,6 +372,12 @@ impl Project { default_envs_dir } + /// Returns the default solve group environments directory, without interacting with config + pub fn default_solve_group_environments_dir(&self) -> PathBuf { + self.default_environments_dir() + .join(consts::SOLVE_GROUP_ENVIRONMENTS_DIR) + } + /// Returns the solve group environments directory pub fn solve_group_environments_dir(&self) -> PathBuf { // If the detached-environments path is set, use it instead of the default @@ -374,7 +385,7 @@ impl Project { if let Some(detached_environments_path) = self.detached_environments_path() { return detached_environments_path.join(consts::SOLVE_GROUP_ENVIRONMENTS_DIR); } - self.pixi_dir().join(consts::SOLVE_GROUP_ENVIRONMENTS_DIR) + self.default_solve_group_environments_dir() } /// Returns the path to the manifest file. @@ -578,12 +589,14 @@ fn create_symlink(target_dir: &Path, symlink_dir: &Path) { symlink(target_dir, symlink_dir) .map_err(|e| { - tracing::error!( - "Failed to create symlink from '{}' to '{}': {}", - target_dir.display(), - symlink_dir.display(), - e - ) + if e.kind() != std::io::ErrorKind::AlreadyExists { + tracing::error!( + "Failed to create symlink from '{}' to '{}': {}", + target_dir.display(), + symlink_dir.display(), + e + ) + } }) .ok(); } From f3946b17c551ed1bacbc92864cd727b97bc1254f Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Sat, 8 Jun 2024 23:57:58 +0200 Subject: [PATCH 6/7] docs: add clean command to the cli docs --- docs/reference/cli.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index d86ca0928..01b711dd3 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -658,6 +658,34 @@ More information [here](../advanced/explain_info_command.md). pixi info pixi info --json --extended ``` +## `clean` + +Clean the parts of your system which are touched by pixi. +Defaults to cleaning the environments and task cache. +Use the `cache` subcommand to clean the cache + +##### Options +- `--manifest-path `: the path to [manifest file](project_configuration.md), by default it searches for one in the parent directories. + +```shell +pixi clean +``` + +### `clean cache` + +Clean the pixi cache on your system. + +##### Options +- `--pypi`: Clean the pypi cache. +- `--conda`: Clean the conda cache. +- `--yes`: Skip the confirmation prompt. + +```shell +pixi clean cache # clean all pixi caches +pixi clean cache --pypi # clean only the pypi cache +pixi clean cache --conda # clean only the conda cache +pixi clean cache --yes # skip the confirmation prompt +``` ## `upload` From eb8a27ab1962bc1061cce62edc61d55874c62cf5 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Sun, 9 Jun 2024 00:01:01 +0200 Subject: [PATCH 7/7] docs: add missing environments option --- docs/reference/cli.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/reference/cli.md b/docs/reference/cli.md index 01b711dd3..d77642adc 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -666,6 +666,7 @@ Use the `cache` subcommand to clean the cache ##### Options - `--manifest-path `: the path to [manifest file](project_configuration.md), by default it searches for one in the parent directories. +- `--environment (-e)`: The environment to clean, if none are provided all environments will be removed. ```shell pixi clean