From aa629c4a54c31d6132ab1655b90dd7542c17d120 Mon Sep 17 00:00:00 2001 From: konsti Date: Mon, 10 Mar 2025 12:38:08 +0100 Subject: [PATCH] Re-add 3 retries in `uv publish` (#12041) In the publish client, we have to set the client retries to 0 as the retry middleware is incompatible with upload bodies. This however also sets `client.retry_policy()` to a zero-retry policy, so we need to construct our own policy. Fixes #12027 --------- Co-authored-by: Zanie Blue --- crates/uv-publish/src/lib.rs | 17 ++++++++++------- crates/uv/src/commands/publish.rs | 3 ++- scripts/publish/test_publish.py | 10 ++++++++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/crates/uv-publish/src/lib.rs b/crates/uv-publish/src/lib.rs index ac0fcfa14..6cd51cbd8 100644 --- a/crates/uv-publish/src/lib.rs +++ b/crates/uv-publish/src/lib.rs @@ -11,6 +11,7 @@ use reqwest::header::AUTHORIZATION; use reqwest::multipart::Part; use reqwest::{Body, Response, StatusCode}; use reqwest_middleware::RequestBuilder; +use reqwest_retry::policies::ExponentialBackoff; use reqwest_retry::{RetryPolicy, Retryable, RetryableStrategy}; use rustc_hash::FxHashSet; use serde::Deserialize; @@ -23,21 +24,22 @@ use tokio::io::{AsyncReadExt, BufReader}; use tokio::sync::Semaphore; use tokio_util::io::ReaderStream; use tracing::{debug, enabled, trace, warn, Level}; +use trusted_publishing::TrustedPublishingToken; use url::Url; -use uv_client::{BaseClient, OwnedArchive, RegistryClientBuilder, UvRetryableStrategy}; +use uv_cache::{Cache, Refresh}; +use uv_client::{ + BaseClient, OwnedArchive, RegistryClientBuilder, UvRetryableStrategy, DEFAULT_RETRIES, +}; use uv_configuration::{KeyringProviderType, TrustedPublishing}; use uv_distribution_filename::{DistFilename, SourceDistExtension, SourceDistFilename}; +use uv_distribution_types::{IndexCapabilities, IndexUrl}; +use uv_extract::hash::{HashReader, Hasher}; use uv_fs::{ProgressReader, Simplified}; use uv_metadata::read_metadata_async_seek; use uv_pypi_types::{HashAlgorithm, HashDigest, Metadata23, MetadataError}; use uv_static::EnvVars; use uv_warnings::{warn_user, warn_user_once}; -pub use trusted_publishing::TrustedPublishingToken; -use uv_cache::{Cache, Refresh}; -use uv_distribution_types::{IndexCapabilities, IndexUrl}; -use uv_extract::hash::{HashReader, Hasher}; - #[derive(Error, Debug)] pub enum PublishError { #[error("The publish path is not a valid glob pattern: `{0}`")] @@ -379,7 +381,8 @@ pub async fn upload( let mut n_past_retries = 0; let start_time = SystemTime::now(); - let retry_policy = client.retry_policy(); + // N.B. We cannot use the client policy here because it is set to zero retries + let retry_policy = ExponentialBackoff::builder().build_with_max_retries(DEFAULT_RETRIES); loop { let (request, idx) = build_request( file, diff --git a/crates/uv/src/commands/publish.rs b/crates/uv/src/commands/publish.rs index a5dc2c493..b022abc34 100644 --- a/crates/uv/src/commands/publish.rs +++ b/crates/uv/src/commands/publish.rs @@ -46,7 +46,8 @@ pub(crate) async fn publish( // * For the uploads themselves, we roll our own retries due to // https://github.com/seanmonstar/reqwest/issues/2416, but for trusted publishing, we want - // the default retries. + // the default retries. We set the retries to 0 here and manually construct the retry policy + // in the upload loop. // * We want to allow configuring TLS for the registry, while for trusted publishing we know the // defaults are correct. // * For the uploads themselves, we know we need an authorization header and we can't nor diff --git a/scripts/publish/test_publish.py b/scripts/publish/test_publish.py index ef018a3a0..620a08e61 100644 --- a/scripts/publish/test_publish.py +++ b/scripts/publish/test_publish.py @@ -181,12 +181,18 @@ def get_latest_version(project_name: str, client: httpx.Client) -> Version: break except httpx.HTTPError as err: error = err - print(f"Error getting version, sleeping for 1s: {err}", file=sys.stderr) + print( + f"Error getting version for {project_name}, sleeping for 1s: {err}", + file=sys.stderr, + ) time.sleep(1) except InvalidSdistFilename as err: # Sometimes there's a link that says "status page" error = err - print(f"Invalid index page, sleeping for 1s: {err}", file=sys.stderr) + print( + f"Invalid index page for {project_name}, sleeping for 1s: {err}", + file=sys.stderr, + ) time.sleep(1) else: raise RuntimeError(f"Failed to fetch {url}") from error