Skip to content

Commit 052b89d

Browse files
committed
Add uv workspace metadata
1 parent a1610c7 commit 052b89d

File tree

12 files changed

+576
-43
lines changed

12 files changed

+576
-43
lines changed

crates/uv-cli/src/lib.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,12 @@ pub enum Commands {
517517
Build(BuildArgs),
518518
/// Upload distributions to an index.
519519
Publish(PublishArgs),
520+
/// Manage workspaces.
521+
#[command(
522+
after_help = "Use `uv help workspace` for more details.",
523+
after_long_help = ""
524+
)]
525+
Workspace(WorkspaceNamespace),
520526
/// The implementation of the build backend.
521527
///
522528
/// These commands are not directly exposed to the user, instead users invoke their build
@@ -6773,6 +6779,21 @@ pub struct PublishArgs {
67736779
pub dry_run: bool,
67746780
}
67756781

6782+
#[derive(Args)]
6783+
pub struct WorkspaceNamespace {
6784+
#[command(subcommand)]
6785+
pub command: WorkspaceCommand,
6786+
}
6787+
6788+
#[derive(Subcommand)]
6789+
pub enum WorkspaceCommand {
6790+
/// Display package metadata.
6791+
Metadata(MetadataArgs),
6792+
}
6793+
6794+
#[derive(Args, Debug)]
6795+
pub struct MetadataArgs;
6796+
67766797
/// See [PEP 517](https://peps.python.org/pep-0517/) and
67776798
/// [PEP 660](https://peps.python.org/pep-0660/) for specifications of the parameters.
67786799
#[derive(Subcommand)]

crates/uv-preview/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ bitflags::bitflags! {
2020
const FORMAT = 1 << 8;
2121
const NATIVE_AUTH = 1 << 9;
2222
const S3_ENDPOINT = 1 << 10;
23+
const WORKSPACE_METADATA = 1 << 11;
2324
}
2425
}
2526

