-
Notifications
You must be signed in to change notification settings - Fork 369
feat(install): add --to-prefix option
#4462
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
fecet
wants to merge
1
commit into
prefix-dev:main
Choose a base branch
from
fecet:feat/install-prefix
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,5 @@ | ||
| use std::path::PathBuf; | ||
|
|
||
| use clap::Parser; | ||
| use fancy_display::FancyDisplay; | ||
| use itertools::Itertools; | ||
|
|
@@ -6,7 +8,10 @@ use pixi_core::{ | |
| UpdateLockFileOptions, WorkspaceLocator, | ||
| environment::get_update_lock_file_and_prefixes, | ||
| lock_file::{ReinstallPackages, UpdateMode}, | ||
| prefix_override::PrefixOverrideGuard, | ||
| }; | ||
| use pixi_manifest::FeaturesExt; | ||
| use rattler_conda_types::Platform; | ||
| use std::fmt::Write; | ||
|
|
||
| use crate::cli_config::WorkspaceConfig; | ||
|
|
@@ -30,6 +35,9 @@ use crate::cli_config::WorkspaceConfig; | |
| /// | ||
| /// You can use `pixi reinstall` to reinstall all environments, one environment | ||
| /// or just some packages of an environment. | ||
| /// | ||
| /// Use the `--to-prefix` flag to install packages to a custom directory instead | ||
| /// of the default environment location. | ||
| #[derive(Parser, Debug)] | ||
| pub struct Args { | ||
| #[clap(flatten)] | ||
|
|
@@ -53,20 +61,48 @@ pub struct Args { | |
| /// This can be useful for instance in a Dockerfile to skip local source dependencies when installing dependencies. | ||
| #[arg(long, requires = "frozen")] | ||
| pub skip: Option<Vec<String>>, | ||
|
|
||
| /// Install to a custom prefix directory instead of the default environment location | ||
| #[arg(long, value_name = "PREFIX", conflicts_with = "all")] | ||
| pub to_prefix: Option<PathBuf>, | ||
|
|
||
| /// The platform to install packages for (only used with --to-prefix) | ||
| #[arg(long, short = 'p', requires = "to_prefix")] | ||
| pub platform: Option<Platform>, | ||
| } | ||
|
|
||
| pub async fn execute(args: Args) -> miette::Result<()> { | ||
| use miette::{Context, IntoDiagnostic}; | ||
|
|
||
| let workspace = WorkspaceLocator::for_cli() | ||
| .with_search_start(args.project_config.workspace_locator_start()) | ||
| .locate()? | ||
| .with_cli_config(args.config); | ||
|
|
||
| // Setup custom prefix if specified | ||
| if let Some(prefix_path) = &args.to_prefix { | ||
| tokio::fs::create_dir_all(prefix_path) | ||
| .await | ||
| .into_diagnostic() | ||
| .with_context(|| { | ||
| format!( | ||
| "Failed to create prefix directory: {}", | ||
| prefix_path.display() | ||
| ) | ||
| })?; | ||
| } | ||
|
Comment on lines
+83
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this needed? I would assume this happens during installation. |
||
|
|
||
| // Install either: | ||
| // | ||
| // 1. specific environments | ||
| // 2. all environments | ||
| // 3. default environment (if no environments are specified) | ||
| let envs = if let Some(envs) = args.environment { | ||
| let envs = if args.to_prefix.is_some() { | ||
| vec![ | ||
| args.environment | ||
| .and_then(|envs| envs.into_iter().next()) | ||
| .unwrap_or_else(|| "default".to_string()), | ||
| ] | ||
| } else if let Some(envs) = args.environment { | ||
|
Comment on lines
+99
to
+105
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of this code? It no longer matches the comment above. |
||
| envs | ||
| } else if args.all { | ||
| workspace | ||
|
|
@@ -84,6 +120,37 @@ pub async fn execute(args: Args) -> miette::Result<()> { | |
| .map(|env| workspace.environment_from_name_or_env_var(Some(env))) | ||
| .collect::<Result<Vec<_>, _>>()?; | ||
|
|
||
| // Validate platform support if platform is specified | ||
| if let Some(platform) = args.platform { | ||
| for env in &environments { | ||
| if !env.platforms().contains(&platform) { | ||
| return Err(miette::miette!( | ||
| "Platform '{}' is not supported by environment '{}'. Supported platforms: {}", | ||
| platform, | ||
| env.name(), | ||
| env.platforms() | ||
| .iter() | ||
| .map(|p| p.to_string()) | ||
| .collect::<Vec<_>>() | ||
| .join(", ") | ||
| )); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Use prefix override guard if installing to custom prefix | ||
| let _guard = args.to_prefix.as_ref().map(|prefix_path| { | ||
| if let Some(platform) = args.platform { | ||
| PrefixOverrideGuard::new_with_platform( | ||
| environments[0].name().to_string(), | ||
| prefix_path.clone(), | ||
| platform, | ||
| ) | ||
| } else { | ||
| PrefixOverrideGuard::new(environments[0].name().to_string(), prefix_path.clone()) | ||
| } | ||
| }); | ||
|
|
||
| // Update the prefixes by installing all packages | ||
| let (lock_file, _) = get_update_lock_file_and_prefixes( | ||
| &environments, | ||
|
|
@@ -122,13 +189,21 @@ pub async fn execute(args: Args) -> miette::Result<()> { | |
| .unwrap(); | ||
| } | ||
|
|
||
| if let Ok(Some(path)) = workspace.config().detached_environments().path() { | ||
| // Add location information to the message | ||
| if let Some(prefix_path) = &args.to_prefix { | ||
| write!( | ||
| &mut message, | ||
| " to '{}'", | ||
| console::style(prefix_path.display()).bold() | ||
| ) | ||
| .unwrap(); | ||
| } else if let Ok(Some(path)) = workspace.config().detached_environments().path() { | ||
| write!( | ||
| &mut message, | ||
| " in '{}'", | ||
| console::style(path.display()).bold() | ||
| ) | ||
| .unwrap() | ||
| .unwrap(); | ||
| } | ||
|
|
||
| if let Some(skip) = &args.skip { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| // Empty module - placeholder for future prefix utilities |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| use rattler_conda_types::Platform; | ||
| use std::cell::RefCell; | ||
| use std::collections::HashMap; | ||
| use std::path::PathBuf; | ||
|
|
||
| thread_local! { | ||
| static PREFIX_OVERRIDES: RefCell<HashMap<String, PathBuf>> = RefCell::new(HashMap::new()); | ||
| static PLATFORM_OVERRIDES: RefCell<HashMap<String, Platform>> = RefCell::new(HashMap::new()); | ||
| } | ||
|
|
||
| /// A RAII guard that manages thread-local prefix overrides | ||
| pub struct PrefixOverrideGuard { | ||
| env_names: Vec<String>, | ||
| } | ||
|
|
||
| impl PrefixOverrideGuard { | ||
| /// Create a new prefix override guard for a single environment | ||
| pub fn new(env_name: String, custom_prefix: PathBuf) -> Self { | ||
| PREFIX_OVERRIDES.with(|overrides| { | ||
| overrides | ||
| .borrow_mut() | ||
| .insert(env_name.clone(), custom_prefix); | ||
| }); | ||
|
|
||
| Self { | ||
| env_names: vec![env_name], | ||
| } | ||
| } | ||
|
|
||
| /// Create a new prefix override guard with platform override for a single environment | ||
| pub fn new_with_platform(env_name: String, custom_prefix: PathBuf, platform: Platform) -> Self { | ||
| PREFIX_OVERRIDES.with(|overrides| { | ||
| overrides | ||
| .borrow_mut() | ||
| .insert(env_name.clone(), custom_prefix); | ||
| }); | ||
|
|
||
| PLATFORM_OVERRIDES.with(|overrides| { | ||
| overrides.borrow_mut().insert(env_name.clone(), platform); | ||
| }); | ||
|
|
||
| Self { | ||
| env_names: vec![env_name], | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl Drop for PrefixOverrideGuard { | ||
| fn drop(&mut self) { | ||
| // Remove all overrides for the environments this guard was managing | ||
| PREFIX_OVERRIDES.with(|overrides| { | ||
| let mut map = overrides.borrow_mut(); | ||
| for env_name in &self.env_names { | ||
| map.remove(env_name); | ||
| } | ||
| }); | ||
|
|
||
| PLATFORM_OVERRIDES.with(|overrides| { | ||
| let mut map = overrides.borrow_mut(); | ||
| for env_name in &self.env_names { | ||
| map.remove(env_name); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /// Get the prefix override for a specific environment, if any | ||
| pub fn get_prefix_override(env_name: &str) -> Option<PathBuf> { | ||
| PREFIX_OVERRIDES.with(|overrides| overrides.borrow().get(env_name).cloned()) | ||
| } | ||
|
|
||
| /// Get the platform override for a specific environment, if any | ||
| pub fn get_platform_override(env_name: &str) -> Option<Platform> { | ||
| PLATFORM_OVERRIDES.with(|overrides| overrides.borrow().get(env_name).cloned()) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets just move this up to the top of the file.