mirror of https://github.com/astral-sh/uv
Show workspace dependencies in `uv workspace metadata`
This commit is contained in:
parent
052b89d733
commit
c0801e35ae
|
|
@ -1,12 +1,15 @@
|
|||
use std::collections::BTreeSet;
|
||||
use std::fmt::Write;
|
||||
use std::path::Path;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::Serialize;
|
||||
|
||||
use uv_fs::PortablePathBuf;
|
||||
use uv_normalize::PackageName;
|
||||
use uv_normalize::{ExtraName, GroupName, PackageName};
|
||||
use uv_preview::{Preview, PreviewFeatures};
|
||||
use uv_pypi_types::VerbatimParsedUrl;
|
||||
use uv_warnings::warn_user;
|
||||
use uv_workspace::{DiscoveryOptions, Workspace, WorkspaceCache};
|
||||
|
||||
|
|
@ -29,13 +32,30 @@ struct SchemaReport {
|
|||
version: SchemaVersion,
|
||||
}
|
||||
|
||||
/// A dependency of a workspace member.
|
||||
#[derive(Serialize, Debug)]
|
||||
struct DependencyReport {
|
||||
/// The name of the dependency.
|
||||
name: PackageName,
|
||||
/// Whether this dependency is another workspace member.
|
||||
workspace: bool,
|
||||
/// The extra that requires this dependency, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
extra: Option<ExtraName>,
|
||||
/// The dependency group that requires this dependency, if any.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
group: Option<GroupName>,
|
||||
}
|
||||
|
||||
/// 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,
|
||||
path: PathBuf,
|
||||
/// All dependencies of this workspace member.
|
||||
dependencies: Vec<DependencyReport>,
|
||||
}
|
||||
|
||||
/// The report for a metadata operation.
|
||||
|
|
@ -49,6 +69,68 @@ struct MetadataReport {
|
|||
members: Vec<WorkspaceMemberReport>,
|
||||
}
|
||||
|
||||
/// Extract all dependencies from a workspace member.
|
||||
///
|
||||
/// This function examines the member's regular dependencies, optional dependencies (extras),
|
||||
/// and dependency groups, marking which dependencies are workspace members.
|
||||
fn extract_dependencies(
|
||||
package: &uv_workspace::WorkspaceMember,
|
||||
workspace_names: &BTreeSet<PackageName>,
|
||||
) -> Vec<DependencyReport> {
|
||||
let mut dependencies = Vec::new();
|
||||
|
||||
// Extract regular dependencies
|
||||
if let Some(deps) = package.project().dependencies.as_ref() {
|
||||
for dep in deps {
|
||||
if let Ok(req) = uv_pep508::Requirement::<VerbatimParsedUrl>::from_str(dep) {
|
||||
dependencies.push(DependencyReport {
|
||||
name: req.name.clone(),
|
||||
workspace: workspace_names.contains(&req.name),
|
||||
extra: None,
|
||||
group: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract optional dependencies (extras)
|
||||
if let Some(optional_dependencies) = package.project().optional_dependencies.as_ref() {
|
||||
for (extra_name, deps) in optional_dependencies {
|
||||
for dep in deps {
|
||||
if let Ok(req) = uv_pep508::Requirement::<VerbatimParsedUrl>::from_str(dep) {
|
||||
dependencies.push(DependencyReport {
|
||||
name: req.name.clone(),
|
||||
workspace: workspace_names.contains(&req.name),
|
||||
extra: Some(extra_name.clone()),
|
||||
group: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extract dependency groups
|
||||
if let Some(dependency_groups) = package.pyproject_toml().dependency_groups.as_ref() {
|
||||
for (group_name, deps) in dependency_groups {
|
||||
for dep_spec in deps {
|
||||
// Only process Requirement variants, skip IncludeGroup
|
||||
if let uv_pypi_types::DependencyGroupSpecifier::Requirement(dep) = dep_spec {
|
||||
if let Ok(req) = uv_pep508::Requirement::<VerbatimParsedUrl>::from_str(dep) {
|
||||
dependencies.push(DependencyReport {
|
||||
name: req.name.clone(),
|
||||
workspace: workspace_names.contains(&req.name),
|
||||
extra: None,
|
||||
group: Some(group_name.clone()),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies
|
||||
}
|
||||
|
||||
/// Display package metadata.
|
||||
pub(crate) async fn metadata(
|
||||
project_dir: &Path,
|
||||
|
|
@ -66,12 +148,20 @@ pub(crate) async fn metadata(
|
|||
let workspace =
|
||||
Workspace::discover(project_dir, &DiscoveryOptions::default(), &workspace_cache).await?;
|
||||
|
||||
// Collect all workspace member names for filtering
|
||||
let workspace_names: BTreeSet<PackageName> = workspace
|
||||
.packages()
|
||||
.values()
|
||||
.map(|package| package.project().name.clone())
|
||||
.collect();
|
||||
|
||||
let members = workspace
|
||||
.packages()
|
||||
.values()
|
||||
.map(|package| WorkspaceMemberReport {
|
||||
name: package.project().name.clone(),
|
||||
path: PortablePathBuf::from(package.root().as_path()),
|
||||
path: package.root().clone(),
|
||||
dependencies: extract_dependencies(package, &workspace_names),
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ fn workspace_metadata_simple() {
|
|||
|
||||
let workspace = context.temp_dir.child("foo");
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r###"
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -40,13 +40,14 @@ fn workspace_metadata_simple() {
|
|||
"members": [
|
||||
{
|
||||
"name": "foo",
|
||||
"path": "[TEMP_DIR]/foo"
|
||||
"path": "[TEMP_DIR]/foo",
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -61,7 +62,7 @@ fn workspace_metadata_root_workspace() -> Result<()> {
|
|||
&workspace,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r###"
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -73,21 +74,47 @@ fn workspace_metadata_root_workspace() -> Result<()> {
|
|||
"members": [
|
||||
{
|
||||
"name": "albatross",
|
||||
"path": "[TEMP_DIR]/workspace"
|
||||
"path": "[TEMP_DIR]/workspace",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"workspace": true
|
||||
},
|
||||
{
|
||||
"name": "iniconfig",
|
||||
"workspace": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder"
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "iniconfig",
|
||||
"workspace": false
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"workspace": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds"
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "idna",
|
||||
"workspace": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -104,7 +131,7 @@ fn workspace_metadata_virtual_workspace() -> Result<()> {
|
|||
&workspace,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r###"
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -116,21 +143,47 @@ fn workspace_metadata_virtual_workspace() -> Result<()> {
|
|||
"members": [
|
||||
{
|
||||
"name": "albatross",
|
||||
"path": "[TEMP_DIR]/workspace/packages/albatross"
|
||||
"path": "[TEMP_DIR]/workspace/packages/albatross",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"workspace": true
|
||||
},
|
||||
{
|
||||
"name": "iniconfig",
|
||||
"workspace": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder"
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "anyio",
|
||||
"workspace": false
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"workspace": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds"
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "idna",
|
||||
"workspace": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -149,7 +202,7 @@ fn workspace_metadata_from_member() -> Result<()> {
|
|||
|
||||
let member_dir = workspace.join("packages").join("bird-feeder");
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&member_dir), @r###"
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&member_dir), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -161,21 +214,47 @@ fn workspace_metadata_from_member() -> Result<()> {
|
|||
"members": [
|
||||
{
|
||||
"name": "albatross",
|
||||
"path": "[TEMP_DIR]/workspace"
|
||||
"path": "[TEMP_DIR]/workspace",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"workspace": true
|
||||
},
|
||||
{
|
||||
"name": "iniconfig",
|
||||
"workspace": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bird-feeder",
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder"
|
||||
"path": "[TEMP_DIR]/workspace/packages/bird-feeder",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "iniconfig",
|
||||
"workspace": false
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"workspace": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "seeds",
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds"
|
||||
"path": "[TEMP_DIR]/workspace/packages/seeds",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "idna",
|
||||
"workspace": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -206,7 +285,7 @@ fn workspace_metadata_multiple_members() {
|
|||
.assert()
|
||||
.success();
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace_root), @r###"
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace_root), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -218,21 +297,24 @@ fn workspace_metadata_multiple_members() {
|
|||
"members": [
|
||||
{
|
||||
"name": "pkg-a",
|
||||
"path": "[TEMP_DIR]/pkg-a"
|
||||
"path": "[TEMP_DIR]/pkg-a",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "pkg-b",
|
||||
"path": "[TEMP_DIR]/pkg-a/pkg-b"
|
||||
"path": "[TEMP_DIR]/pkg-a/pkg-b",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "pkg-c",
|
||||
"path": "[TEMP_DIR]/pkg-a/pkg-c"
|
||||
"path": "[TEMP_DIR]/pkg-a/pkg-c",
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -245,7 +327,7 @@ fn workspace_metadata_single_project() {
|
|||
|
||||
let project = context.temp_dir.child("my-project");
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&project), @r###"
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&project), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -257,13 +339,14 @@ fn workspace_metadata_single_project() {
|
|||
"members": [
|
||||
{
|
||||
"name": "my-project",
|
||||
"path": "[TEMP_DIR]/my-project"
|
||||
"path": "[TEMP_DIR]/my-project",
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -278,7 +361,7 @@ fn workspace_metadata_with_excluded() -> Result<()> {
|
|||
&workspace,
|
||||
)?;
|
||||
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r###"
|
||||
uv_snapshot!(context.filters(), context.workspace_metadata().current_dir(&workspace), @r#"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
|
@ -290,13 +373,19 @@ fn workspace_metadata_with_excluded() -> Result<()> {
|
|||
"members": [
|
||||
{
|
||||
"name": "albatross",
|
||||
"path": "[TEMP_DIR]/workspace"
|
||||
"path": "[TEMP_DIR]/workspace",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "iniconfig",
|
||||
"workspace": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"###
|
||||
"#
|
||||
);
|
||||
|
||||
Ok(())
|
||||
|
|
@ -317,3 +406,478 @@ fn workspace_metadata_no_project() {
|
|||
"###
|
||||
);
|
||||
}
|
||||
|
||||
/// Test metadata with regular workspace dependencies.
|
||||
#[test]
|
||||
fn workspace_metadata_with_regular_dependencies() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create workspace with multiple members
|
||||
context.init().arg("workspace-root").assert().success();
|
||||
|
||||
let workspace_root = context.temp_dir.child("workspace-root");
|
||||
|
||||
// Create a library package
|
||||
context
|
||||
.init()
|
||||
.arg("lib-a")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Create another package that depends on lib-a
|
||||
context
|
||||
.init()
|
||||
.arg("app-b")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Add lib-a as a dependency to app-b
|
||||
context
|
||||
.add()
|
||||
.arg("lib-a")
|
||||
.current_dir(workspace_root.child("app-b"))
|
||||
.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]/workspace-root",
|
||||
"members": [
|
||||
{
|
||||
"name": "app-b",
|
||||
"path": "[TEMP_DIR]/workspace-root/app-b",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "lib-a",
|
||||
"workspace": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "lib-a",
|
||||
"path": "[TEMP_DIR]/workspace-root/lib-a",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "workspace-root",
|
||||
"path": "[TEMP_DIR]/workspace-root",
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
/// Test metadata with optional dependencies (extras) on workspace members.
|
||||
#[test]
|
||||
fn workspace_metadata_with_extras() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create workspace with multiple members
|
||||
context.init().arg("workspace-root").assert().success();
|
||||
|
||||
let workspace_root = context.temp_dir.child("workspace-root");
|
||||
|
||||
// Create library packages
|
||||
context
|
||||
.init()
|
||||
.arg("lib-a")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.init()
|
||||
.arg("lib-b")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Create app with optional dependencies on lib-a and lib-b
|
||||
context
|
||||
.init()
|
||||
.arg("app")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Add optional dependencies
|
||||
let app_dir = workspace_root.child("app");
|
||||
context
|
||||
.add()
|
||||
.arg("lib-a")
|
||||
.arg("--optional")
|
||||
.arg("extra-a")
|
||||
.current_dir(&app_dir)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.add()
|
||||
.arg("lib-b")
|
||||
.arg("--optional")
|
||||
.arg("extra-b")
|
||||
.current_dir(&app_dir)
|
||||
.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]/workspace-root",
|
||||
"members": [
|
||||
{
|
||||
"name": "app",
|
||||
"path": "[TEMP_DIR]/workspace-root/app",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "lib-a",
|
||||
"workspace": true,
|
||||
"extra": "extra-a"
|
||||
},
|
||||
{
|
||||
"name": "lib-b",
|
||||
"workspace": true,
|
||||
"extra": "extra-b"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "lib-a",
|
||||
"path": "[TEMP_DIR]/workspace-root/lib-a",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "lib-b",
|
||||
"path": "[TEMP_DIR]/workspace-root/lib-b",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "workspace-root",
|
||||
"path": "[TEMP_DIR]/workspace-root",
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
/// Test metadata with dependency groups containing workspace members.
|
||||
#[test]
|
||||
fn workspace_metadata_with_dependency_groups() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create workspace with multiple members
|
||||
context.init().arg("workspace-root").assert().success();
|
||||
|
||||
let workspace_root = context.temp_dir.child("workspace-root");
|
||||
|
||||
// Create library packages
|
||||
context
|
||||
.init()
|
||||
.arg("test-utils")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.init()
|
||||
.arg("dev-tools")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Create app with dependency groups
|
||||
context
|
||||
.init()
|
||||
.arg("app")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Add dependency groups
|
||||
let app_dir = workspace_root.child("app");
|
||||
context
|
||||
.add()
|
||||
.arg("test-utils")
|
||||
.arg("--group")
|
||||
.arg("test")
|
||||
.current_dir(&app_dir)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.add()
|
||||
.arg("dev-tools")
|
||||
.arg("--group")
|
||||
.arg("dev")
|
||||
.current_dir(&app_dir)
|
||||
.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]/workspace-root",
|
||||
"members": [
|
||||
{
|
||||
"name": "app",
|
||||
"path": "[TEMP_DIR]/workspace-root/app",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "dev-tools",
|
||||
"workspace": true,
|
||||
"group": "dev"
|
||||
},
|
||||
{
|
||||
"name": "test-utils",
|
||||
"workspace": true,
|
||||
"group": "test"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "dev-tools",
|
||||
"path": "[TEMP_DIR]/workspace-root/dev-tools",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "test-utils",
|
||||
"path": "[TEMP_DIR]/workspace-root/test-utils",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "workspace-root",
|
||||
"path": "[TEMP_DIR]/workspace-root",
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
/// Test metadata with workspace members that have no dependencies on each other.
|
||||
#[test]
|
||||
fn workspace_metadata_no_workspace_dependencies() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create workspace with multiple members that don't depend on each other
|
||||
context.init().arg("workspace-root").assert().success();
|
||||
|
||||
let workspace_root = context.temp_dir.child("workspace-root");
|
||||
|
||||
// Create independent packages
|
||||
context
|
||||
.init()
|
||||
.arg("package-a")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.init()
|
||||
.arg("package-b")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.init()
|
||||
.arg("package-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]/workspace-root",
|
||||
"members": [
|
||||
{
|
||||
"name": "package-a",
|
||||
"path": "[TEMP_DIR]/workspace-root/package-a",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "package-b",
|
||||
"path": "[TEMP_DIR]/workspace-root/package-b",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "package-c",
|
||||
"path": "[TEMP_DIR]/workspace-root/package-c",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "workspace-root",
|
||||
"path": "[TEMP_DIR]/workspace-root",
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
/// Test metadata with mixed workspace dependencies (regular, extras, and groups).
|
||||
#[test]
|
||||
fn workspace_metadata_mixed_dependencies() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create workspace with multiple members
|
||||
context.init().arg("workspace-root").assert().success();
|
||||
|
||||
let workspace_root = context.temp_dir.child("workspace-root");
|
||||
|
||||
// Create library packages
|
||||
context
|
||||
.init()
|
||||
.arg("core")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.init()
|
||||
.arg("utils")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
context
|
||||
.init()
|
||||
.arg("testing")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Create app with all types of dependencies
|
||||
context
|
||||
.init()
|
||||
.arg("app")
|
||||
.current_dir(&workspace_root)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Add regular dependency, optional dependency, and dependency group
|
||||
let app_dir = workspace_root.child("app");
|
||||
|
||||
// Add regular dependency
|
||||
context
|
||||
.add()
|
||||
.arg("core")
|
||||
.current_dir(&app_dir)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Add optional dependency
|
||||
context
|
||||
.add()
|
||||
.arg("utils")
|
||||
.arg("--optional")
|
||||
.arg("utils")
|
||||
.current_dir(&app_dir)
|
||||
.assert()
|
||||
.success();
|
||||
|
||||
// Add dependency group
|
||||
context
|
||||
.add()
|
||||
.arg("testing")
|
||||
.arg("--group")
|
||||
.arg("test")
|
||||
.current_dir(&app_dir)
|
||||
.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]/workspace-root",
|
||||
"members": [
|
||||
{
|
||||
"name": "app",
|
||||
"path": "[TEMP_DIR]/workspace-root/app",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "core",
|
||||
"workspace": true
|
||||
},
|
||||
{
|
||||
"name": "utils",
|
||||
"workspace": true,
|
||||
"extra": "utils"
|
||||
},
|
||||
{
|
||||
"name": "testing",
|
||||
"workspace": true,
|
||||
"group": "test"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "core",
|
||||
"path": "[TEMP_DIR]/workspace-root/core",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "testing",
|
||||
"path": "[TEMP_DIR]/workspace-root/testing",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "utils",
|
||||
"path": "[TEMP_DIR]/workspace-root/utils",
|
||||
"dependencies": []
|
||||
},
|
||||
{
|
||||
"name": "workspace-root",
|
||||
"path": "[TEMP_DIR]/workspace-root",
|
||||
"dependencies": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
----- stderr -----
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue