From 7e4f5b06736f22d4c8e6a836f941121fdec0c74a Mon Sep 17 00:00:00 2001 From: Pavel Zwerschke Date: Thu, 26 Jun 2025 18:16:45 +0200 Subject: [PATCH 1/2] wip --- src/cli/command_info.rs | 29 +++++++++++++++++++++++++++++ src/cli/mod.rs | 4 ++++ 2 files changed, 33 insertions(+) diff --git a/src/cli/command_info.rs b/src/cli/command_info.rs index e0baea811d..5c0dd00933 100644 --- a/src/cli/command_info.rs +++ b/src/cli/command_info.rs @@ -1,6 +1,7 @@ use clap::CommandFactory; use is_executable::IsExecutable; use miette::{Context, IntoDiagnostic}; +use tracing::warn; use std::env; use std::path::PathBuf; @@ -17,6 +18,34 @@ pub fn find_external_subcommand(cmd: &str) -> Option { }) } +pub fn find_all_external_commands() -> Vec { + let all_search_directories = search_directories().unwrap_or_default(); + let mut all_executables_on_path = Vec::new(); + for dir in all_search_directories { + match std::fs::read_dir(dir) { + Ok(read_dir) => { + for entry in read_dir { + match entry { + Ok(entry) => { + if entry.path().is_executable() && entry.path().file_name().unwrap().to_string_lossy().starts_with("pixi-") { + all_executables_on_path.push(entry.path()); + } + } + Err(e) => { + warn!("Couldn't read entry: {}", e); + } + } + } + } + Err(e) => { + warn!("Couldn't read directory: {}", e); + } + } + } + + all_executables_on_path +} + /// Execute an external subcommand pub fn execute_external_command(args: Vec) -> miette::Result<()> { // There should be always at least one argument, the command itself. diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 855872873a..a09d601ad0 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -43,6 +43,10 @@ pub mod workspace; #[derive(Parser, Debug)] #[command( version(consts::PIXI_VERSION), + after_help = format!("{}", { + panic!("adwdw"); + std::process::id() + }), about = format!(" Pixi [version {}] - Developer Workflow and Environment Management for Multi-Platform, Language-Agnostic Workspaces. From 41951a8530c96fa9416266c419926b3dc32db125 Mon Sep 17 00:00:00 2001 From: Daniel Elsner Date: Thu, 26 Jun 2025 18:48:39 +0200 Subject: [PATCH 2/2] Add extensions output --- src/cli/command_info.rs | 31 ++++++++++++++----- src/cli/extensions.rs | 67 +++++++++++++++++++++++++++++++++++++++++ src/cli/mod.rs | 7 ++--- 3 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 src/cli/extensions.rs diff --git a/src/cli/command_info.rs b/src/cli/command_info.rs index 5c0dd00933..90c8d40a0f 100644 --- a/src/cli/command_info.rs +++ b/src/cli/command_info.rs @@ -1,9 +1,9 @@ use clap::CommandFactory; use is_executable::IsExecutable; use miette::{Context, IntoDiagnostic}; -use tracing::warn; -use std::env; use std::path::PathBuf; +use std::{collections::HashSet, env}; +use tracing::warn; use super::{Command, get_styles}; @@ -18,17 +18,28 @@ pub fn find_external_subcommand(cmd: &str) -> Option { }) } -pub fn find_all_external_commands() -> Vec { +pub fn find_all_external_commands() -> HashSet { let all_search_directories = search_directories().unwrap_or_default(); - let mut all_executables_on_path = Vec::new(); - for dir in all_search_directories { + let mut all_executables_on_path = HashSet::new(); + + for dir in &all_search_directories { + if !dir.is_dir() { + continue; + } match std::fs::read_dir(dir) { Ok(read_dir) => { for entry in read_dir { match entry { Ok(entry) => { - if entry.path().is_executable() && entry.path().file_name().unwrap().to_string_lossy().starts_with("pixi-") { - all_executables_on_path.push(entry.path()); + if entry.path().is_executable() + && entry + .path() + .file_name() + .unwrap() + .to_string_lossy() + .starts_with("pixi-") + { + all_executables_on_path.insert(entry.path()); } } Err(e) => { @@ -38,7 +49,11 @@ pub fn find_all_external_commands() -> Vec { } } Err(e) => { - warn!("Couldn't read directory: {}", e); + warn!( + "Couldn't read directory {}: {}", + dir.to_string_lossy().to_string(), + e + ); } } } diff --git a/src/cli/extensions.rs b/src/cli/extensions.rs new file mode 100644 index 0000000000..b7a4a3627d --- /dev/null +++ b/src/cli/extensions.rs @@ -0,0 +1,67 @@ +use std::collections::HashMap; + +use clap::Parser; +use itertools::Itertools; + +use crate::cli::command_info::find_all_external_commands; + +#[derive(Parser, Debug)] +#[clap(verbatim_doc_comment)] +pub struct Args; + +pub async fn execute(_args: Args) -> miette::Result<()> { + let known_extensions = HashMap::from([ + ( + "pixi-pack".to_string(), + "Pack conda environments created with pixi".to_string(), + ), + ( + "pixi-unpack".to_string(), + "Unpack conda environments packaged with pixi-pack".to_string(), + ), + ( + "pixi-diff".to_string(), + "Generate JSON diffs between pixi lockfiles".to_string(), + ), + ( + "pixi-diff-to-markdown".to_string(), + "Generate markdown summaries from pixi update".to_string(), + ), + ( + "pixi-inject".to_string(), + "Inject conda packages into an already existing conda prefix".to_string(), + ), + ( + "pixi-install-to-prefix".to_string(), + "Install pixi environments to an arbitrary prefix".to_string(), + ), + ]); + + let extensions = find_all_external_commands(); + + println!( + "{}", + console::style("Available Extensions:") + .green() + .underlined() + .bold() + ); + + for extension in extensions.iter().sorted() { + let Some(name) = extension + .as_path() + .file_stem() + .map(|p| p.to_string_lossy().to_string()) + else { + continue; + }; + print!(" {}", console::style(&name).cyan()); + if let Some(description) = known_extensions.get(&name) { + println!("\t{}", description); + } else { + println!(); + } + } + + Ok(()) +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a09d601ad0..ed8e44e162 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -19,6 +19,7 @@ pub mod command_info; pub mod completion; pub mod config; pub mod exec; +pub mod extensions; pub mod global; pub mod has_specs; pub mod info; @@ -43,10 +44,6 @@ pub mod workspace; #[derive(Parser, Debug)] #[command( version(consts::PIXI_VERSION), - after_help = format!("{}", { - panic!("adwdw"); - std::process::id() - }), about = format!(" Pixi [version {}] - Developer Workflow and Environment Management for Multi-Platform, Language-Agnostic Workspaces. @@ -140,6 +137,7 @@ pub enum Command { Config(config::Args), #[clap(visible_alias = "x")] Exec(exec::Args), + Extensions(extensions::Args), #[clap(visible_alias = "g")] Global(global::Args), Info(info::Args), @@ -300,6 +298,7 @@ pub async fn execute_command( Command::Lock(cmd) => lock::execute(cmd).await, Command::Exec(args) => exec::execute(args).await, Command::Build(args) => build::execute(args).await, + Command::Extensions(args) => extensions::execute(args).await, Command::External(args) => command_info::execute_external_command(args), } }