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()