@@ -40,6 +41,7 @@ impl PreviewFeatures {
4041
Self::FORMAT => "format",
4142
Self::NATIVE_AUTH => "native-auth",
4243
Self::S3_ENDPOINT => "s3-endpoint",
44+
Self::WORKSPACE_METADATA => "workspace-metadata",
4345
_ => panic!("`flag_as_str` can only be used for exactly one feature flag"),
4446
}
4547
}
@@ -88,6 +90,7 @@ impl FromStr for PreviewFeatures {
8890
"format" => Self::FORMAT,
8991
"native-auth" => Self::NATIVE_AUTH,
9092
"s3-endpoint" => Self::S3_ENDPOINT,
93+
"workspace-metadata" => Self::WORKSPACE_METADATA,
9194
_ => {
9295
warn_user_once!("Unknown preview feature: `{part}`");
9396
continue;

crates/uv/src/commands/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ use uv_normalize::PackageName;
6666
use uv_python::PythonEnvironment;
6767
use uv_scripts::Pep723Script;
6868
pub(crate) use venv::venv;
69+
pub(crate) use workspace::metadata::metadata;
6970

7071
use crate::printer::Printer;
7172

@@ -86,6 +87,7 @@ pub(crate) mod reporters;
8687
mod self_update;
8788
mod tool;
8889
mod venv;
90+
mod workspace;
8991

9092
#[derive(Copy, Clone)]
9193
pub(crate) enum ExitStatus {
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
use std::fmt::Write;
2+
use std::path::Path;
3+
4+
use anyhow::Result;
5+
use serde::Serialize;
6+
7+
use uv_fs::PortablePathBuf;
8+
use uv_normalize::PackageName;
9+
use uv_preview::{Preview, PreviewFeatures};
10+
use uv_warnings::warn_user;
11+
use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceCache};
12+
13+
use crate::commands::ExitStatus;
14+
use crate::printer::Printer;
15+
16+
/// The schema version for the metadata report.
17+
#[derive(Serialize, Debug, Default)]
18+
#[serde(rename_all = "snake_case")]
19+
enum SchemaVersion {
20+
/// An unstable, experimental schema.
21+
#[default]
22+
Preview,
23+
}
24+
25+
/// The schema metadata for the metadata report.
26+
#[derive(Serialize, Debug, Default)]
27+
struct SchemaReport {
28+
/// The version of the schema.
29+
version: SchemaVersion,
30+
}
31+
32+
/// Report for a single workspace member.
33+
#[derive(Serialize, Debug)]
34+
struct WorkspaceMemberReport {
35+
/// The name of the workspace member.
36+
name: PackageName,
37+
/// The path to the workspace member's root directory.
38+
path: PortablePathBuf,
39+
}
40+
41+
/// The report for a metadata operation.
42+
#[derive(Serialize, Debug)]
43+
struct MetadataReport {
44+
/// The schema of this report.
45+
schema: SchemaReport,
46+
/// The workspace root directory.
47+
workspace_root: PortablePathBuf,
48+
/// The workspace members.
49+
members: Vec<WorkspaceMemberReport>,
50+
}
51+
52+
/// Display package metadata.
53+
pub(crate) async fn metadata(
54+
project_dir: &Path,
55+
preview: Preview,
56+
printer: Printer,
57+
) -> Result<ExitStatus> {
58+
if preview.is_enabled(PreviewFeatures::WORKSPACE_METADATA) {
59+
warn_user!(
60+
"The `uv workspace metadata` command is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.",
61+
PreviewFeatures::WORKSPACE_METADATA
62+
);
63+
}
64+
65+
let workspace_cache = WorkspaceCache::default();
66+
let workspace =
67+
Workspace::discover(project_dir, &DiscoveryOptions::default(), &workspace_cache).await?;
68+
69+
let members = workspace
70+
.packages()
71+
.values()
72+
.map(|package| WorkspaceMemberReport {
73+
name: package.project().name.clone(),
74+
path: PortablePathBuf::from(package.root().as_path()),
75+
})
76+
.collect();
77+
78+
let report = MetadataReport {
79+
schema: SchemaReport::default(),
80+
workspace_root: PortablePathBuf::from(workspace.install_path().as_path()),
81+
members,
82+
};
83+
84+
writeln!(
85+
printer.stdout(),
86+
"{}",
87+
serde_json::to_string_pretty(&report)?
88+
)?;
89+
90+
Ok(ExitStatus::Success)
91+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub(crate) mod metadata;

crates/uv/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ use uv_cli::SelfUpdateArgs;
2626
use uv_cli::{
2727
AuthCommand, AuthNamespace, BuildBackendCommand, CacheCommand, CacheNamespace, Cli, Commands,
2828
PipCommand, PipNamespace, ProjectCommand, PythonCommand, PythonNamespace, SelfCommand,
29-
SelfNamespace, ToolCommand, ToolNamespace, TopLevelArgs, compat::CompatArgs,
29+
SelfNamespace, ToolCommand, ToolNamespace, TopLevelArgs, WorkspaceCommand, WorkspaceNamespace,
30+
compat::CompatArgs,
3031
};
3132
use uv_client::BaseClientBuilder;
3233
use uv_configuration::min_stack_size;
@@ -1709,6 +1710,11 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
17091710
)
17101711
.await
17111712
}
1713+
Commands::Workspace(WorkspaceNamespace { command }) => match command {
1714+
WorkspaceCommand::Metadata(_args) => {
1715+
commands::metadata(&project_dir, globals.preview, printer).await
1716+
}
1717+
},
17121718
Commands::BuildBackend { command } => spawn_blocking(move || match command {
17131719
BuildBackendCommand::BuildSdist { sdist_directory } => {
17141720
commands::build_backend::build_sdist(&sdist_directory)

crates/uv/tests/it/common/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,14 @@ impl TestContext {
10501050
command
10511051
}
10521052

1053+
/// Create a `uv workspace metadata` command with options shared across scenarios.
1054+
pub fn workspace_metadata(&self) -> Command {
1055+
let mut command = Self::new_command();
1056+
command.arg("workspace").arg("metadata");
1057+
self.add_shared_options(&mut command, false);
1058+
command
1059+
}
1060+
10531061
/// Create a `uv export` command with options shared across scenarios.
10541062
pub fn export(&self) -> Command {
10551063
let mut command = Self::new_command();

crates/uv/tests/it/help.rs

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ fn help() {
3333
venv Create a virtual environment
3434
build Build Python packages into source distributions and wheels
3535
publish Upload distributions to an index
36+
workspace Manage workspaces
3637
cache Manage uv's cache
3738
self Manage the uv executable
3839
generate-shell-completion Generate shell completion
@@ -98,26 +99,27 @@ fn help_flag() {
9899
Usage: uv [OPTIONS] <COMMAND>
99100
100101
Commands:
101-
auth Manage authentication
102-
run Run a command or script
103-
init Create a new project
104-
add Add dependencies to the project
105-
remove Remove dependencies from the project
106-
version Read or update the project's version
107-
sync Update the project's environment
108-
lock Update the project's lockfile
109-
export Export the project's lockfile to an alternate format
110-
tree Display the project's dependency tree
111-
format Format Python code in the project
112-
tool Run and install commands provided by Python packages
113-
python Manage Python versions and installations
114-
pip Manage Python packages with a pip-compatible interface
115-
venv Create a virtual environment
116-
build Build Python packages into source distributions and wheels
117-
publish Upload distributions to an index
118-
cache Manage uv's cache
119-
self Manage the uv executable
120-
help Display documentation for a command
102+
auth Manage authentication
103+
run Run a command or script
104+
init Create a new project
105+
add Add dependencies to the project
106+
remove Remove dependencies from the project
107+
version Read or update the project's version
108+
sync Update the project's environment
109+
lock Update the project's lockfile
110+
export Export the project's lockfile to an alternate format
111+
tree Display the project's dependency tree
112+
format Format Python code in the project
113+
tool Run and install commands provided by Python packages
114+
python Manage Python versions and installations
115+
pip Manage Python packages with a pip-compatible interface
116+
venv Create a virtual environment
117+
build Build Python packages into source distributions and wheels
118+
publish Upload distributions to an index
119+
workspace Manage workspaces
120+
cache Manage uv's cache
121+
self Manage the uv executable
122+
help Display documentation for a command
121123
122124
Cache options:
123125
-n, --no-cache Avoid reading from or writing to the cache, instead using a temporary
@@ -178,26 +180,27 @@ fn help_short_flag() {
178180
Usage: uv [OPTIONS] <COMMAND>
179181
180182
Commands:
181-
auth Manage authentication
182-
run Run a command or script
183-
init Create a new project
184-
add Add dependencies to the project
185-
remove Remove dependencies from the project
186-
version Read or update the project's version
187-
sync Update the project's environment
188-
lock Update the project's lockfile
189-
export Export the project's lockfile to an alternate format
190-
tree Display the project's dependency tree
191-
format Format Python code in the project
192-
tool Run and install commands provided by Python packages
193-
python Manage Python versions and installations
194-
pip Manage Python packages with a pip-compatible interface
195-
venv Create a virtual environment
196-
build Build Python packages into source distributions and wheels
197-
publish Upload distributions to an index
198-
cache Manage uv's cache
199-
self Manage the uv executable
200-
help Display documentation for a command
183+
auth Manage authentication
184+
run Run a command or script
185+
init Create a new project
186+
add Add dependencies to the project
187+
remove Remove dependencies from the project
188+
version Read or update the project's version
189+
sync Update the project's environment
190+
lock Update the project's lockfile
191+
export Export the project's lockfile to an alternate format
192+
tree Display the project's dependency tree
193+
format Format Python code in the project
194+
tool Run and install commands provided by Python packages
195+
python Manage Python versions and installations
196+
pip Manage Python packages with a pip-compatible interface
197+
venv Create a virtual environment
198+
build Build Python packages into source distributions and wheels
199+
publish Upload distributions to an index
200+
workspace Manage workspaces
201+
cache Manage uv's cache
202+
self Manage the uv executable
203+
help Display documentation for a command
201204
202205
Cache options:
203206
-n, --no-cache Avoid reading from or writing to the cache, instead using a temporary
@@ -892,6 +895,7 @@ fn help_unknown_subcommand() {
892895
venv
893896
build
894897
publish
898+
workspace
895899
cache
896900
self
897901
generate-shell-completion
@@ -921,6 +925,7 @@ fn help_unknown_subcommand() {
921925
venv
922926
build
923927
publish
928+
workspace
924929
cache
925930
self
926931
generate-shell-completion
@@ -979,6 +984,7 @@ fn help_with_global_option() {
979984
venv Create a virtual environment
980985
build Build Python packages into source distributions and wheels
981986
publish Upload distributions to an index
987+
workspace Manage workspaces
982988
cache Manage uv's cache
983989
self Manage the uv executable
984990
generate-shell-completion Generate shell completion
@@ -1102,6 +1108,7 @@ fn help_with_no_pager() {
11021108
venv Create a virtual environment
11031109
build Build Python packages into source distributions and wheels
11041110
publish Upload distributions to an index
1111+
workspace Manage workspaces
11051112
cache Manage uv's cache
11061113
self Manage the uv executable
11071114
generate-shell-completion Generate shell completion

crates/uv/tests/it/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,4 @@ mod workflow;
138138

139139
mod extract;
140140
mod workspace;
141+
mod workspace_metadata;

crates/uv/tests/it/show_settings.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7760,7 +7760,7 @@ fn preview_features() {
77607760
show_settings: true,
77617761
preview: Preview {
77627762
flags: PreviewFeatures(
7763-
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT,
7763+
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | WORKSPACE_METADATA,
77647764
),
77657765
},
77667766
python_preference: Managed,
@@ -7988,7 +7988,7 @@ fn preview_features() {
79887988
show_settings: true,
79897989
preview: Preview {
79907990
flags: PreviewFeatures(
7991-
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT,
7991+
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | WORKSPACE_METADATA,
79927992
),
79937993
},
79947994
python_preference: Managed,

0 commit comments

Comments
 (0)