From 526486c40a98a0f05b216ad4b2bc13669754cbec Mon Sep 17 00:00:00 2001 From: Alex Kerney Date: Fri, 5 Apr 2024 10:43:21 -0400 Subject: [PATCH] feat: Persistant shell manifests (#1080) Co-authored-by: Ruben Arts --- src/activation.rs | 1 + src/cli/add.rs | 1 + src/cli/info.rs | 4 +++ src/cli/install.rs | 5 ++-- src/cli/list.rs | 6 ++--- src/cli/remove.rs | 1 + src/cli/run.rs | 1 + src/cli/search.rs | 1 + src/cli/shell.rs | 4 +-- src/cli/task.rs | 4 +-- src/cli/tree.rs | 5 ++-- src/project/manifest/environment.rs | 16 ++++++++++++ src/project/mod.rs | 40 +++++++++++++++++++++++++++-- 13 files changed, 73 insertions(+), 16 deletions(-) diff --git a/src/activation.rs b/src/activation.rs index 07d2e443d..f30c92c57 100644 --- a/src/activation.rs +++ b/src/activation.rs @@ -40,6 +40,7 @@ impl Project { version.to_string() }), ), + (String::from("PIXI_IN_SHELL"), String::from("1")), ]); if let Ok(exe_path) = std::env::current_exe() { diff --git a/src/cli/add.rs b/src/cli/add.rs index 6653733b2..1d039a56a 100644 --- a/src/cli/add.rs +++ b/src/cli/add.rs @@ -212,6 +212,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { ) } + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } diff --git a/src/cli/info.rs b/src/cli/info.rs index 5f42e3bac..33deae6d9 100644 --- a/src/cli/info.rs +++ b/src/cli/info.rs @@ -379,9 +379,13 @@ pub async fn execute(args: Args) -> miette::Result<()> { if args.json { println!("{}", serde_json::to_string_pretty(&info).into_diagnostic()?); + + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } else { println!("{}", info); + + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } } diff --git a/src/cli/install.rs b/src/cli/install.rs index f55672efe..eaedb7400 100644 --- a/src/cli/install.rs +++ b/src/cli/install.rs @@ -26,9 +26,7 @@ pub struct Args { pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?.with_cli_config(args.config); - let environment_name = args - .environment - .map_or_else(|| EnvironmentName::Default, EnvironmentName::Named); + let environment_name = EnvironmentName::from_arg_or_env_var(args.environment); let environment = project .environment(&environment_name) .ok_or_else(|| miette::miette!("unknown environment '{environment_name}'"))?; @@ -47,5 +45,6 @@ pub async fn execute(args: Args) -> miette::Result<()> { console::style(console::Emoji("✔ ", "")).green(), project.root().display() ); + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } diff --git a/src/cli/list.rs b/src/cli/list.rs index fc361a36c..1f4519edc 100644 --- a/src/cli/list.rs +++ b/src/cli/list.rs @@ -105,9 +105,7 @@ where pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; - let environment_name = args - .environment - .map_or_else(|| EnvironmentName::Default, EnvironmentName::Named); + let environment_name = EnvironmentName::from_arg_or_env_var(args.environment); let environment = project .environment(&environment_name) .ok_or_else(|| miette::miette!("unknown environment '{environment_name}'"))?; @@ -199,6 +197,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { "{}No packages found.", console::style(console::Emoji("✘ ", "")).red(), ); + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); return Ok(()); } @@ -211,6 +210,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { print_packages_as_table(&packages_to_output).expect("an io error occurred"); } + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } diff --git a/src/cli/remove.rs b/src/cli/remove.rs index 46217f99b..74c372565 100644 --- a/src/cli/remove.rs +++ b/src/cli/remove.rs @@ -134,5 +134,6 @@ pub async fn execute(args: Args) -> miette::Result<()> { ) .await?; + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } diff --git a/src/cli/run.rs b/src/cli/run.rs index cbbd3f667..1931bb518 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -189,6 +189,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { .into_diagnostic()?; } + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } diff --git a/src/cli/search.rs b/src/cli/search.rs index 4ea3681f5..551b4efeb 100644 --- a/src/cli/search.rs +++ b/src/cli/search.rs @@ -173,6 +173,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { search_exact_package(package_name, repo_data, stdout).await?; } + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } diff --git a/src/cli/shell.rs b/src/cli/shell.rs index 7ccdf9666..c4249aaac 100644 --- a/src/cli/shell.rs +++ b/src/cli/shell.rs @@ -202,9 +202,7 @@ async fn start_nu_shell( pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?.with_cli_config(args.config); - let environment_name = args - .environment - .map_or_else(|| EnvironmentName::Default, EnvironmentName::Named); + let environment_name = EnvironmentName::from_arg_or_env_var(args.environment); let environment = project .environment(&environment_name) .ok_or_else(|| miette::miette!("unknown environment '{environment_name}'"))?; diff --git a/src/cli/task.rs b/src/cli/task.rs index 4be312bc5..83e5e6aa2 100644 --- a/src/cli/task.rs +++ b/src/cli/task.rs @@ -6,7 +6,6 @@ use itertools::Itertools; use miette::miette; use rattler_conda_types::Platform; use std::path::PathBuf; -use std::str::FromStr; use toml_edit::{Array, Item, Table, Value}; #[derive(Parser, Debug)] @@ -253,7 +252,7 @@ pub fn execute(args: Args) -> miette::Result<()> { ); } Operation::List(args) => { - let env = EnvironmentName::from_str(args.environment.as_deref().unwrap_or("default"))?; + let env = EnvironmentName::from_arg_or_env_var(args.environment); let tasks = project .environment(&env) .ok_or(miette!("Environment `{}` not found in project", env))? @@ -280,6 +279,7 @@ pub fn execute(args: Args) -> miette::Result<()> { } }; + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } diff --git a/src/cli/tree.rs b/src/cli/tree.rs index 9c37367b7..585b8ae50 100644 --- a/src/cli/tree.rs +++ b/src/cli/tree.rs @@ -70,9 +70,7 @@ static UTF8_SYMBOLS: Symbols = Symbols { pub async fn execute(args: Args) -> miette::Result<()> { let project = Project::load_or_else_discover(args.manifest_path.as_deref())?; - let environment_name = args - .environment - .map_or_else(|| EnvironmentName::Default, EnvironmentName::Named); + let environment_name = EnvironmentName::from_arg_or_env_var(args.environment); let environment = project .environment(&environment_name) .ok_or_else(|| miette::miette!("unknown environment '{environment_name}'"))?; @@ -99,6 +97,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { } else { print_dependency_tree(&dep_map, &direct_deps, &args.regex)?; } + Project::warn_on_discovered_from_env(args.manifest_path.as_deref()); Ok(()) } diff --git a/src/project/manifest/environment.rs b/src/project/manifest/environment.rs index 47572600d..cf1c53e89 100644 --- a/src/project/manifest/environment.rs +++ b/src/project/manifest/environment.rs @@ -43,6 +43,22 @@ impl EnvironmentName { pub fn fancy_display(&self) -> console::StyledObject<&str> { console::style(self.as_str()).magenta() } + + /// Tries to read the environment name from an argument, then it will try + /// to read from an environment variable, otherwise it will fall back to default + pub fn from_arg_or_env_var(arg_name: Option) -> Self { + if let Some(arg_name) = arg_name { + return EnvironmentName::Named(arg_name); + } else if std::env::var("PIXI_IN_SHELL").is_ok() { + if let Ok(env_var_name) = std::env::var("PIXI_ENVIRONMENT_NAME") { + if env_var_name == consts::DEFAULT_ENVIRONMENT_NAME { + return EnvironmentName::Default; + } + return EnvironmentName::Named(env_var_name); + } + } + EnvironmentName::Default + } } impl Borrow for EnvironmentName { diff --git a/src/project/mod.rs b/src/project/mod.rs index 399723def..44b198aa2 100644 --- a/src/project/mod.rs +++ b/src/project/mod.rs @@ -154,10 +154,27 @@ impl Project { } /// Discovers the project manifest file in the current directory or any of the parent - /// directories. + /// directories, or use the manifest specified by the environment. /// This will also set the current working directory to the project root. pub fn discover() -> miette::Result { - let project_toml = match find_project_manifest() { + let project_toml = find_project_manifest(); + + if std::env::var("PIXI_IN_SHELL").is_ok() { + if let Ok(env_manifest_path) = std::env::var("PIXI_PROJECT_MANIFEST") { + if let Some(project_toml) = project_toml { + if env_manifest_path != project_toml.to_string_lossy() { + tracing::warn!( + "Using mainfest {} from `PIXI_PROJECT_MANIFEST` rather than local {}", + env_manifest_path, + project_toml.to_string_lossy() + ); + } + } + return Self::load(Path::new(env_manifest_path.as_str())); + } + } + + let project_toml = match project_toml { Some(file) => file, None => miette::bail!( "could not find {} or {} which is configured to use pixi", @@ -165,6 +182,7 @@ impl Project { PYPROJECT_MANIFEST ), }; + Self::load(&project_toml) } @@ -211,6 +229,24 @@ impl Project { Ok(project) } + /// Warns if Pixi is using a manifest from an environment variable rather than a discovered version + pub fn warn_on_discovered_from_env(manifest_path: Option<&Path>) { + if manifest_path.is_none() && std::env::var("PIXI_IN_SHELL").is_ok() { + let discover_path = find_project_manifest(); + let env_path = std::env::var("PIXI_PROJECT_MANIFEST"); + + if let (Some(discover_path), Ok(env_path)) = (discover_path, env_path) { + if env_path.as_str() != discover_path.to_str().unwrap() { + tracing::warn!( + "Used manifest {} from `PIXI_PROJECT_MANIFEST` rather than local {}", + env_path, + discover_path.to_string_lossy() + ); + } + } + } + } + pub fn with_cli_config(mut self, config: C) -> Self where C: Into,