Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/reference/cli/pixi/tree.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ pixi tree [OPTIONS] [REGEX]
: The environment to list packages for. Defaults to the default environment
- <a id="arg---invert" href="#arg---invert">`--invert (-i)`</a>
: Invert tree and show what depends on given package in the regex argument
- <a id="arg---json" href="#arg---json">`--json`</a>
: Show a JSON representation of the dependency tree

## Update Options
- <a id="arg---no-lockfile-update" href="#arg---no-lockfile-update">`--no-lockfile-update`</a>
Expand Down
67 changes: 64 additions & 3 deletions src/cli/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use pixi_manifest::FeaturesExt;
use rattler_conda_types::Platform;
use rattler_lock::LockedPackageRef;
use regex::Regex;
use serde::{Deserialize, Serialize};

use crate::{
WorkspaceLocator, cli::cli_config::WorkspaceConfig, lock_file::UpdateLockFileOptions,
Expand All @@ -36,7 +37,7 @@ use super::cli_config::LockFileUpdateConfig;
))]
pub struct Args {
/// List only packages matching a regular expression
#[arg()]
#[arg(conflicts_with = "json")]
pub regex: Option<String>,

/// The platform to list packages for. Defaults to the current platform.
Expand All @@ -55,8 +56,12 @@ pub struct Args {
pub lock_file_update_config: LockFileUpdateConfig,

/// Invert tree and show what depends on given package in the regex argument
#[arg(short, long, requires = "regex")]
#[arg(short, long, requires = "regex", conflicts_with = "json")]
pub invert: bool,

/// Show a JSON representation of the dependency tree
#[arg(long)]
pub json: bool,
}

struct Symbols {
Expand Down Expand Up @@ -108,7 +113,9 @@ pub async fn execute(args: Args) -> miette::Result<()> {

let stdout = std::io::stdout();
let mut handle = stdout.lock();
if args.invert {
if args.json {
print_json_dependency_tree(&mut handle, &dep_map, &direct_deps)?;
} else if args.invert {
print_inverted_dependency_tree(
&mut handle,
&invert_dep_map(&dep_map),
Expand All @@ -123,6 +130,60 @@ pub async fn execute(args: Args) -> miette::Result<()> {
Ok(())
}

#[derive(Debug, Clone, Serialize, Deserialize)]
struct DependencyTreeNode {
name: String,
version: String,
tags: Vec<String>,
dependencies: Vec<DependencyTreeNode>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nice to jam a little more info in here, we've had people ask for the size and I would say it misses the type (conda or pypi)

}

fn package_to_tree_node(
package: &Package,
dep_map: &HashMap<String, Package>,
) -> DependencyTreeNode {
let dependencies = package
.dependencies
.iter()
.filter_map(|dep_name| dep_map.get(dep_name))
.map(|dep| package_to_tree_node(dep, dep_map))
.collect();

DependencyTreeNode {
name: package.name.clone(),
version: package.version.clone(),
tags: Vec::new(),
dependencies,
}
}

/// Print a JSON representation of the dependency tree
fn print_json_dependency_tree(
handle: &mut StdoutLock,
dep_map: &HashMap<String, Package>,
direct_deps: &HashSet<String>,
) -> miette::Result<()> {
let dependencies: Vec<DependencyTreeNode> = direct_deps
.iter()
.filter_map(|pkg_name| dep_map.get(pkg_name))
.map(|pkg| package_to_tree_node(pkg, dep_map))
.collect();
let json = serde_json::to_string_pretty(&dependencies)
.into_diagnostic()
.wrap_err("Failed to serialize dependency tree to JSON")?;
writeln!(handle, "{}", json)
.map_err(|e| {
if e.kind() == std::io::ErrorKind::BrokenPipe {
// Exit gracefully
std::process::exit(0);
} else {
e
}
})
Comment on lines +175 to +182
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We dont do this anywhere else, can you explain why this is needed?

.into_diagnostic()
.wrap_err("Failed to serialize dependency tree to JSON")
}

/// Filter and print an inverted dependency tree
fn print_inverted_dependency_tree(
handle: &mut StdoutLock,
Expand Down
Loading