Skip to content

Commit

Permalink
feat: update --json (prefix-dev#1446)
Browse files Browse the repository at this point in the history
Fixes prefix-dev#1437

Schema:

```json
{
  "environment": {
    "linux-64": [
    {
        "name": "zstd",
        "before": {
          "version": "1.5.5",
          "build": "hfc55251_0"
        },
        "after": {
          "version": "1.5.6",
          "build": "ha6fb4c9_0"
        },
        "type": "conda"
        "explicit": false
      }
    ]
  }
}
```

@pavelzw Let me know what you think!

I changed to an array instead of a dictionary because a package changing
from conda to pypi is represented as a removal followed by an insert.

The `explicit` flag is only present if its value is `true`. 

@ruben-arts This is missing a test! Waiting for Pavel to give me the
green light first.

### TODO:
- [x] Documentation, add it to the reference/cli.md
- [x] Full record spec in json output
  • Loading branch information
baszalmstra authored and jjjermiah committed Jun 11, 2024
1 parent 49d0bff commit b95d571
Show file tree
Hide file tree
Showing 15 changed files with 348 additions and 68 deletions.
71 changes: 55 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 16 additions & 14 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ clap = { version = "4.5.4", default-features = false, features = [
] }
clap-verbosity-flag = "2.2.0"
clap_complete = "4.5.2"
concat-idents = "1.1.5"
console = { version = "0.15.8", features = ["windows-console-colors"] }
crossbeam-channel = "0.5.12"
csv = "1.3.0"
Expand Down Expand Up @@ -74,18 +75,19 @@ miette = { version = "7.2.0", features = [
minijinja = { version = "1.0.20", features = ["builtins"] }
once_cell = "1.19.0"
parking_lot = "0.12.2"

pep440_rs = { git = "https://github.com/astral-sh/uv", rev = "65b17f6e81125064ea04c5cfef685516ab660cf5" }
pep508_rs = { git = "https://github.com/astral-sh/uv", rev = "65b17f6e81125064ea04c5cfef685516ab660cf5" }
platform-tags = { git = "https://github.com/astral-sh/uv", rev = "65b17f6e81125064ea04c5cfef685516ab660cf5" }
pypi-types = { git = "https://github.com/astral-sh/uv", rev = "65b17f6e81125064ea04c5cfef685516ab660cf5" }
pyproject-toml = "0.11.0"
rattler = { version = "0.26.1", default-features = false, features = [
rattler = { version = "0.26.4", default-features = false, features = [
"cli-tools",
"indicatif",
] }
rattler_conda_types = { version = "0.25.0", default-features = false }
rattler_conda_types = { version = "0.25.2", default-features = false }
rattler_digest = { version = "0.19.4", default-features = false }
rattler_lock = { version = "0.22.9", default-features = false }
rattler_lock = { version = "0.22.12", default-features = false }
rattler_networking = { version = "0.20.8", default-features = false }
rattler_repodata_gateway = { version = "0.20.3", default-features = false, features = [
"sparse",
Expand All @@ -94,7 +96,7 @@ rattler_repodata_gateway = { version = "0.20.3", default-features = false, featu
rattler_shell = { version = "0.20.6", default-features = false, features = [
"sysinfo",
] }
rattler_solve = { version = "0.23.2", default-features = false, features = [
rattler_solve = { version = "0.24.2", default-features = false, features = [
"resolvo",
] }

Expand Down Expand Up @@ -194,16 +196,16 @@ pep508_rs = { git = "https://github.com/astral-sh/uv", rev = "65b17f6e81125064ea
#rattler_shell = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_solve = { git = "https://github.com/mamba-org/rattler", branch = "main" }
#rattler_virtual_packages = { git = "https://github.com/mamba-org/rattler", branch = "main" }
# rattler_conda_types = { path = "../rattler-1/crates/rattler_conda_types" }
# rattler_digest = { path = "../rattler-1/crates/rattler_digest" }
# rattler_networking = { path = "../rattler-1/crates/rattler_networking" }
# rattler_repodata_gateway = { path = "../rattler-1/crates/rattler_repodata_gateway" }
# rattler_shell = { path = "../rattler-1/crates/rattler_shell" }
# rattler_solve = { path = "../rattler-1/crates/rattler_solve" }
# rattler_virtual_packages = { path = "../rattler-1/crates/rattler_virtual_packages" }
# rattler_lock = { path = "../rattler-1/crates/rattler_lock" }
# rattler_package_streaming = { path = "../rattler-1/crates/rattler_package_streaming" }
# rattler = { path = "../rattler-1/crates/rattler" }
#rattler_conda_types = { path = "../rattler/crates/rattler_conda_types" }
#rattler_digest = { path = "../rattler/crates/rattler_digest" }
#rattler_networking = { path = "../rattler/crates/rattler_networking" }
#rattler_repodata_gateway = { path = "../rattler/crates/rattler_repodata_gateway" }
#rattler_shell = { path = "../rattler/crates/rattler_shell" }
#rattler_solve = { path = "../rattler/crates/rattler_solve" }
#rattler_virtual_packages = { path = "../rattler/crates/rattler_virtual_packages" }
#rattler_lock = { path = "../rattler/crates/rattler_lock" }
#rattler_package_streaming = { path = "../rattler/crates/rattler_package_streaming" }
#rattler = { path = "../rattler/crates/rattler" }
# Change these lines if you want a patched version of uv
# [patch.'https://github.com/astral-sh/uv']
# pep440_rs = { git = "https://github.com/astral-sh/uv", rev = "65b17f6e81125064ea04c5cfef685516ab660cf5" }
Expand Down
1 change: 1 addition & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ It will only update the lock file if the dependencies in the [manifest file](pro
- `--platform <PLATFORM> (-p)`: The platform for which the dependencies should be updated.
- `--dry-run (-n)`: Only show the changes that would be made, without actually updating the lock file or environment.
- `--no-install`: Don't install the (solve) environment needed for solving pypi-dependencies.
- `--json`: Output the changes in json format.

```shell
pixi update numpy
Expand Down
49 changes: 28 additions & 21 deletions src/cli/shell_hook.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::{collections::HashMap, default::Default, path::PathBuf};

use clap::Parser;
use miette::IntoDiagnostic;
use rattler_shell::{
Expand All @@ -6,24 +8,24 @@ use rattler_shell::{
};
use serde::Serialize;
use serde_json;
use std::collections::HashMap;
use std::{default::Default, path::PathBuf};

use crate::config::ConfigCliPrompt;
use crate::{
activation::get_activator,
cli::LockFileUsageArgs,
config::ConfigCliPrompt,
environment::get_up_to_date_prefix,
project::{has_features::HasFeatures, Environment},
Project,
};

/// Print the pixi environment activation script.
///
/// You can source the script to activate the environment without needing pixi itself.
/// You can source the script to activate the environment without needing pixi
/// itself.
#[derive(Parser, Debug)]
pub struct Args {
/// Sets the shell, options: [`bash`, `zsh`, `xonsh`, `cmd`, `powershell`, `fish`, `nushell`]
/// Sets the shell, options: [`bash`, `zsh`, `xonsh`, `cmd`,
/// `powershell`, `fish`, `nushell`]
#[arg(short, long)]
shell: Option<ShellEnum>,

Expand Down Expand Up @@ -56,7 +58,8 @@ async fn generate_activation_script(
shell: Option<ShellEnum>,
environment: &Environment<'_>,
) -> miette::Result<String> {
// Get shell from the arguments or from the current process or use default if all fails
// Get shell from the arguments or from the current process or use default if
// all fails
let shell = shell.unwrap_or_else(|| {
ShellEnum::from_parent_process()
.unwrap_or_else(|| ShellEnum::from_env().unwrap_or_default())
Expand All @@ -68,7 +71,8 @@ async fn generate_activation_script(
.ok()
.map(|p| std::env::split_paths(&p).collect::<Vec<_>>());

// If we are in a conda environment, we need to deactivate it before activating the host / build prefix
// If we are in a conda environment, we need to deactivate it before activating
// the host / build prefix
let conda_prefix = std::env::var("CONDA_PREFIX").ok().map(|p| p.into());
let result = activator
.activation(ActivationVariables {
Expand All @@ -81,8 +85,8 @@ async fn generate_activation_script(
result.script.contents().into_diagnostic()
}

/// Generates a JSON object describing the changes to the shell environment when activating
/// the provided pixi environment.
/// Generates a JSON object describing the changes to the shell environment when
/// activating the provided pixi environment.
async fn generate_environment_json(environment: &Environment<'_>) -> miette::Result<String> {
let environment_variables = environment.project().get_env_variables(environment).await?;
let shell_env = ShellEnv {
Expand Down Expand Up @@ -112,17 +116,21 @@ pub async fn execute(args: Args) -> miette::Result<()> {

#[cfg(test)]
mod tests {
use rattler_conda_types::Platform;
use rattler_shell::shell::{Bash, CmdExe, Fish, NuShell, PowerShell, Shell, Xonsh, Zsh};

use super::*;
use rattler_shell::shell::{Bash, CmdExe, Fish, NuShell, PowerShell, Xonsh, Zsh};

#[tokio::test]
async fn test_shell_hook() {
let default_shell = rattler_shell::shell::ShellEnum::default();
let path_var_name = default_shell.path_var(&Platform::current());
let project = Project::discover().unwrap();
let environment = project.default_environment();
let script = generate_activation_script(Some(ShellEnum::Bash(Bash)), &environment)
.await
.unwrap();
assert!(script.contains("export PATH="));
assert!(script.contains(&format!("export {path_var_name}=")));
assert!(script.contains("export CONDA_PREFIX="));

let script = generate_activation_script(
Expand All @@ -131,50 +139,49 @@ mod tests {
)
.await
.unwrap();
assert!(script.contains("${Env:PATH}"));
assert!(script.contains(&format!("${{Env:{path_var_name}}}")));
assert!(script.contains("${Env:CONDA_PREFIX}"));

let script = generate_activation_script(Some(ShellEnum::Zsh(Zsh)), &environment)
.await
.unwrap();
assert!(script.contains("export PATH="));
assert!(script.contains(&format!("export {path_var_name}=")));
assert!(script.contains("export CONDA_PREFIX="));

let script = generate_activation_script(Some(ShellEnum::Fish(Fish)), &environment)
.await
.unwrap();
assert!(script.contains("set -gx PATH "));
assert!(script.contains(&format!("set -gx {path_var_name} ")));
assert!(script.contains("set -gx CONDA_PREFIX "));

let script = generate_activation_script(Some(ShellEnum::Xonsh(Xonsh)), &environment)
.await
.unwrap();
assert!(script.contains("$PATH = "));
assert!(script.contains(&format!("${path_var_name} = ")));
assert!(script.contains("$CONDA_PREFIX = "));

let script = generate_activation_script(Some(ShellEnum::CmdExe(CmdExe)), &environment)
.await
.unwrap();
assert!(script.contains("@SET \"PATH="));
assert!(script.contains(&format!("@SET \"{path_var_name}=")));
assert!(script.contains("@SET \"CONDA_PREFIX="));

let script = generate_activation_script(Some(ShellEnum::NuShell(NuShell)), &environment)
.await
.unwrap();
assert!(script.contains("$env.PATH = "));
assert!(script.contains(&format!("$env.{path_var_name} = ")));
assert!(script.contains("$env.CONDA_PREFIX = "));
}

#[tokio::test]
async fn test_environment_json() {
let default_shell = rattler_shell::shell::ShellEnum::default();
let path_var_name = default_shell.path_var(&Platform::current());
let project = Project::discover().unwrap();
let environment = project.default_environment();
let json_env = generate_environment_json(&environment).await.unwrap();
assert!(json_env.contains("\"PIXI_ENVIRONMENT_NAME\":\"default\""));
assert!(json_env.contains("\"CONDA_PREFIX\":"));
#[cfg(not(target_os = "windows"))]
assert!(json_env.contains("\"PATH\":"));
#[cfg(target_os = "windows")]
assert!(json_env.contains("\"Path\":"));
assert!(json_env.contains(&format!("\"{path_var_name}\":")));
}
}
Loading

0 comments on commit b95d571

Please sign in to comment.