diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index 1f9c91b4e..b57863797 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -3077,7 +3077,7 @@ pub struct RunArgs { /// Optional dependencies are defined via `project.optional-dependencies` in a `pyproject.toml`. /// /// This option is only available when running in a project. - #[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)] + #[arg(long, conflicts_with = "all_extras", conflicts_with = "only_group", value_parser = extra_name_with_clap_error)] pub extra: Option>, /// Include all optional dependencies. @@ -3085,7 +3085,7 @@ pub struct RunArgs { /// Optional dependencies are defined via `project.optional-dependencies` in a `pyproject.toml`. /// /// This option is only available when running in a project. - #[arg(long, conflicts_with = "extra")] + #[arg(long, conflicts_with = "extra", conflicts_with = "only_group")] pub all_extras: bool, /// Exclude the specified optional dependencies, if `--all-extras` is supplied. @@ -3396,7 +3396,7 @@ pub struct SyncArgs { /// /// Note that all optional dependencies are always included in the resolution; this option only /// affects the selection of packages to install. - #[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)] + #[arg(long, conflicts_with = "all_extras", conflicts_with = "only_group", value_parser = extra_name_with_clap_error)] pub extra: Option>, /// Select the output format. @@ -3410,7 +3410,7 @@ pub struct SyncArgs { /// /// Note that all optional dependencies are always included in the resolution; this option only /// affects the selection of packages to install. - #[arg(long, conflicts_with = "extra")] + #[arg(long, conflicts_with = "extra", conflicts_with = "only_group")] pub all_extras: bool, /// Exclude the specified optional dependencies, if `--all-extras` is supplied. @@ -4247,11 +4247,11 @@ pub struct ExportArgs { /// Include optional dependencies from the specified extra name. /// /// May be provided more than once. - #[arg(long, conflicts_with = "all_extras", value_parser = extra_name_with_clap_error)] + #[arg(long, conflicts_with = "all_extras", conflicts_with = "only_group", value_parser = extra_name_with_clap_error)] pub extra: Option>, /// Include all optional dependencies. - #[arg(long, conflicts_with = "extra")] + #[arg(long, conflicts_with = "extra", conflicts_with = "only_group")] pub all_extras: bool, /// Exclude the specified optional dependencies, if `--all-extras` is supplied. diff --git a/crates/uv/tests/it/export.rs b/crates/uv/tests/it/export.rs index c8a2f7cc7..703c2de3d 100644 --- a/crates/uv/tests/it/export.rs +++ b/crates/uv/tests/it/export.rs @@ -4467,3 +4467,55 @@ fn no_editable_env_var() -> Result<()> { Ok(()) } + +#[test] +fn export_only_group_and_extra_conflict() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = [] + + [project.optional-dependencies] + test = ["pytest"] + + [dependency-groups] + dev = ["ruff"] + "#, + )?; + + // Using --only-group and --extra together should error. + uv_snapshot!(context.filters(), context.export().arg("--only-group").arg("dev").arg("--extra").arg("test"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: the argument '--only-group ' cannot be used with '--extra ' + + Usage: uv export --cache-dir [CACHE_DIR] --only-group --exclude-newer + + For more information, try '--help'. + "###); + + // Using --only-group and --all-extras together should also error. + uv_snapshot!(context.filters(), context.export().arg("--only-group").arg("dev").arg("--all-extras"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: the argument '--only-group ' cannot be used with '--all-extras' + + Usage: uv export --cache-dir [CACHE_DIR] --only-group --exclude-newer + + For more information, try '--help'. + "###); + + Ok(()) +} diff --git a/crates/uv/tests/it/run.rs b/crates/uv/tests/it/run.rs index c7415c01a..54a1bae31 100644 --- a/crates/uv/tests/it/run.rs +++ b/crates/uv/tests/it/run.rs @@ -6039,3 +6039,55 @@ fn isolate_child_environment() -> Result<()> { Ok(()) } + +#[test] +fn run_only_group_and_extra_conflict() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = [] + + [project.optional-dependencies] + test = ["pytest"] + + [dependency-groups] + dev = ["ruff"] + "#, + )?; + + // Using --only-group and --extra together should error. + uv_snapshot!(context.filters(), context.run().arg("--only-group").arg("dev").arg("--extra").arg("test").arg("python").arg("-c").arg("print('hello')"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: the argument '--only-group ' cannot be used with '--extra ' + + Usage: uv run --cache-dir [CACHE_DIR] --only-group --exclude-newer + + For more information, try '--help'. + "###); + + // Using --only-group and --all-extras together should also error. + uv_snapshot!(context.filters(), context.run().arg("--only-group").arg("dev").arg("--all-extras").arg("python").arg("-c").arg("print('hello')"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: the argument '--only-group ' cannot be used with '--all-extras' + + Usage: uv run --cache-dir [CACHE_DIR] --only-group --exclude-newer + + For more information, try '--help'. + "###); + + Ok(()) +} diff --git a/crates/uv/tests/it/sync.rs b/crates/uv/tests/it/sync.rs index cd5e5ae43..747ce4aad 100644 --- a/crates/uv/tests/it/sync.rs +++ b/crates/uv/tests/it/sync.rs @@ -14093,3 +14093,55 @@ fn workspace_editable_conflict() -> Result<()> { Ok(()) } + +#[test] +fn only_group_and_extra_conflict() -> Result<()> { + let context = TestContext::new("3.12"); + + let pyproject_toml = context.temp_dir.child("pyproject.toml"); + pyproject_toml.write_str( + r#" + [project] + name = "project" + version = "0.1.0" + requires-python = ">=3.12" + dependencies = [] + + [project.optional-dependencies] + test = ["pytest"] + + [dependency-groups] + dev = ["ruff"] + "#, + )?; + + // Using --only-group and --extra together should error. + uv_snapshot!(context.filters(), context.sync().arg("--only-group").arg("dev").arg("--extra").arg("test"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: the argument '--only-group ' cannot be used with '--extra ' + + Usage: uv sync --cache-dir [CACHE_DIR] --only-group --exclude-newer + + For more information, try '--help'. + "###); + + // Using --only-group and --all-extras together should also error. + uv_snapshot!(context.filters(), context.sync().arg("--only-group").arg("dev").arg("--all-extras"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + error: the argument '--only-group ' cannot be used with '--all-extras' + + Usage: uv sync --cache-dir [CACHE_DIR] --only-group --exclude-newer + + For more information, try '--help'. + "###); + + Ok(()) +}