diff --git a/docs/reference/cli/pixi/run.md b/docs/reference/cli/pixi/run.md index e065a77925..24a66363b6 100644 --- a/docs/reference/cli/pixi/run.md +++ b/docs/reference/cli/pixi/run.md @@ -61,6 +61,8 @@ pixi run [OPTIONS] [TASK]... - `--locked` : Check if lockfile is up-to-date before installing the environment, aborts when lockfile isn't up-to-date with the manifest file
**env**: `PIXI_LOCKED` +- `--as-is` +: Shorthand for the combination of --no-install and --frozen ## Global Options - `--manifest-path ` diff --git a/docs/reference/cli/pixi/shell-hook.md b/docs/reference/cli/pixi/shell-hook.md index 355543b3fb..5c514c50fa 100644 --- a/docs/reference/cli/pixi/shell-hook.md +++ b/docs/reference/cli/pixi/shell-hook.md @@ -56,6 +56,8 @@ pixi shell-hook [OPTIONS] - `--locked` : Check if lockfile is up-to-date before installing the environment, aborts when lockfile isn't up-to-date with the manifest file
**env**: `PIXI_LOCKED` +- `--as-is` +: Shorthand for the combination of --no-install and --frozen ## Global Options - `--manifest-path ` diff --git a/docs/reference/cli/pixi/shell.md b/docs/reference/cli/pixi/shell.md index 2db4f4e375..ee181216e0 100644 --- a/docs/reference/cli/pixi/shell.md +++ b/docs/reference/cli/pixi/shell.md @@ -51,6 +51,8 @@ pixi shell [OPTIONS] - `--locked` : Check if lockfile is up-to-date before installing the environment, aborts when lockfile isn't up-to-date with the manifest file
**env**: `PIXI_LOCKED` +- `--as-is` +: Shorthand for the combination of --no-install and --frozen ## Global Options - `--manifest-path ` diff --git a/src/cli/cli_config.rs b/src/cli/cli_config.rs index fc4a8e4e9e..29bd276b27 100644 --- a/src/cli/cli_config.rs +++ b/src/cli/cli_config.rs @@ -148,6 +148,44 @@ impl NoInstallConfig { } } +/// Lock file and installation configuration with --as-is support +/// Used by shell, shell-hook, and run commands +#[derive(Parser, Debug, Default, Clone)] +pub struct LockAndInstallConfig { + #[clap(flatten)] + pub no_install_config: NoInstallConfig, + + #[clap(flatten)] + pub lock_file_update_config: LockFileUpdateConfig, + + /// Shorthand for the combination of --no-install and --frozen. + #[arg(long, help_heading = consts::CLAP_UPDATE_OPTIONS, conflicts_with_all = ["locked"])] + pub as_is: bool, +} + +impl LockAndInstallConfig { + /// Returns true if the --as-is flag is set or if the no_install flag is set + pub fn no_install(&self) -> bool { + self.as_is || self.no_install_config.no_install + } + + /// Get the effective lock file usage based on the configuration + pub fn lock_file_usage(&self) -> miette::Result { + // If --as-is is set this is equivalent to --frozen and --no-install + if self.as_is { + return Ok(LockFileUsage::Frozen); + } + + // Otherwise use the normal lock file update config + self.lock_file_update_config.lock_file_usage() + } + + /// Check if installs are allowed (considering --as-is) + pub fn allow_installs(&self) -> bool { + !self.as_is && self.no_install_config.allow_installs() + } +} + #[derive(Parser, Debug, Default, Clone)] pub struct GitRev { /// The git branch @@ -410,7 +448,10 @@ fn build_vcs_requirement( mod tests { use url::Url; - use crate::cli::cli_config::{GitRev, build_vcs_requirement}; + use crate::cli::cli_config::{ + GitRev, LockAndInstallConfig, LockFileUpdateConfig, NoInstallConfig, build_vcs_requirement, + }; + use pixi_core::environment::LockFileUsage; #[test] fn test_build_vcs_requirement_with_all_fields() { @@ -475,4 +516,52 @@ mod tests { ); assert_eq!(result, "mypackage @ git+file:///home/user/GitHub/mypackage"); } + + #[test] + fn test_lock_and_install_config_as_is_flag() { + // Test --as-is sets both frozen and no_install + let config = LockAndInstallConfig { + as_is: true, + no_install_config: NoInstallConfig::default(), + lock_file_update_config: LockFileUpdateConfig::default(), + }; + + assert!(config.no_install(), "as_is should enable no_install"); + assert!(!config.allow_installs(), "as_is should disable installs"); + + let lock_usage = config.lock_file_usage().unwrap(); + assert!( + matches!(lock_usage, LockFileUsage::Frozen), + "as_is should set lock file usage to Frozen" + ); + } + + #[test] + fn test_lock_and_install_config_respects_individual_flags() { + // Test that individual flags still work when --as-is is not set + let config = LockAndInstallConfig { + as_is: false, + no_install_config: NoInstallConfig::new(true), + lock_file_update_config: { + let mut lock_config = LockFileUpdateConfig::default(); + lock_config.lock_file_usage.frozen = true; + lock_config + }, + }; + + assert!( + config.no_install(), + "should respect individual no_install flag" + ); + assert!( + !config.allow_installs(), + "should respect individual no_install flag" + ); + + let lock_usage = config.lock_file_usage().unwrap(); + assert!( + matches!(lock_usage, LockFileUsage::Frozen), + "should respect individual frozen flag" + ); + } } diff --git a/src/cli/run.rs b/src/cli/run.rs index 3bb95b3e6d..985a9f1fe3 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -29,7 +29,7 @@ use pixi_core::{ workspace::{Environment, errors::UnsupportedPlatformError}, }; -use crate::cli::cli_config::{LockFileUpdateConfig, NoInstallConfig, WorkspaceConfig}; +use crate::cli::cli_config::{LockAndInstallConfig, WorkspaceConfig}; /// Runs task in the pixi environment. /// @@ -50,9 +50,7 @@ pub struct Args { pub workspace_config: WorkspaceConfig, #[clap(flatten)] - pub no_install_config: NoInstallConfig, - #[clap(flatten)] - pub lock_file_update_config: LockFileUpdateConfig, + pub lock_and_install_config: LockAndInstallConfig, #[clap(flatten)] pub config: ConfigCli, @@ -125,8 +123,8 @@ pub async fn execute(args: Args) -> miette::Result<()> { // Ensure that the lock-file is up-to-date. let lock_file = workspace .update_lock_file(UpdateLockFileOptions { - no_install: args.no_install_config.no_install, - lock_file_usage: args.lock_file_update_config.lock_file_usage()?, + lock_file_usage: args.lock_and_install_config.lock_file_usage()?, + no_install: args.lock_and_install_config.no_install(), max_concurrent_solves: workspace.config().max_concurrent_solves(), }) .await? @@ -254,7 +252,7 @@ pub async fn execute(args: Args) -> miette::Result<()> { Entry::Occupied(env) => env.into_mut(), Entry::Vacant(entry) => { // Check if we allow installs - if args.no_install_config.allow_installs() { + if args.lock_and_install_config.allow_installs() { // Ensure there is a valid prefix lock_file .prefix( diff --git a/src/cli/shell.rs b/src/cli/shell.rs index a24cb5c02a..3690d9f1f1 100644 --- a/src/cli/shell.rs +++ b/src/cli/shell.rs @@ -18,15 +18,13 @@ use pixi_core::{ workspace::get_activated_environment_variables, }; -use crate::cli::cli_config::{NoInstallConfig, WorkspaceConfig}; +use crate::cli::cli_config::{LockAndInstallConfig, WorkspaceConfig}; #[cfg(target_family = "unix")] use pixi_pty::unix::PtySession; #[cfg(target_family = "unix")] use pixi_utils::prefix::Prefix; -use crate::cli::cli_config::LockFileUpdateConfig; - /// Start a shell in a pixi environment, run `exit` to leave the shell. #[derive(Parser, Debug)] pub struct Args { @@ -34,9 +32,7 @@ pub struct Args { workspace_config: WorkspaceConfig, #[clap(flatten)] - pub no_install_config: NoInstallConfig, - #[clap(flatten)] - pub lock_file_update_config: LockFileUpdateConfig, + pub lock_and_install_config: LockAndInstallConfig, #[clap(flatten)] config: ConfigCli, @@ -281,8 +277,8 @@ pub async fn execute(args: Args) -> miette::Result<()> { &environment, UpdateMode::QuickValidate, UpdateLockFileOptions { - lock_file_usage: args.lock_file_update_config.lock_file_usage()?, - no_install: args.no_install_config.no_install, + lock_file_usage: args.lock_and_install_config.lock_file_usage()?, + no_install: args.lock_and_install_config.no_install(), max_concurrent_solves: workspace.config().max_concurrent_solves(), }, ReinstallPackages::default(), diff --git a/src/cli/shell_hook.rs b/src/cli/shell_hook.rs index 8e2efd55ab..d05117b65c 100644 --- a/src/cli/shell_hook.rs +++ b/src/cli/shell_hook.rs @@ -20,7 +20,7 @@ use pixi_core::{ workspace::{Environment, HasWorkspaceRef, get_activated_environment_variables}, }; -use crate::cli::cli_config::{LockFileUpdateConfig, NoInstallConfig, WorkspaceConfig}; +use crate::cli::cli_config::{LockAndInstallConfig, WorkspaceConfig}; /// Print the pixi environment activation script. /// @@ -37,9 +37,7 @@ pub struct Args { pub project_config: WorkspaceConfig, #[clap(flatten)] - pub no_install_config: NoInstallConfig, - #[clap(flatten)] - pub lock_file_update_config: LockFileUpdateConfig, + pub lock_and_install_config: LockAndInstallConfig, #[clap(flatten)] config: ConfigCli, @@ -160,8 +158,8 @@ pub async fn execute(args: Args) -> miette::Result<()> { &environment, UpdateMode::QuickValidate, UpdateLockFileOptions { - lock_file_usage: args.lock_file_update_config.lock_file_usage()?, - no_install: args.no_install_config.no_install, + lock_file_usage: args.lock_and_install_config.lock_file_usage()?, + no_install: args.lock_and_install_config.no_install(), max_concurrent_solves: workspace.config().max_concurrent_solves(), }, ReinstallPackages::default(), diff --git a/tests/integration_rust/common/mod.rs b/tests/integration_rust/common/mod.rs index 8df58acc41..fe776d94a9 100644 --- a/tests/integration_rust/common/mod.rs +++ b/tests/integration_rust/common/mod.rs @@ -491,7 +491,7 @@ impl PixiControl { // Ensure the lock-file is up-to-date let lock_file = project .update_lock_file(UpdateLockFileOptions { - lock_file_usage: args.lock_file_update_config.lock_file_usage().unwrap(), + lock_file_usage: args.lock_and_install_config.lock_file_usage().unwrap(), ..UpdateLockFileOptions::default() }) .await? diff --git a/tests/integration_rust/install_tests.rs b/tests/integration_rust/install_tests.rs index 147c38c56c..6a5cba705f 100644 --- a/tests/integration_rust/install_tests.rs +++ b/tests/integration_rust/install_tests.rs @@ -10,7 +10,7 @@ use fs_err::tokio as tokio_fs; use pixi::cli::run::{self, Args}; use pixi::cli::{ LockFileUsageConfig, - cli_config::{LockFileUpdateConfig, WorkspaceConfig}, + cli_config::{LockAndInstallConfig, LockFileUpdateConfig, WorkspaceConfig}, }; use pixi_config::{Config, DetachedEnvironments}; use pixi_consts::consts; @@ -297,9 +297,12 @@ async fn install_frozen() { // Check if running with frozen doesn't suddenly install the latest update. let result = pixi .run(run::Args { - lock_file_update_config: LockFileUpdateConfig { - lock_file_usage: LockFileUsageConfig { - frozen: true, + lock_and_install_config: LockAndInstallConfig { + lock_file_update_config: LockFileUpdateConfig { + lock_file_usage: LockFileUsageConfig { + frozen: true, + ..Default::default() + }, ..Default::default() }, ..Default::default()