diff --git a/crates/uv-resolver/src/lock/export/metadata.rs b/crates/uv-resolver/src/lock/export/metadata.rs index eaa69f6658b..89aa52ec03e 100644 --- a/crates/uv-resolver/src/lock/export/metadata.rs +++ b/crates/uv-resolver/src/lock/export/metadata.rs @@ -1,5 +1,6 @@ use std::collections::BTreeMap; use std::fmt::Display; +use std::path::Path; use uv_distribution_filename::WheelFilename; use uv_distribution_types::{Name, RequiresPython, ResolvedDist, UrlString}; @@ -62,6 +63,9 @@ pub struct Metadata { /// Ideally absolute paths to things that are found in subdirs of this should have exactly /// this as a prefix so it can be stripped to get relative paths if one wants. workspace_root: PortablePathBuf, + /// Information about the synchronized environment, when `--sync` was used. + #[serde(skip_serializing_if = "Option::is_none", default)] + environment: Option, /// The version of python required by the workspace /// /// Every `marker` we emit implicitly assumes this constraint to keep things clean @@ -97,6 +101,13 @@ struct SchemaReport { version: SchemaVersion, } +/// Information about the environment synchronized for the workspace. +#[derive(Debug, serde::Serialize)] +struct MetadataEnvironment { + /// Absolute path to the environment root. + root: PortablePathBuf, +} + /// Info for looking up workspace members, most information is stored in the node behind `id` #[derive(Debug, serde::Serialize)] struct MetadataWorkspaceMember { @@ -830,6 +841,7 @@ impl Metadata { version: SchemaVersion::Preview, }, conflicts, + environment: None, module_owners: BTreeMap::new(), workspace_root, requires_python: lock.requires_python.clone(), @@ -852,6 +864,14 @@ impl Metadata { .to_flat()) } + #[must_use] + pub fn with_environment_root(mut self, environment_root: &Path) -> Self { + self.environment = Some(MetadataEnvironment { + root: PortablePathBuf::from(environment_root), + }); + self + } + #[must_use] pub fn with_module_owners(mut self, module_owners: BTreeMap>) -> Self { self.module_owners = module_owners diff --git a/crates/uv/src/commands/workspace/metadata.rs b/crates/uv/src/commands/workspace/metadata.rs index e9433ea71c1..8751d9af5ab 100644 --- a/crates/uv/src/commands/workspace/metadata.rs +++ b/crates/uv/src/commands/workspace/metadata.rs @@ -158,7 +158,9 @@ pub(crate) async fn metadata( ) .await .context("Failed to collect module owners")?; - export = export.with_module_owners(module_owners); + export = export + .with_environment_root(environment.root()) + .with_module_owners(module_owners); } print_metadata(&export, printer) diff --git a/crates/uv/tests/it/workspace_metadata.rs b/crates/uv/tests/it/workspace_metadata.rs index a1c970b104f..66e912e0591 100644 --- a/crates/uv/tests/it/workspace_metadata.rs +++ b/crates/uv/tests/it/workspace_metadata.rs @@ -162,6 +162,9 @@ dependencies = [ "version": "preview" }, "workspace_root": "[TEMP_DIR]/", + "environment": { + "root": "[VENV]/" + }, "requires_python": ">=3.12", "conflicts": { "sets": [] diff --git a/docs/reference/internals/metadata.md b/docs/reference/internals/metadata.md index 341e9edf342..3e0a6552019 100644 --- a/docs/reference/internals/metadata.md +++ b/docs/reference/internals/metadata.md @@ -135,19 +135,24 @@ Here is a human-readable annotated example: ```js { - // Information about the schema of this output - "schema": { - // The version of this output, currently "preview" - "version": "preview" - }, - // The directory the uv.lock can be found in - "workspace_root": "/workspace", - // Any requirements on the python version this workspace has + // Information about the schema of this output + "schema": { + // The version of this output, currently "preview" + "version": "preview" + }, + // The directory the uv.lock can be found in + "workspace_root": "/workspace", + // Information about the environment, currently only available with `--sync` + "environment": { + // The absolute path to the environment root + "root": "/workspace/.venv" + }, + // Any requirements on the python version this workspace has // // `marker` fields all have this as an implicit constraint that is omitted for cleanliness - "requires_python": ">=3.12", - // A list of workspace members - "members": [ + "requires_python": ">=3.12", + // A list of workspace members + "members": [ { // The name of the package "name": "mypackage", @@ -156,7 +161,7 @@ Here is a human-readable annotated example: // The id of this package's info in the `resolution` map below "id": "mypackage==0.1.0@editable+/workspace/packages/mypackage" }, - ], + ], // A list-of-sets of workspace items that are mutually-exclusive to install, // presumably because they need to install different versions of the same package. //