diff --git a/src/cli/global/sync.rs b/src/cli/global/sync.rs index c5320ba2d4..8e235cdeb3 100644 --- a/src/cli/global/sync.rs +++ b/src/cli/global/sync.rs @@ -22,8 +22,12 @@ pub async fn execute(args: Args) -> miette::Result<()> { // Prune environments that are not listed let state_change = project.prune_old_environments().await?; - // Prune broken completions - project.completions_dir.prune_old_completions()?; + #[cfg(unix)] + { + // Prune broken completions + let completions_dir = crate::global::completions::CompletionsDir::from_env().await?; + completions_dir.prune_old_completions()?; + } if state_change.has_changed() { has_changed = true; diff --git a/src/cli/global/update.rs b/src/cli/global/update.rs index 24a81739b5..dd0df7a88a 100644 --- a/src/cli/global/update.rs +++ b/src/cli/global/update.rs @@ -86,7 +86,11 @@ pub async fn execute(args: Args) -> miette::Result<()> { // prune old environments and completions let state_changes = project_original.prune_old_environments().await?; state_changes.report(); - project_original.completions_dir.prune_old_completions()?; + #[cfg(unix)] + { + let completions_dir = global::completions::CompletionsDir::from_env().await?; + completions_dir.prune_old_completions()?; + } project_original.environments().keys().cloned().collect() } }; diff --git a/src/global/common.rs b/src/global/common.rs index 6de61a239b..7519911a67 100644 --- a/src/global/common.rs +++ b/src/global/common.rs @@ -356,6 +356,7 @@ pub(crate) enum StateChange { UninstalledShortcut(String), #[allow(dead_code)] // This variant is not used on Windows AddedCompletion(String), + #[allow(dead_code)] // This variant is not used on Windows RemovedCompletion(String), } diff --git a/src/global/completions.rs b/src/global/completions.rs index ba4823b0fd..07f8c328de 100644 --- a/src/global/completions.rs +++ b/src/global/completions.rs @@ -71,7 +71,6 @@ impl CompletionsDir { #[derive(Debug, Clone)] pub struct Completion { name: String, - #[allow(dead_code)] // This member variable is not used on Windows source: PathBuf, destination: PathBuf, } @@ -85,12 +84,7 @@ impl Completion { } } - pub fn name(&self) -> &str { - &self.name - } - /// Install the shell completion - #[cfg(unix)] pub async fn install(&self) -> miette::Result> { tracing::debug!("Requested to install completion {}.", self.source.display()); @@ -107,15 +101,6 @@ impl Completion { Ok(Some(StateChange::AddedCompletion(self.name.clone()))) } - #[cfg(not(unix))] - pub async fn install(&self) -> miette::Result> { - tracing::info!( - "Symlinks are only supported on unix-like platforms. Skipping completion installation for {}.", - self.name - ); - Ok(None) - } - /// Remove the shell completion pub async fn remove(&self) -> miette::Result { tokio_fs::remove_file(&self.destination) diff --git a/src/global/mod.rs b/src/global/mod.rs index 0ee7c71349..3eb17e2361 100644 --- a/src/global/mod.rs +++ b/src/global/mod.rs @@ -1,4 +1,5 @@ pub(crate) mod common; +#[cfg(unix)] // Completions are only supported on unix-like systems pub(crate) mod completions; pub(crate) mod install; pub(crate) mod list; diff --git a/src/global/project/mod.rs b/src/global/project/mod.rs index a254c679b6..cafa2f0951 100644 --- a/src/global/project/mod.rs +++ b/src/global/project/mod.rs @@ -10,7 +10,6 @@ use crate::{ common::{ channel_url_to_prioritized_channel, expose_scripts_sync_status, find_package_records, }, - completions::{completions_sync_status, CompletionsDir}, find_executables, find_executables_for_many_records, install::{create_executable_trampolines, script_exec_mapping}, project::environment::environment_specs_in_sync, @@ -20,6 +19,7 @@ use crate::{ repodata::Repodata, rlimit::try_increase_rlimit_to_sensible, }; + use std::{ ffi::OsStr, fmt::{Debug, Formatter}, @@ -86,8 +86,6 @@ pub struct Project { pub(crate) env_root: EnvRoot, /// Binary directory pub(crate) bin_dir: BinDir, - /// Directory where shell completions are located - pub(crate) completions_dir: CompletionsDir, /// Reqwest client shared for this project. /// This is wrapped in a `OnceCell` to allow for lazy initialization. // TODO: once https://github.com/rust-lang/rust/issues/109737 is stabilized, switch to OnceLock @@ -256,12 +254,7 @@ async fn package_from_conda_meta( impl Project { /// Constructs a new instance from an internal manifest representation - pub(crate) fn from_manifest( - manifest: Manifest, - env_root: EnvRoot, - bin_dir: BinDir, - completions_dir: CompletionsDir, - ) -> Self { + pub(crate) fn from_manifest(manifest: Manifest, env_root: EnvRoot, bin_dir: BinDir) -> Self { let root = manifest .path .parent() @@ -278,7 +271,6 @@ impl Project { config, env_root, bin_dir, - completions_dir, client, repodata_gateway, concurrent_downloads_semaphore: OnceCell::new(), @@ -291,15 +283,9 @@ impl Project { content: &str, env_root: EnvRoot, bin_dir: BinDir, - completions_dir: CompletionsDir, ) -> miette::Result { let manifest = Manifest::from_str(manifest_path, content)?; - Ok(Self::from_manifest( - manifest, - env_root, - bin_dir, - completions_dir, - )) + Ok(Self::from_manifest(manifest, env_root, bin_dir)) } /// Discovers the project manifest file in path at @@ -313,7 +299,6 @@ impl Project { let bin_dir = BinDir::from_env().await?; let env_root = EnvRoot::from_env().await?; - let completions_dir = CompletionsDir::from_env().await?; if !manifest_path.exists() { tracing::debug!( @@ -328,14 +313,11 @@ impl Project { tracing::debug!( "Existing installation found. Creating global manifest from that information." ); - return Self::try_from_existing_installation( - &manifest_path, - env_root, - bin_dir, - completions_dir, - ) - .await - .wrap_err_with(|| "Failed to create global manifest from existing installation"); + return Self::try_from_existing_installation(&manifest_path, env_root, bin_dir) + .await + .wrap_err_with(|| { + "Failed to create global manifest from existing installation" + }); } else { tracing::debug!("Create an empty global manifest."); tokio_fs::File::create(&manifest_path) @@ -344,14 +326,13 @@ impl Project { } } - Self::from_path(&manifest_path, env_root, bin_dir, completions_dir) + Self::from_path(&manifest_path, env_root, bin_dir) } async fn try_from_existing_installation( manifest_path: &Path, env_root: EnvRoot, bin_dir: BinDir, - completions_dir: CompletionsDir, ) -> miette::Result { let config = Config::load(env_root.path()); @@ -406,7 +387,7 @@ impl Project { tokio_fs::write(&manifest_path, &toml) .await .into_diagnostic()?; - Self::from_str(manifest_path, &toml, env_root, bin_dir, completions_dir) + Self::from_str(manifest_path, &toml, env_root, bin_dir) } /// Get default dir for the pixi global manifest @@ -445,15 +426,9 @@ impl Project { manifest_path: &Path, env_root: EnvRoot, bin_dir: BinDir, - completions_dir: CompletionsDir, ) -> miette::Result { let manifest = Manifest::from_path(manifest_path)?; - Ok(Project::from_manifest( - manifest, - env_root, - bin_dir, - completions_dir, - )) + Ok(Project::from_manifest(manifest, env_root, bin_dir)) } /// Merge config with existing config project @@ -672,8 +647,12 @@ impl Project { ); } - // Prune old completions - self.completions_dir.prune_old_completions()?; + #[cfg(unix)] // Completions are only supported on unix-like systems + { + // Prune old completions + let completions_dir = super::completions::CompletionsDir::from_env().await?; + completions_dir.prune_old_completions()?; + } state_changes.insert_change(env_name, StateChange::RemovedEnvironment); @@ -907,30 +886,6 @@ impl Project { return Ok(false); } - tracing::debug!("Verify that the completions are in sync with the environment"); - let execs_all = self - .executables_of_all_dependencies(env_name) - .await? - .into_iter() - .map(|exec| exec.name) - .collect(); - let (completions_to_remove, completions_to_add) = completions_sync_status( - environment.exposed.clone(), - execs_all, - prefix.root(), - &self.completions_dir, - ) - .await?; - if !completions_to_remove.is_empty() || !completions_to_add.is_empty() { - tracing::debug!( - "Environment {} completions are not in sync: to_remove: {}, to_add: {}", - env_name.fancy_display(), - completions_to_remove.iter().map(|c| c.name()).join(", "), - completions_to_add.iter().map(|c| c.name()).join(", ") - ); - return Ok(false); - } - tracing::debug!("Verify that the shortcuts are in sync with the environment"); let shortcuts = environment.shortcuts.clone().unwrap_or_default(); let (shortcuts_to_remove, shortcuts_to_add) = @@ -1252,6 +1207,7 @@ impl Project { Ok(state_changes) } + #[cfg(unix)] // Completions are only supported on unix like systems pub async fn sync_completions( &self, env_name: &EnvironmentName, @@ -1270,13 +1226,15 @@ impl Project { .map(|exec| exec.name) .collect(); - let (completions_to_remove, completions_to_add) = completions_sync_status( - environment.exposed.clone(), - execs_all, - prefix.root(), - &self.completions_dir, - ) - .await?; + let completions_dir = crate::global::completions::CompletionsDir::from_env().await?; + let (completions_to_remove, completions_to_add) = + super::completions::completions_sync_status( + environment.exposed.clone(), + execs_all, + prefix.root(), + &completions_dir, + ) + .await?; for completion_to_remove in completions_to_remove { let state_change = completion_to_remove.remove().await?; @@ -1294,6 +1252,15 @@ impl Project { Ok(state_changes) } + #[cfg(not(unix))] + pub async fn sync_completions( + &self, + _env_name: &EnvironmentName, + ) -> miette::Result { + let state_changes = StateChanges::default(); + Ok(state_changes) + } + /// Returns a semaphore than can be used to limit the number of concurrent /// according to the user configuration. fn concurrent_downloads_semaphore(&self) -> Arc { @@ -1351,16 +1318,9 @@ mod tests { let manifest_path: PathBuf = FilePath().fake(); let env_root = EnvRoot::from_env().await.unwrap(); let bin_dir = BinDir::from_env().await.unwrap(); - let completions_dir = CompletionsDir::from_env().await.unwrap(); - let project = Project::from_str( - &manifest_path, - SIMPLE_MANIFEST, - env_root, - bin_dir, - completions_dir, - ) - .unwrap(); + let project = + Project::from_str(&manifest_path, SIMPLE_MANIFEST, env_root, bin_dir).unwrap(); assert_eq!(project.root, manifest_path.parent().unwrap()); } @@ -1371,13 +1331,11 @@ mod tests { let env_root = EnvRoot::from_env().await.unwrap(); let bin_dir = BinDir::from_env().await.unwrap(); - let completions_dir = CompletionsDir::from_env().await.unwrap(); // Create and write global manifest let mut file = fs::File::create(&manifest_path).unwrap(); file.write_all(SIMPLE_MANIFEST.as_bytes()).unwrap(); - let project = - Project::from_path(&manifest_path, env_root, bin_dir, completions_dir).unwrap(); + let project = Project::from_path(&manifest_path, env_root, bin_dir).unwrap(); // Canonicalize both paths let canonical_root = project.root.canonicalize().unwrap(); @@ -1392,10 +1350,9 @@ mod tests { let env_root = EnvRoot::from_env().await.unwrap(); let bin_dir = BinDir::from_env().await.unwrap(); - let completions_dir = CompletionsDir::from_env().await.unwrap(); let manifest = Manifest::from_str(&manifest_path, SIMPLE_MANIFEST).unwrap(); - let project = Project::from_manifest(manifest, env_root, bin_dir, completions_dir); + let project = Project::from_manifest(manifest, env_root, bin_dir); assert_eq!(project.root, manifest_path.parent().unwrap()); } @@ -1419,7 +1376,6 @@ mod tests { "#, EnvRoot::new(tempdir.path().to_path_buf()).unwrap(), BinDir::new(tempdir.path().to_path_buf()).unwrap(), - CompletionsDir::from_env().await.unwrap(), ) .unwrap(); @@ -1541,7 +1497,6 @@ mod tests { manifest, env_root.clone(), BinDir::new(env_root.path().parent().unwrap().to_path_buf()).unwrap(), - CompletionsDir::from_env().await.unwrap(), ); // Call the prune method with a list of environments to keep (env1 and env3) but