Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions crates/uv-publish/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -639,7 +639,9 @@ pub async fn upload(
}
}

/// Validate a file against a registry.
/// Validate a distribution before uploading.
///
/// Returns `true` if the file should be uploaded, `false` if it already exists on the server.
pub async fn validate(
file: &Path,
form_metadata: &FormMetadata,
Expand All @@ -648,7 +650,7 @@ pub async fn validate(
store: &PyxTokenStore,
client: &BaseClient,
credentials: &Credentials,
) -> Result<(), PublishError> {
) -> Result<bool, PublishError> {
if store.is_known_url(registry) {
debug!("Performing validation request for {registry}");

Expand All @@ -674,16 +676,45 @@ pub async fn validate(
)
})?;

let status_code = response.status();
debug!("Response code for {validation_url}: {status_code}");

if status_code.is_success() {
#[derive(Deserialize)]
struct ValidateResponse {
exists: bool,
}

// Check if the file already exists.
match response.text().await {
Ok(body) => {
trace!("Response content for {validation_url}: {body}");
if let Ok(response) = serde_json::from_str::<ValidateResponse>(&body) {
if response.exists {
debug!("File already uploaded: {raw_filename}");
return Ok(false);
}
}
}
Err(err) => {
trace!("Failed to read response content for {validation_url}: {err}");
}
}
return Ok(true);
}

// Handle error response.
handle_response(&validation_url, response)
.await
.map_err(|err| {
PublishError::Validate(file.to_path_buf(), registry.clone().into(), err.into())
})?;

Ok(true)
} else {
debug!("Skipping validation request for unsupported publish URL: {registry}");
Ok(true)
}

Ok(())
}

/// Upload a file using the two-phase upload protocol for pyx.
Expand Down
44 changes: 28 additions & 16 deletions crates/uv/src/commands/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ pub(crate) async fn publish(
let uploaded = if direct {
if dry_run {
// For dry run, call validate since we won't call reserve.
uv_publish::validate(
let should_upload = uv_publish::validate(
&group.file,
&form_metadata,
&group.raw_filename,
Expand All @@ -249,6 +249,13 @@ pub(crate) async fn publish(
&credentials,
)
.await?;
if !should_upload {
writeln!(
printer.stderr(),
"{}",
"File already exists, skipping".dimmed()
)?;
}
continue;
}

Expand All @@ -268,7 +275,7 @@ pub(crate) async fn publish(
.await?
} else {
// Run validation checks on the file, but don't upload it (if possible).
uv_publish::validate(
let should_upload = uv_publish::validate(
&group.file,
&form_metadata,
&group.raw_filename,
Expand All @@ -283,20 +290,25 @@ pub(crate) async fn publish(
continue;
}

let reporter = PublishReporter::single(printer);
upload(
&group,
&form_metadata,
&publish_url,
&upload_client,
retry_policy,
&credentials,
check_url_client.as_ref(),
&download_concurrency,
// Needs to be an `Arc` because the reqwest `Body` static lifetime requirement
Arc::new(reporter),
)
.await? // Filename and/or URL are already attached, if applicable.
// If validation indicates the file already exists, skip the upload.
if !should_upload {
false
} else {
let reporter = PublishReporter::single(printer);
upload(
&group,
&form_metadata,
&publish_url,
&upload_client,
retry_policy,
&credentials,
check_url_client.as_ref(),
&download_concurrency,
// Needs to be an `Arc` because the reqwest `Body` static lifetime requirement
Arc::new(reporter),
)
.await? // Filename and/or URL are already attached, if applicable.
}
};
info!("Upload succeeded");

Expand Down
Loading