mirror of
https://github.com/astral-sh/uv.git
synced 2025-11-19 11:35:36 +00:00
Add uv workspace metadata (#16516)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | activate nushell venv (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 10 (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | x86-64 python on macos aarch64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | windows python install manager (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | pyenv on wsl x86-64 (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / lint (push) Waiting to run
CI / cargo clippy | ubuntu (push) Blocked by required conditions
CI / cargo clippy | windows (push) Blocked by required conditions
CI / cargo dev generate-all (push) Blocked by required conditions
CI / cargo shear (push) Waiting to run
CI / check system | python3.12 via chocolatey (push) Blocked by required conditions
CI / cargo test | ubuntu (push) Blocked by required conditions
CI / cargo test | macos (push) Blocked by required conditions
CI / cargo test | windows (push) Blocked by required conditions
CI / check windows trampoline | aarch64 (push) Blocked by required conditions
CI / check windows trampoline | i686 (push) Blocked by required conditions
CI / check windows trampoline | x86_64 (push) Blocked by required conditions
CI / test windows trampoline | aarch64 (push) Blocked by required conditions
CI / test windows trampoline | i686 (push) Blocked by required conditions
CI / test windows trampoline | x86_64 (push) Blocked by required conditions
CI / typos (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / build binary | linux libc (push) Blocked by required conditions
CI / build binary | linux aarch64 (push) Blocked by required conditions
CI / build binary | linux musl (push) Blocked by required conditions
CI / smoke test | linux (push) Blocked by required conditions
CI / build binary | macos aarch64 (push) Blocked by required conditions
CI / build binary | macos x86_64 (push) Blocked by required conditions
CI / build binary | windows x86_64 (push) Blocked by required conditions
CI / build binary | windows aarch64 (push) Blocked by required conditions
CI / build binary | msrv (push) Blocked by required conditions
CI / smoke test | linux aarch64 (push) Blocked by required conditions
CI / smoke test | macos (push) Blocked by required conditions
CI / smoke test | windows x86_64 (push) Blocked by required conditions
CI / smoke test | windows aarch64 (push) Blocked by required conditions
CI / integration test | activate nushell venv (push) Blocked by required conditions
CI / integration test | conda on ubuntu (push) Blocked by required conditions
CI / integration test | deadsnakes python3.9 on ubuntu (push) Blocked by required conditions
CI / integration test | free-threaded on windows (push) Blocked by required conditions
CI / integration test | pypy on windows (push) Blocked by required conditions
CI / integration test | aarch64 windows implicit (push) Blocked by required conditions
CI / integration test | determine publish changes (push) Blocked by required conditions
CI / integration test | registries (push) Blocked by required conditions
CI / check cache | macos aarch64 (push) Blocked by required conditions
CI / check system | python on debian (push) Blocked by required conditions
CI / check system | python on fedora (push) Blocked by required conditions
CI / check system | python on ubuntu (push) Blocked by required conditions
CI / check system | python on rocky linux 10 (push) Blocked by required conditions
CI / check system | python on rocky linux 8 (push) Blocked by required conditions
CI / check system | python on rocky linux 9 (push) Blocked by required conditions
CI / check system | graalpy on ubuntu (push) Blocked by required conditions
CI / check system | pypy on ubuntu (push) Blocked by required conditions
CI / check system | pyston (push) Blocked by required conditions
CI / check system | windows registry (push) Blocked by required conditions
CI / check system | python on macos aarch64 (push) Blocked by required conditions
CI / check system | homebrew python on macos aarch64 (push) Blocked by required conditions
CI / check system | x86-64 python on macos aarch64 (push) Blocked by required conditions
CI / check system | python3.10 on windows x86-64 (push) Blocked by required conditions
CI / build binary | freebsd (push) Blocked by required conditions
CI / ecosystem test | pydantic/pydantic-core (push) Blocked by required conditions
CI / ecosystem test | prefecthq/prefect (push) Blocked by required conditions
CI / ecosystem test | pallets/flask (push) Blocked by required conditions
CI / check system | alpine (push) Blocked by required conditions
CI / integration test | aarch64 windows explicit (push) Blocked by required conditions
CI / integration test | windows python install manager (push) Blocked by required conditions
CI / integration test | pypy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on ubuntu (push) Blocked by required conditions
CI / integration test | graalpy on windows (push) Blocked by required conditions
CI / integration test | pyodide on ubuntu (push) Blocked by required conditions
CI / integration test | github actions (push) Blocked by required conditions
CI / integration test | free-threaded python on github actions (push) Blocked by required conditions
CI / integration test | pyenv on wsl x86-64 (push) Blocked by required conditions
CI / integration test | uv publish (push) Blocked by required conditions
CI / integration test | uv_build (push) Blocked by required conditions
CI / check cache | ubuntu (push) Blocked by required conditions
CI / check system | python on macos x86-64 (push) Blocked by required conditions
CI / check system | python3.13 on windows x86-64 (push) Blocked by required conditions
CI / check system | python3.9 via pyenv (push) Blocked by required conditions
CI / check system | python3.13 (push) Blocked by required conditions
CI / check system | conda3.11 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on macos aarch64 (push) Blocked by required conditions
CI / check system | conda3.11 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.8 on linux x86-64 (push) Blocked by required conditions
CI / check system | conda3.11 on windows x86-64 (push) Blocked by required conditions
zizmor / Run zizmor (push) Waiting to run
CI / check system | python3.10 on windows x86 (push) Blocked by required conditions
CI / check system | x86-64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | aarch64 python3.13 on windows aarch64 (push) Blocked by required conditions
CI / check system | conda3.8 on windows x86-64 (push) Blocked by required conditions
CI / check system | amazonlinux (push) Blocked by required conditions
CI / check system | embedded python3.10 on windows x86-64 (push) Blocked by required conditions
CI / benchmarks | walltime aarch64 linux (push) Blocked by required conditions
CI / benchmarks | instrumented (push) Blocked by required conditions
This adds the scaffolding for a `uv workspace metadata` command, as an equivalent to `cargo metadata`, for integration with downstream tools. I didn't do much here beyond emit the workspace root path and the paths of the workspace members. I explored doing a bit more in #16638, but I think we're actually going to want to come up with a fairly comprehensive schema like `cargo metadata` has. I've started exploring that too, but I don't have a concrete proposal to share yet. I don't want this to be a top-level command because I think people would expect `uv metadata <PACKAGE>` to show metadata about arbitrary packages (this has been requested several times). I also think we can do other things in the workspace namespace to make trivial integrations simpler, like `uv workspace list` (enumerate members) and `uv workspace dir` (show the path to the workspace root). I don't expect this to be stable at all to start. I've both gated it with preview and hidden it from the help. The intent is to merge so we can iterate on it as we figure out what integrations need.
This commit is contained in:
parent
5b517bb966
commit
3ccad58166
11 changed files with 458 additions and 4 deletions
|
|
@ -517,6 +517,13 @@ pub enum Commands {
|
|||
Build(BuildArgs),
|
||||
/// Upload distributions to an index.
|
||||
Publish(PublishArgs),
|
||||
/// Manage workspaces.
|
||||
#[command(
|
||||
after_help = "Use `uv help workspace` for more details.",
|
||||
after_long_help = "",
|
||||
hide = true
|
||||
)]
|
||||
Workspace(WorkspaceNamespace),
|
||||
/// The implementation of the build backend.
|
||||
///
|
||||
/// These commands are not directly exposed to the user, instead users invoke their build
|
||||
|
|
@ -6835,6 +6842,22 @@ pub struct PublishArgs {
|
|||
pub dry_run: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct WorkspaceNamespace {
|
||||
#[command(subcommand)]
|
||||
pub command: WorkspaceCommand,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum WorkspaceCommand {
|
||||
/// Display package metadata.
|
||||
#[command(hide = true)]
|
||||
Metadata(MetadataArgs),
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct MetadataArgs;
|
||||
|
||||
/// See [PEP 517](https://peps.python.org/pep-0517/) and
|
||||
/// [PEP 660](https://peps.python.org/pep-0660/) for specifications of the parameters.
|
||||
#[derive(Subcommand)]
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ bitflags::bitflags! {
|
|||
const S3_ENDPOINT = 1 << 10;
|
||||
const CACHE_SIZE = 1 << 11;
|
||||
const INIT_PROJECT_FLAG = 1 << 12;
|
||||
const WORKSPACE_METADATA = 1 << 13;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ impl PreviewFeatures {
|
|||
Self::S3_ENDPOINT => "s3-endpoint",
|
||||
Self::CACHE_SIZE => "cache-size",
|
||||
Self::INIT_PROJECT_FLAG => "init-project-flag",
|
||||
Self::WORKSPACE_METADATA => "workspace-metadata",
|
||||
_ => panic!("`flag_as_str` can only be used for exactly one feature flag"),
|
||||
}
|
||||
}
|
||||
|
|
@ -94,12 +96,12 @@ impl FromStr for PreviewFeatures {
|
|||
"s3-endpoint" => Self::S3_ENDPOINT,
|
||||
"cache-size" => Self::CACHE_SIZE,
|
||||
"init-project-flag" => Self::INIT_PROJECT_FLAG,
|
||||
"workspace-metadata" => Self::WORKSPACE_METADATA,
|
||||
_ => {
|
||||
warn_user_once!("Unknown preview feature: `{part}`");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
flags |= flag;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ use uv_normalize::PackageName;
|
|||
use uv_python::PythonEnvironment;
|
||||
use uv_scripts::Pep723Script;
|
||||
pub(crate) use venv::venv;
|
||||
pub(crate) use workspace::metadata::metadata;
|
||||
|
||||
use crate::printer::Printer;
|
||||
|
||||
|
|
@ -88,6 +89,7 @@ pub(crate) mod reporters;
|
|||
mod self_update;
|
||||
mod tool;
|
||||
mod venv;
|
||||
mod workspace;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum ExitStatus {
|
||||
|
|
|
|||
91
crates/uv/src/commands/workspace/metadata.rs
Normal file
91
crates/uv/src/commands/workspace/metadata.rs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
|
||||
use uv_fs::PortablePathBuf;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_preview::{Preview, PreviewFeatures};
|
||||
use uv_warnings::warn_user;
|
||||
use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceCache};
|
||||
|
||||
use crate::commands::ExitStatus;
|
||||
use crate::printer::Printer;
|
||||
|
||||
/// The schema version for the metadata report.
|
||||
#[derive(Serialize, Debug, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
enum SchemaVersion {
|
||||
/// An unstable, experimental schema.
|
||||
#[default]
|
||||
Preview,
|
||||
}
|
||||
|
||||
/// The schema metadata for the metadata report.
|
||||
#[derive(Serialize, Debug, Default)]
|
||||
struct SchemaReport {
|
||||
/// The version of the schema.
|
||||
version: SchemaVersion,
|
||||
}
|
||||
|
||||
/// Report for a single workspace member.
|
||||
#[derive(Serialize, Debug)]
|
||||
struct WorkspaceMemberReport {
|
||||
/// The name of the workspace member.
|
||||
name: PackageName,
|
||||
/// The path to the workspace member's root directory.
|
||||
path: PortablePathBuf,
|
||||
}
|
||||
|
||||
/// The report for a metadata operation.
|
||||
#[derive(Serialize, Debug)]
|
||||
struct MetadataReport {
|
||||
/// The schema of this report.
|
||||
schema: SchemaReport,
|
||||
/// The workspace root directory.
|
||||
workspace_root: PortablePathBuf,
|
||||
/// The workspace members.
|
||||
members: Vec<WorkspaceMemberReport>,
|
||||
}
|
||||
|
||||
/// Display metadata about the workspace.
|
||||
pub(crate) async fn metadata(
|
||||
project_dir: &Path,
|
||||
preview: Preview,
|
||||
printer: Printer,
|
||||
) -> Result<ExitStatus> {
|
||||
if preview.is_enabled(PreviewFeatures::WORKSPACE_METADATA) {
|
||||
warn_user!(
|
||||
"The `uv workspace metadata` command is experimental and may change without warning. Pass `--preview-features {}` to disable this warning.",
|
||||
PreviewFeatures::WORKSPACE_METADATA
|
||||
);
|
||||
}
|
||||
|
||||
let workspace_cache = WorkspaceCache::default();
|
||||
let workspace =
|
||||
Workspace::discover(project_dir, &DiscoveryOptions::default(), &workspace_cache).await?;
|
||||
|
||||
let members = workspace
|
||||
.packages()
|
||||
.values()
|
||||
.map(|package| WorkspaceMemberReport {
|
||||
name: package.project().name.clone(),
|
||||
path: PortablePathBuf::from(package.root().as_path()),
|
||||
})
|
||||
.collect();
|
||||
|
||||
let report = MetadataReport {
|
||||
schema: SchemaReport::default(),
|
||||
workspace_root: PortablePathBuf::from(workspace.install_path().as_path()),
|
||||
members,
|
||||
};
|
||||
|
||||
writeln!(
|
||||
printer.stdout(),
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&report)?
|
||||
)?;
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
1
crates/uv/src/commands/workspace/mod.rs
Normal file
1
crates/uv/src/commands/workspace/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub(crate) mod metadata;
|
||||
|
|
@ -26,7 +26,8 @@ use uv_cli::SelfUpdateArgs;
|
|||
use uv_cli::{
|
||||
AuthCommand, AuthNamespace, BuildBackendCommand, CacheCommand, CacheNamespace, Cli, Commands,
|
||||
PipCommand, PipNamespace, ProjectCommand, PythonCommand, PythonNamespace, SelfCommand,
|
||||
SelfNamespace, ToolCommand, ToolNamespace, TopLevelArgs, compat::CompatArgs,
|
||||
SelfNamespace, ToolCommand, ToolNamespace, TopLevelArgs, WorkspaceCommand, WorkspaceNamespace,
|
||||
compat::CompatArgs,
|
||||
};
|
||||
use uv_client::BaseClientBuilder;
|
||||
use uv_configuration::min_stack_size;
|
||||
|
|
@ -1733,6 +1734,11 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
|||
)
|
||||
.await
|
||||
}
|
||||
Commands::Workspace(WorkspaceNamespace { command }) => match command {
|
||||
WorkspaceCommand::Metadata(_args) => {
|
||||
commands::metadata(&project_dir, globals.preview, printer).await
|
||||
}
|
||||
},
|
||||
Commands::BuildBackend { command } => spawn_blocking(move || match command {
|
||||
BuildBackendCommand::BuildSdist { sdist_directory } => {
|
||||
commands::build_backend::build_sdist(&sdist_directory)
|
||||
|
|
|
|||
|
|
@ -1064,6 +1064,14 @@ impl TestContext {
|
|||
command
|
||||
}
|
||||
|
||||
/// Create a `uv workspace metadata` command with options shared across scenarios.
|
||||
pub fn workspace_metadata(&self) -> Command {
|
||||
let mut command = Self::new_command();
|
||||
command.arg("workspace").arg("metadata");
|
||||
self.add_shared_options(&mut command, false);
|
||||
command
|
||||
}
|
||||
|
||||
/// Create a `uv export` command with options shared across scenarios.
|
||||
pub fn export(&self) -> Command {
|
||||
let mut command = Self::new_command();
|
||||
|
|
|
|||
|
|
@ -141,3 +141,4 @@ mod workflow;
|
|||
|
||||
mod extract;
|
||||
mod workspace;
|
||||
mod workspace_metadata;
|
||||
|
|
|
|||
|
|
@ -7831,7 +7831,7 @@ fn preview_features() {
|
|||
show_settings: true,
|
||||
preview: Preview {
|
||||
flags: PreviewFeatures(
|
||||
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG,
|
||||
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA,
|
||||
),
|
||||
},
|
||||
python_preference: Managed,
|
||||
|
|
@ -8059,7 +8059,7 @@ fn preview_features() {
|
|||
show_settings: true,
|
||||
preview: Preview {
|
||||
flags: PreviewFeatures(
|
||||
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG,
|
||||
PYTHON_INSTALL_DEFAULT | PYTHON_UPGRADE | JSON_OUTPUT | PYLOCK | ADD_BOUNDS | PACKAGE_CONFLICTS | EXTRA_BUILD_DEPENDENCIES | DETECT_MODULE_CONFLICTS | FORMAT | NATIVE_AUTH | S3_ENDPOINT | CACHE_SIZE | INIT_PROJECT_FLAG | WORKSPACE_METADATA,
|
||||
),
|
||||
},
|
||||
python_preference: Managed,
|
||||
|
|
|
|||
319
crates/uv/tests/it/workspace_metadata.rs
Normal file
319
crates/uv/tests/it/workspace_metadata.rs
Normal file
|
|
@ -0,0 +1,319 @@
|
|||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use assert_cmd::assert::OutputAssertExt;
|
||||
use assert_fs::fixture::PathChild;
|
||||
|
||||
use crate::common::{TestContext, copy_dir_ignore, uv_snapshot};
|
||||
|
||||
fn workspaces_dir() -> PathBuf {
|
||||
env::current_dir()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.join("scripts")
|
||||
.join("workspaces")
|
||||
}
|
||||
|
||||
/// Test basic metadata output for a simple workspace with one member.
|
||||
#[test]
|
||||
fn workspace_metadata_simple() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Initialize a workspace with one member
|
||||
context.init().arg("foo").assert().success();
|
||||
|
||||
let workspace = context.temp_dir.child("foo");
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"schema": {
|
||||
"version": "preview"
|
||||
},
|
||||
"workspace_root": "[TEMP_DIR]/foo",
|
||||
"members": [
|
||||
{
|
||||
"name": "foo",
|
||||
"path": "[TEMP_DIR]/foo"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
/// Test metadata for a root workspace (workspace with a root package).
|
||||
#[test]
|
||||
fn workspace_metadata_root_workspace() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
let workspace = context.temp_dir.child("workspace");
|
||||
|
||||
copy_dir_ignore(
|
||||
workspaces_dir().join("albatross-root-workspace"),
|
||||
&workspace,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"schema": {
|
||||
"version": "preview"
|
||||
},
|
||||
"workspace_root": "[TEMP_DIR]/workspace",
|
||||
"members": [
|
||||
{
|
||||
"name": "albatross",
|
||||
"path": "[TEMP_DIR]/workspace"
|
||||
},
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder"
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test metadata for a virtual workspace (no root package).
|
||||
#[test]
|
||||
fn workspace_metadata_virtual_workspace() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
let workspace = context.temp_dir.child("workspace");
|
||||
|
||||
copy_dir_ignore(
|
||||
workspaces_dir().join("albatross-virtual-workspace"),
|
||||
&workspace,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"schema": {
|
||||
"version": "preview"
|
||||
},
|
||||
"workspace_root": "[TEMP_DIR]/workspace",
|
||||
"members": [
|
||||
{
|
||||
"name": "albatross",
|
||||
"path": "[TEMP_DIR]/workspace/packages/albatross"
|
||||
},
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder"
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test metadata when run from a workspace member directory.
|
||||
#[test]
|
||||
fn workspace_metadata_from_member() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
let workspace = context.temp_dir.child("workspace");
|
||||
|
||||
copy_dir_ignore(
|
||||
workspaces_dir().join("albatross-root-workspace"),
|
||||
&workspace,
|
||||
)?;
|
||||
|
||||
let member_dir = workspace.join("packages").join("bird-feeder");
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&member_dir), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"schema": {
|
||||
"version": "preview"
|
||||
},
|
||||
"workspace_root": "[TEMP_DIR]/workspace",
|
||||
"members": [
|
||||
{
|
||||
"name": "albatross",
|
||||
"path": "[TEMP_DIR]/workspace"
|
||||
},
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder"
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test metadata for a workspace with multiple packages.
|
||||
#[test]
|
||||
fn workspace_metadata_multiple_members() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Initialize workspace root
|
||||
context.init().arg("pkg-a").assert().success();
|
||||
|
||||
let workspace_root = context.temp_dir.child("pkg-a");
|
||||
|
||||
// Add more members
|
||||
context
|
||||
.init()
|
||||
.arg("pkg-b")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.init()
|
||||
.arg("pkg-c")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace_root), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"schema": {
|
||||
"version": "preview"
|
||||
},
|
||||
"workspace_root": "[TEMP_DIR]/pkg-a",
|
||||
"members": [
|
||||
{
|
||||
"name": "pkg-a",
|
||||
"path": "[TEMP_DIR]/pkg-a"
|
||||
},
|
||||
{
|
||||
"name": "pkg-b",
|
||||
"path": "[TEMP_DIR]/pkg-a/pkg-b"
|
||||
},
|
||||
{
|
||||
"name": "pkg-c",
|
||||
"path": "[TEMP_DIR]/pkg-a/pkg-c"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
/// Test metadata for a single project (not a workspace).
|
||||
#[test]
|
||||
fn workspace_metadata_single_project() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
context.init().arg("my-project").assert().success();
|
||||
|
||||
let project = context.temp_dir.child("my-project");
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&project), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"schema": {
|
||||
"version": "preview"
|
||||
},
|
||||
"workspace_root": "[TEMP_DIR]/my-project",
|
||||
"members": [
|
||||
{
|
||||
"name": "my-project",
|
||||
"path": "[TEMP_DIR]/my-project"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
}
|
||||
|
||||
/// Test metadata with excluded packages.
|
||||
#[test]
|
||||
fn workspace_metadata_with_excluded() -> Result<()> {
|
||||
let context = TestContext::new("3.12");
|
||||
let workspace = context.temp_dir.child("workspace");
|
||||
|
||||
copy_dir_ignore(
|
||||
workspaces_dir().join("albatross-project-in-excluded"),
|
||||
&workspace,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r###"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
{
|
||||
"schema": {
|
||||
"version": "preview"
|
||||
},
|
||||
"workspace_root": "[TEMP_DIR]/workspace",
|
||||
"members": [
|
||||
{
|
||||
"name": "albatross",
|
||||
"path": "[TEMP_DIR]/workspace"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Test metadata error when not in a project.
|
||||
#[test]
|
||||
fn workspace_metadata_no_project() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata(), @r###"
|
||||
success: false
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
error: No `pyproject.toml` found in current directory or any parent directory
|
||||
"###
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue