Skip to content

Commit 0822594

Browse files
konstinzanieb
andauthored
Skip existing, second iteration: Check the index before uploading (#8531)
Co-authored-by: Zanie Blue <contact@zanie.dev>
1 parent b52f229 commit 0822594

14 files changed

Lines changed: 483 additions & 219 deletions

File tree

Cargo.lock

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/uv-cli/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4819,6 +4819,22 @@ pub struct PublishArgs {
48194819
value_parser = parse_insecure_host,
48204820
)]
48214821
pub allow_insecure_host: Option<Vec<Maybe<TrustedHost>>>,
4822+
4823+
/// Check an index URL for existing files to skip duplicate uploads.
4824+
///
4825+
/// This option allows retrying publishing that failed after only some, but not all files have
4826+
/// been uploaded, and handles error due to parallel uploads of the same file.
4827+
///
4828+
/// Before uploading, the index is checked. If the exact same file already exists in the index,
4829+
/// the file will not be uploaded. If an error occurred during the upload, the index is checked
4830+
/// again, to handle cases where the identical file was uploaded twice in parallel.
4831+
///
4832+
/// The exact behavior will vary based on the index. When uploading to PyPI, uploading the same
4833+
/// file succeeds even without `--check-url`, while most other indexes error.
4834+
///
4835+
/// The index must provide one of the supported hashes (SHA-256, SHA-384, or SHA-512).
4836+
#[arg(long,env = EnvVars::UV_PUBLISH_CHECK_URL)]
4837+
pub check_url: Option<IndexUrl>,
48224838
}
48234839

48244840
/// See [PEP 517](https://peps.python.org/pep-0517/) and

crates/uv-client/src/registry_client.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ use crate::cached_client::CacheControl;
3131
use crate::html::SimpleHtml;
3232
use crate::remote_metadata::wheel_metadata_from_remote_zip;
3333
use crate::rkyvutil::OwnedArchive;
34-
use crate::{CachedClient, CachedClientError, Error, ErrorKind};
34+
use crate::{BaseClient, CachedClient, CachedClientError, Error, ErrorKind};
3535

3636
/// A builder for an [`RegistryClient`].
3737
#[derive(Debug, Clone)]
@@ -143,6 +143,27 @@ impl<'a> RegistryClientBuilder<'a> {
143143
timeout,
144144
}
145145
}
146+
147+
/// Share the underlying client between two different middleware configurations.
148+
pub fn wrap_existing(self, existing: &BaseClient) -> RegistryClient {
149+
// Wrap in any relevant middleware and handle connectivity.
150+
let client = self.base_client_builder.wrap_existing(existing);
151+
152+
let timeout = client.timeout();
153+
let connectivity = client.connectivity();
154+
155+
// Wrap in the cache middleware.
156+
let client = CachedClient::new(client);
157+
158+
RegistryClient {
159+
index_urls: self.index_urls,
160+
index_strategy: self.index_strategy,
161+
cache: self.cache,
162+
connectivity,
163+
client,
164+
timeout,
165+
}
166+
}
146167
}
147168

148169
impl<'a> TryFrom<BaseClientBuilder<'a>> for RegistryClientBuilder<'a> {

crates/uv-extract/src/hash.rs

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,6 @@ impl Hasher {
2323
Hasher::Sha512(hasher) => hasher.update(data),
2424
}
2525
}
26-
27-
pub fn finalize(self) -> Vec<u8> {
28-
match self {
29-
Hasher::Md5(hasher) => hasher.finalize().to_vec(),
30-
Hasher::Sha256(hasher) => hasher.finalize().to_vec(),
31-
Hasher::Sha384(hasher) => hasher.finalize().to_vec(),
32-
Hasher::Sha512(hasher) => hasher.finalize().to_vec(),
33-
}
34-
}
3526
}
3627

3728
impl From<HashAlgorithm> for Hasher {

crates/uv-publish/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ license.workspace = true
1313
doctest = false
1414

1515
[dependencies]
16+
uv-cache = { workspace = true }
1617
uv-client = { workspace = true }
1718
uv-configuration = { workspace = true }
1819
uv-distribution-filename = { workspace = true }
20+
uv-distribution-types = { workspace = true }
21+
uv-extract = { workspace = true }
1922
uv-fs = { workspace = true }
2023
uv-metadata = { workspace = true }
2124
uv-pypi-types = { workspace = true }
@@ -35,7 +38,6 @@ reqwest-retry = { workspace = true }
3538
rustc-hash = { workspace = true }
3639
serde = { workspace = true, features = ["derive"] }
3740
serde_json = { workspace = true }
38-
sha2 = { workspace = true }
3941
thiserror = { workspace = true }
4042
tokio = { workspace = true }
4143
tokio-util = { workspace = true , features = ["io"] }

0 commit comments

Comments
 (0)