mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Check if relative URL is valid directory before treating as index (#13917)
As per #13874, passing a relative URL like `test` to `--index` for `uv add` causes unexpected behavior if the directory does not exist. The non-existent index is effectively ignored and uv falls back to PyPI. If a package is found there, the spurious index is then written to `pyproject.toml`. This doesn't happen for `--default-index` since resolution will fail without fallback to PyPI. This PR adds a validation step for indexes provided on the command line. If a directory does not exist, uv will fail with an error. Closes #13874
This commit is contained in:
parent
619a0eafa1
commit
2a66349e96
3 changed files with 112 additions and 6 deletions
|
@ -106,10 +106,8 @@ impl VerbatimUrl {
|
||||||
let (path, fragment) = split_fragment(&path);
|
let (path, fragment) = split_fragment(&path);
|
||||||
|
|
||||||
// Convert to a URL.
|
// Convert to a URL.
|
||||||
let mut url = DisplaySafeUrl::from(
|
let mut url = DisplaySafeUrl::from_file_path(path.clone())
|
||||||
Url::from_file_path(path.clone())
|
.unwrap_or_else(|()| panic!("path is absolute: {}", path.display()));
|
||||||
.unwrap_or_else(|()| panic!("path is absolute: {}", path.display())),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Set the fragment, if it exists.
|
// Set the fragment, if it exists.
|
||||||
if let Some(fragment) = fragment {
|
if let Some(fragment) = fragment {
|
||||||
|
|
|
@ -23,8 +23,8 @@ use uv_configuration::{
|
||||||
use uv_dispatch::BuildDispatch;
|
use uv_dispatch::BuildDispatch;
|
||||||
use uv_distribution::DistributionDatabase;
|
use uv_distribution::DistributionDatabase;
|
||||||
use uv_distribution_types::{
|
use uv_distribution_types::{
|
||||||
Index, IndexName, IndexUrls, NameRequirementSpecification, Requirement, RequirementSource,
|
Index, IndexName, IndexUrl, IndexUrls, NameRequirementSpecification, Requirement,
|
||||||
UnresolvedRequirement, VersionId,
|
RequirementSource, UnresolvedRequirement, VersionId,
|
||||||
};
|
};
|
||||||
use uv_fs::{LockedFile, Simplified};
|
use uv_fs::{LockedFile, Simplified};
|
||||||
use uv_git::GIT_STORE;
|
use uv_git::GIT_STORE;
|
||||||
|
@ -473,6 +473,19 @@ pub(crate) async fn add(
|
||||||
&mut toml,
|
&mut toml,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
// Validate any indexes that were provided on the command-line to ensure
|
||||||
|
// they point to existing directories when using path URLs.
|
||||||
|
for index in &indexes {
|
||||||
|
if let IndexUrl::Path(url) = &index.url {
|
||||||
|
let path = url
|
||||||
|
.to_file_path()
|
||||||
|
.map_err(|()| anyhow::anyhow!("Invalid file path in index URL: {url}"))?;
|
||||||
|
if !path.is_dir() {
|
||||||
|
bail!("Directory not found for index: {url}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add any indexes that were provided on the command-line, in priority order.
|
// Add any indexes that were provided on the command-line, in priority order.
|
||||||
if !raw {
|
if !raw {
|
||||||
let urls = IndexUrls::from_indexes(indexes);
|
let urls = IndexUrls::from_indexes(indexes);
|
||||||
|
|
|
@ -9303,6 +9303,101 @@ fn add_index_without_trailing_slash() -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add an index with an existing relative path.
|
||||||
|
#[test]
|
||||||
|
fn add_index_with_existing_relative_path_index() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
// Create test-index/ subdirectory and copy our "offline" tqdm wheel there
|
||||||
|
let packages = context.temp_dir.child("test-index");
|
||||||
|
packages.create_dir_all()?;
|
||||||
|
|
||||||
|
let wheel_src = context
|
||||||
|
.workspace_root
|
||||||
|
.join("scripts/links/ok-1.0.0-py3-none-any.whl");
|
||||||
|
let wheel_dst = packages.child("ok-1.0.0-py3-none-any.whl");
|
||||||
|
fs_err::copy(&wheel_src, &wheel_dst)?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.add().arg("iniconfig").arg("--index").arg("test-index"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
Resolved 2 packages in [TIME]
|
||||||
|
Prepared 1 package in [TIME]
|
||||||
|
Installed 1 package in [TIME]
|
||||||
|
+ iniconfig==2.0.0
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an index with a non-existent relative path.
|
||||||
|
#[test]
|
||||||
|
fn add_index_with_non_existent_relative_path() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.add().arg("iniconfig").arg("--index").arg("test-index"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Directory not found for index: file://[TEMP_DIR]/test-index
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add an index with a non-existent relative path with the same name as a defined index.
|
||||||
|
#[test]
|
||||||
|
fn add_index_with_non_existent_relative_path_with_same_name_as_index() -> Result<()> {
|
||||||
|
let context = TestContext::new("3.12");
|
||||||
|
|
||||||
|
let pyproject_toml = context.temp_dir.child("pyproject.toml");
|
||||||
|
pyproject_toml.write_str(indoc! {r#"
|
||||||
|
[project]
|
||||||
|
name = "project"
|
||||||
|
version = "0.1.0"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = []
|
||||||
|
|
||||||
|
[[tool.uv.index]]
|
||||||
|
name = "test-index"
|
||||||
|
url = "https://pypi-proxy.fly.dev/simple"
|
||||||
|
"#})?;
|
||||||
|
|
||||||
|
uv_snapshot!(context.filters(), context.add().arg("iniconfig").arg("--index").arg("test-index"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: Directory not found for index: file://[TEMP_DIR]/test-index
|
||||||
|
");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a PyPI requirement.
|
/// Add a PyPI requirement.
|
||||||
#[test]
|
#[test]
|
||||||
fn add_group_comment() -> Result<()> {
|
fn add_group_comment() -> Result<()> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue