diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 6e48f3aa3b263..634058bf95d5f 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -8367,6 +8367,14 @@ pub struct MetadataArgs { #[arg(long)] pub sync: bool, + /// Sync dependencies to the active virtual environment. + /// + /// Instead of creating or updating the virtual environment for the project or script, the + /// active virtual environment will be preferred, if the `VIRTUAL_ENV` environment variable is + /// set. + #[arg(long)] + pub active: bool, + /// The Python interpreter to use during resolution. /// /// A Python interpreter is required for building source distributions to determine package diff --git a/crates/uv/src/commands/workspace/metadata.rs b/crates/uv/src/commands/workspace/metadata.rs index 863afa5ec3c7d..eae2741b4b133 100644 --- a/crates/uv/src/commands/workspace/metadata.rs +++ b/crates/uv/src/commands/workspace/metadata.rs @@ -39,6 +39,7 @@ pub(crate) async fn metadata( dry_run: DryRun, refresh: Refresh, sync: bool, + active: bool, python: Option, install_mirrors: PythonInstallMirrors, malware_settings: MalwareCheckSettings, @@ -93,7 +94,7 @@ pub(crate) async fn metadata( &install_mirrors, false, config_discovery, - Some(false), + Some(active), cache, printer, ) @@ -117,7 +118,7 @@ pub(crate) async fn metadata( python_downloads, &install_mirrors, false, - Some(false), + Some(active), cache, printer, ) @@ -184,7 +185,7 @@ pub(crate) async fn metadata( python_downloads, false, config_discovery, - Some(false), + Some(active), cache, DryRun::Disabled, LinkErrorReporting::User, @@ -201,7 +202,7 @@ pub(crate) async fn metadata( &install_mirrors, false, config_discovery, - Some(false), + Some(active), cache, DryRun::Disabled, printer, diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index cb0c7f13900aa..fe5e40593033a 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -2066,6 +2066,7 @@ pub async fn run(cli: Cli, global_initialization: GlobalInitialization) -> Resul args.dry_run, args.refresh, args.sync, + args.active, args.python, args.install_mirrors, args.malware_settings, diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index 89db418faa5b6..7870400c71475 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -2101,6 +2101,7 @@ pub(crate) struct MetadataSettings { pub(crate) frozen: Option, pub(crate) dry_run: DryRun, pub(crate) sync: bool, + pub(crate) active: bool, pub(crate) python: Option, pub(crate) install_mirrors: PythonInstallMirrors, pub(crate) refresh: Refresh, @@ -2124,6 +2125,7 @@ impl MetadataSettings { build, refresh, sync, + active, python, } = *args; @@ -2147,6 +2149,7 @@ impl MetadataSettings { frozen: resolve_frozen(frozen), dry_run: DryRun::from_args(dry_run), sync, + active, python: python.and_then(Maybe::into_option), refresh: Refresh::from(refresh), settings: ResolverSettings::combine( diff --git a/crates/uv/tests/workspace/workspace_metadata.rs b/crates/uv/tests/workspace/workspace_metadata.rs index 1f93df11cd669..91bb7bd092fdb 100644 --- a/crates/uv/tests/workspace/workspace_metadata.rs +++ b/crates/uv/tests/workspace/workspace_metadata.rs @@ -8,6 +8,7 @@ use async_zip::{Compression, ZipEntryBuilder}; use futures::executor::block_on; use url::Url; +use uv_static::EnvVars; use uv_test::{copy_dir_ignore, uv_snapshot}; fn write_wheel( @@ -525,6 +526,52 @@ fn workspace_metadata_sync_centralized_environment() -> Result<()> { Ok(()) } +#[test] +fn workspace_metadata_sync_active_environment() -> Result<()> { + let context = uv_test::test_context_with_versions!(&["3.12", "3.11"]); + + context.temp_dir.child("pyproject.toml").write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = [] + "#, + )?; + + context + .venv() + .arg("--python") + .arg("3.11") + .assert() + .success(); + let active = context.temp_dir.child("active"); + context + .venv() + .arg(active.path()) + .arg("--python") + .arg("3.12") + .assert() + .success(); + + let assert = context + .workspace_metadata() + .arg("--sync") + .arg("--active") + .env(EnvVars::VIRTUAL_ENV, active.path()) + .assert() + .success(); + let metadata: serde_json::Value = serde_json::from_slice(&assert.get_output().stdout)?; + + assert_eq!( + metadata["environment"]["root"].as_str().map(Path::new), + Some(active.path()) + ); + + Ok(()) +} + #[test] fn workspace_metadata_module_owners_from_locked_wheels() -> Result<()> { let context = uv_test::test_context!("3.12");