mirror of https://github.com/astral-sh/uv
Remove subdirectory support
This commit is contained in:
parent
ed48a81fa7
commit
36a8b04d34
|
|
@ -1,7 +1,7 @@
|
||||||
use anstream::println;
|
use anstream::println;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
use tokio::sync::Semaphore;
|
||||||
use uv_cache::{Cache, CacheArgs};
|
use uv_cache::{Cache, CacheArgs};
|
||||||
use uv_client::{BaseClientBuilder, RegistryClientBuilder};
|
use uv_client::{BaseClientBuilder, RegistryClientBuilder};
|
||||||
use uv_distribution_types::IndexUrl;
|
use uv_distribution_types::IndexUrl;
|
||||||
|
|
@ -27,9 +27,10 @@ pub(crate) async fn list_packages(
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let index_url = IndexUrl::parse(&args.url, None)?;
|
let index_url = IndexUrl::parse(&args.url, None)?;
|
||||||
let index = client.fetch_simple_index(&index_url).await?;
|
let concurrency = Semaphore::new(Semaphore::MAX_PERMITS);
|
||||||
|
let index = client.fetch_simple_index(&index_url, &concurrency).await?;
|
||||||
|
|
||||||
for package_name in index.iter() {
|
for package_name in &index.projects {
|
||||||
println!("{}", package_name);
|
println!("{}", package_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -590,12 +590,16 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
) -> Result<Option<LocalWheel>, Error> {
|
) -> Result<Option<LocalWheel>, Error> {
|
||||||
let Some(index) = self
|
let Some(index) = self
|
||||||
.resolver
|
.resolver
|
||||||
.get_cached_distribution(&BuildableSource::Dist(source), Some(tags), &self.client)
|
.get_cached_distribution(source, Some(tags), &self.client)
|
||||||
.await?
|
.await?
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
for prioritized_dist in index.iter() {
|
for prioritized_dist in index
|
||||||
|
.get(source.name())
|
||||||
|
.iter()
|
||||||
|
.flat_map(|index| index.iter())
|
||||||
|
{
|
||||||
let Some(compatible_dist) = prioritized_dist.get() else {
|
let Some(compatible_dist) = prioritized_dist.get() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
@ -627,6 +631,9 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
source: &BuildableSource<'_>,
|
source: &BuildableSource<'_>,
|
||||||
hashes: HashPolicy<'_>,
|
hashes: HashPolicy<'_>,
|
||||||
) -> Result<Option<ArchiveMetadata>, Error> {
|
) -> Result<Option<ArchiveMetadata>, Error> {
|
||||||
|
let BuildableSource::Dist(source) = source else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
let Some(index) = self
|
let Some(index) = self
|
||||||
.resolver
|
.resolver
|
||||||
.get_cached_distribution(source, None, &self.client)
|
.get_cached_distribution(source, None, &self.client)
|
||||||
|
|
@ -634,7 +641,11 @@ impl<'a, Context: BuildContext> DistributionDatabase<'a, Context> {
|
||||||
else {
|
else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
for prioritized_dist in index.iter() {
|
for prioritized_dist in index
|
||||||
|
.get(source.name())
|
||||||
|
.iter()
|
||||||
|
.flat_map(|index| index.iter())
|
||||||
|
{
|
||||||
let Some(compatible_dist) = prioritized_dist.get() else {
|
let Some(compatible_dist) = prioritized_dist.get() else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::collections::btree_map::Entry;
|
use std::collections::btree_map::Entry;
|
||||||
use std::path::Path;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use blake2::Digest;
|
use blake2::Digest;
|
||||||
|
|
@ -11,15 +9,15 @@ use tracing::{debug, instrument, warn};
|
||||||
|
|
||||||
use uv_auth::PyxTokenStore;
|
use uv_auth::PyxTokenStore;
|
||||||
use uv_cache_key::RepositoryUrl;
|
use uv_cache_key::RepositoryUrl;
|
||||||
use uv_client::{MetadataFormat, SimpleIndexMetadata, VersionFiles};
|
use uv_client::{MetadataFormat, VersionFiles};
|
||||||
use uv_configuration::BuildOptions;
|
use uv_configuration::BuildOptions;
|
||||||
use uv_distribution_filename::{DistFilename, SourceDistFilename, WheelFilename};
|
use uv_distribution_filename::{DistFilename, SourceDistFilename, WheelFilename};
|
||||||
use uv_distribution_types::{
|
use uv_distribution_types::{
|
||||||
BuildableSource, File, HashComparison, HashPolicy, IncompatibleSource, IncompatibleWheel,
|
File, HashComparison, HashPolicy, IncompatibleSource, IncompatibleWheel, IndexFormat,
|
||||||
IndexFormat, IndexMetadata, IndexUrl, PrioritizedDist, RegistryBuiltWheel, RegistrySourceDist,
|
IndexMetadata, IndexUrl, PrioritizedDist, RegistryBuiltWheel, RegistrySourceDist, SourceDist,
|
||||||
SourceDist, SourceDistCompatibility, SourceUrl, WheelCompatibility,
|
SourceDistCompatibility, WheelCompatibility,
|
||||||
};
|
};
|
||||||
use uv_git_types::{GitOid, GitUrl};
|
use uv_git_types::GitOid;
|
||||||
use uv_normalize::PackageName;
|
use uv_normalize::PackageName;
|
||||||
use uv_pep440::Version;
|
use uv_pep440::Version;
|
||||||
use uv_pep508::VerbatimUrl;
|
use uv_pep508::VerbatimUrl;
|
||||||
|
|
@ -53,12 +51,12 @@ impl<'a, T: BuildContext> RemoteCacheResolver<'a, T> {
|
||||||
/// Return the cached Git index for the given distribution, if any.
|
/// Return the cached Git index for the given distribution, if any.
|
||||||
pub(crate) async fn get_cached_distribution(
|
pub(crate) async fn get_cached_distribution(
|
||||||
&self,
|
&self,
|
||||||
source: &BuildableSource<'_>,
|
dist: &SourceDist,
|
||||||
tags: Option<&Tags>,
|
tags: Option<&Tags>,
|
||||||
client: &ManagedClient<'a>,
|
client: &ManagedClient<'a>,
|
||||||
) -> Result<Option<GitIndex>, Error> {
|
) -> Result<Option<GitIndex>, Error> {
|
||||||
// Fetch the entries for the given distribution.
|
// Fetch the entries for the given distribution.
|
||||||
let entries = self.get_or_fetch_index(source, client).await?;
|
let entries = self.get_or_fetch_index(dist, client).await?;
|
||||||
if entries.is_empty() {
|
if entries.is_empty() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
@ -76,79 +74,45 @@ impl<'a, T: BuildContext> RemoteCacheResolver<'a, T> {
|
||||||
/// Fetch the remote Git index for the given distribution.
|
/// Fetch the remote Git index for the given distribution.
|
||||||
async fn get_or_fetch_index(
|
async fn get_or_fetch_index(
|
||||||
&self,
|
&self,
|
||||||
source: &BuildableSource<'_>,
|
dist: &SourceDist,
|
||||||
client: &ManagedClient<'a>,
|
client: &ManagedClient<'a>,
|
||||||
) -> Result<Vec<GitIndexEntry>, Error> {
|
) -> Result<Vec<GitIndexEntry>, Error> {
|
||||||
#[derive(Debug)]
|
|
||||||
struct BuildableGitSource<'a> {
|
|
||||||
git: &'a GitUrl,
|
|
||||||
subdirectory: Option<&'a Path>,
|
|
||||||
name: Option<&'a PackageName>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(workspace) = &self.workspace else {
|
let Some(workspace) = &self.workspace else {
|
||||||
return Ok(Vec::default());
|
return Ok(Vec::default());
|
||||||
};
|
};
|
||||||
|
|
||||||
let source = match source {
|
let Some(store) = &self.store else {
|
||||||
BuildableSource::Dist(SourceDist::Git(dist)) => BuildableGitSource {
|
|
||||||
git: &dist.git,
|
|
||||||
subdirectory: dist.subdirectory.as_deref(),
|
|
||||||
name: Some(&dist.name),
|
|
||||||
},
|
|
||||||
BuildableSource::Url(SourceUrl::Git(url)) => BuildableGitSource {
|
|
||||||
git: url.git,
|
|
||||||
subdirectory: url.subdirectory,
|
|
||||||
name: None,
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
return Ok(Vec::default());
|
return Ok(Vec::default());
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(precise) = self.build_context.git().get_precise(source.git) else {
|
let SourceDist::Git(dist) = dist else {
|
||||||
|
return Ok(Vec::default());
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(precise) = self.build_context.git().get_precise(&dist.git) else {
|
||||||
return Ok(Vec::default());
|
return Ok(Vec::default());
|
||||||
};
|
};
|
||||||
|
|
||||||
// Determine the cache key for the Git source.
|
// Determine the cache key for the Git source.
|
||||||
let cache_key = GitCacheKey {
|
let cache_key = GitCacheKey {
|
||||||
repository: RepositoryUrl::new(source.git.repository()),
|
repository: RepositoryUrl::new(dist.git.repository()),
|
||||||
precise,
|
precise,
|
||||||
subdirectory: source.subdirectory,
|
|
||||||
};
|
};
|
||||||
let digest = cache_key.digest();
|
let digest = cache_key.digest();
|
||||||
let index = IndexUrl::from(
|
|
||||||
VerbatimUrl::parse_url(format!(
|
// Add the cache key to the URL.
|
||||||
"http://localhost:8000/v1/git/{workspace}/{}/{}/{}",
|
let url = {
|
||||||
|
let mut url = store.api().clone();
|
||||||
|
url.set_path(&format!(
|
||||||
|
"v1/cache/{workspace}/{}/{}/{}",
|
||||||
&digest[..2],
|
&digest[..2],
|
||||||
&digest[2..4],
|
&digest[2..4],
|
||||||
&digest[4..],
|
&digest[4..],
|
||||||
))
|
));
|
||||||
.unwrap(),
|
url
|
||||||
);
|
|
||||||
debug!("Using remote Git index URL: {}", index);
|
|
||||||
|
|
||||||
// Determine the package name.
|
|
||||||
let name = if let Some(name) = source.name {
|
|
||||||
Cow::Borrowed(name)
|
|
||||||
} else {
|
|
||||||
// Fetch the list of packages from the Simple API.
|
|
||||||
let SimpleIndexMetadata { projects } = client
|
|
||||||
.manual(|client, semaphore| client.fetch_simple_index(&index, semaphore))
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Ensure that the index contains exactly one package.
|
|
||||||
let mut packages = projects.into_iter();
|
|
||||||
let Some(name) = packages.next() else {
|
|
||||||
debug!("Remote Git index at `{index}` contains no packages");
|
|
||||||
return Ok(Vec::default());
|
|
||||||
};
|
|
||||||
if packages.next().is_some() {
|
|
||||||
debug!("Remote Git index at `{index}` contains multiple packages");
|
|
||||||
return Ok(Vec::default());
|
|
||||||
}
|
|
||||||
Cow::Owned(name)
|
|
||||||
};
|
};
|
||||||
|
let index = IndexUrl::from(VerbatimUrl::from_url(url));
|
||||||
|
debug!("Using remote Git index URL: {index}");
|
||||||
|
|
||||||
// Store the index entries in a cache, to avoid redundant fetches.
|
// Store the index entries in a cache, to avoid redundant fetches.
|
||||||
{
|
{
|
||||||
|
|
@ -166,7 +130,7 @@ impl<'a, T: BuildContext> RemoteCacheResolver<'a, T> {
|
||||||
let archives = client
|
let archives = client
|
||||||
.manual(|client, semaphore| {
|
.manual(|client, semaphore| {
|
||||||
client.simple_detail(
|
client.simple_detail(
|
||||||
name.as_ref(),
|
&dist.name,
|
||||||
Some(metadata.as_ref()),
|
Some(metadata.as_ref()),
|
||||||
self.build_context.capabilities(),
|
self.build_context.capabilities(),
|
||||||
semaphore,
|
semaphore,
|
||||||
|
|
@ -184,9 +148,10 @@ impl<'a, T: BuildContext> RemoteCacheResolver<'a, T> {
|
||||||
let files = rkyv::deserialize::<VersionFiles, rkyv::rancor::Error>(&datum.files)
|
let files = rkyv::deserialize::<VersionFiles, rkyv::rancor::Error>(&datum.files)
|
||||||
.expect("archived version files always deserializes");
|
.expect("archived version files always deserializes");
|
||||||
for (filename, file) in files.all() {
|
for (filename, file) in files.all() {
|
||||||
if *filename.name() != *name {
|
if *filename.name() != dist.name {
|
||||||
warn!(
|
warn!(
|
||||||
"Skipping file `{filename}` from remote Git index at `{index}` due to name mismatch (expected: `{name}`)"
|
"Skipping file `{filename}` from remote Git index at `{index}` due to name mismatch (expected: `{}`)",
|
||||||
|
dist.name
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -249,11 +214,9 @@ impl GitIndex {
|
||||||
Self(index)
|
Self(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an [`Iterator`] over the distributions.
|
/// Return the [`GitIndexDistributions`] for the given package name, if any.
|
||||||
pub(crate) fn iter(&self) -> impl Iterator<Item = &PrioritizedDist> {
|
pub(crate) fn get(&self, name: &PackageName) -> Option<&GitIndexDistributions> {
|
||||||
self.0
|
self.0.get(name)
|
||||||
.iter()
|
|
||||||
.flat_map(|(.., distributions)| distributions.0.iter().map(|(.., dist)| dist))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -262,6 +225,11 @@ impl GitIndex {
|
||||||
pub(crate) struct GitIndexDistributions(BTreeMap<Version, PrioritizedDist>);
|
pub(crate) struct GitIndexDistributions(BTreeMap<Version, PrioritizedDist>);
|
||||||
|
|
||||||
impl GitIndexDistributions {
|
impl GitIndexDistributions {
|
||||||
|
/// Returns an [`Iterator`] over the distributions.
|
||||||
|
pub(crate) fn iter(&self) -> impl Iterator<Item = &PrioritizedDist> {
|
||||||
|
self.0.iter().map(|(.., dist)| dist)
|
||||||
|
}
|
||||||
|
|
||||||
/// Add the given [`File`] to the [`GitIndexDistributions`] for the given package.
|
/// Add the given [`File`] to the [`GitIndexDistributions`] for the given package.
|
||||||
fn add_file(
|
fn add_file(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -421,36 +389,25 @@ impl GitIndexCache {
|
||||||
|
|
||||||
/// A cache key for a Git repository at a precise commit.
|
/// A cache key for a Git repository at a precise commit.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
struct GitCacheKey<'a> {
|
struct GitCacheKey {
|
||||||
repository: RepositoryUrl,
|
repository: RepositoryUrl,
|
||||||
precise: GitOid,
|
precise: GitOid,
|
||||||
subdirectory: Option<&'a Path>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GitCacheKey<'_> {
|
impl GitCacheKey {
|
||||||
/// Compute the digest for the Git cache key.
|
/// Compute the digest for the Git cache key.
|
||||||
fn digest(&self) -> String {
|
fn digest(&self) -> String {
|
||||||
let mut hasher = blake2::Blake2b::<blake2::digest::consts::U32>::new();
|
let mut hasher = blake2::Blake2b::<blake2::digest::consts::U32>::new();
|
||||||
hasher.update(self.repository.as_str().as_bytes());
|
hasher.update(self.repository.as_str().as_bytes());
|
||||||
hasher.update(b"/");
|
hasher.update(b"/");
|
||||||
hasher.update(self.precise.as_str().as_bytes());
|
hasher.update(self.precise.as_str().as_bytes());
|
||||||
if let Some(subdirectory) = self
|
|
||||||
.subdirectory
|
|
||||||
.and_then(|subdirectory| subdirectory.to_str())
|
|
||||||
{
|
|
||||||
hasher.update(b"?subdirectory=");
|
|
||||||
hasher.update(subdirectory.as_bytes());
|
|
||||||
}
|
|
||||||
hex::encode(hasher.finalize())
|
hex::encode(hasher.finalize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for GitCacheKey<'_> {
|
impl std::fmt::Display for GitCacheKey {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
write!(f, "{}/{}", self.repository, self.precise.as_str())?;
|
write!(f, "{}/{}", self.repository, self.precise.as_str())?;
|
||||||
if let Some(subdirectory) = &self.subdirectory {
|
|
||||||
write!(f, "?subdirectory={}", subdirectory.display())?;
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue