Check all files during a dry-run publish instead of stopping at the first failure (#17785)
This may be superseded by #17786 but is also simpler and easier to merge first.
This commit is contained in:
@@ -195,22 +195,35 @@ pub(crate) async fn publish(
|
||||
None
|
||||
};
|
||||
|
||||
let mut error_count: usize = 0;
|
||||
|
||||
for group in groups {
|
||||
if let Some(check_url_client) = &check_url_client {
|
||||
if uv_publish::check_url(
|
||||
match uv_publish::check_url(
|
||||
check_url_client,
|
||||
&group.file,
|
||||
&group.filename,
|
||||
&download_concurrency,
|
||||
)
|
||||
.await?
|
||||
.await
|
||||
{
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"File {} already exists, skipping",
|
||||
group.filename
|
||||
)?;
|
||||
continue;
|
||||
Ok(true) => {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"File {} already exists, skipping",
|
||||
group.filename
|
||||
)?;
|
||||
continue;
|
||||
}
|
||||
Ok(false) => {}
|
||||
Err(err) => {
|
||||
if dry_run {
|
||||
write_error_chain(&err, printer.stderr(), "error", AnsiColors::Red)?;
|
||||
error_count += 1;
|
||||
continue;
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,14 +248,25 @@ pub(crate) async fn publish(
|
||||
}
|
||||
|
||||
// Collect the metadata for the file.
|
||||
let form_metadata = FormMetadata::read_from_file(&group.file, &group.filename)
|
||||
let form_metadata = match FormMetadata::read_from_file(&group.file, &group.filename)
|
||||
.await
|
||||
.map_err(|err| PublishError::PublishPrepare(group.file.clone(), Box::new(err)))?;
|
||||
.map_err(|err| PublishError::PublishPrepare(group.file.clone(), Box::new(err)))
|
||||
{
|
||||
Ok(metadata) => metadata,
|
||||
Err(err) => {
|
||||
if dry_run {
|
||||
write_error_chain(&err, printer.stderr(), "error", AnsiColors::Red)?;
|
||||
error_count += 1;
|
||||
continue;
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
};
|
||||
|
||||
let uploaded = if direct {
|
||||
if dry_run {
|
||||
// For dry run, call validate since we won't call reserve.
|
||||
let should_upload = uv_publish::validate(
|
||||
match uv_publish::validate(
|
||||
&group.file,
|
||||
&form_metadata,
|
||||
&group.raw_filename,
|
||||
@@ -251,13 +275,27 @@ pub(crate) async fn publish(
|
||||
&upload_client,
|
||||
&credentials,
|
||||
)
|
||||
.await?;
|
||||
if !should_upload {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{}",
|
||||
"File already exists, skipping".dimmed()
|
||||
)?;
|
||||
.await
|
||||
{
|
||||
Ok(should_upload) => {
|
||||
if !should_upload {
|
||||
writeln!(
|
||||
printer.stderr(),
|
||||
"{}",
|
||||
"File already exists, skipping".dimmed()
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let err: anyhow::Error = err.into();
|
||||
write_error_chain(
|
||||
err.as_ref(),
|
||||
printer.stderr(),
|
||||
"error",
|
||||
AnsiColors::Red,
|
||||
)?;
|
||||
error_count += 1;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -278,7 +316,7 @@ pub(crate) async fn publish(
|
||||
.await?
|
||||
} else {
|
||||
// Run validation checks on the file, but don't upload it (if possible).
|
||||
let should_upload = uv_publish::validate(
|
||||
match uv_publish::validate(
|
||||
&group.file,
|
||||
&form_metadata,
|
||||
&group.raw_filename,
|
||||
@@ -287,30 +325,47 @@ pub(crate) async fn publish(
|
||||
&upload_client,
|
||||
&credentials,
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
{
|
||||
Ok(should_upload) => {
|
||||
if dry_run {
|
||||
continue;
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if dry_run {
|
||||
let err: anyhow::Error = err.into();
|
||||
write_error_chain(
|
||||
err.as_ref(),
|
||||
printer.stderr(),
|
||||
"error",
|
||||
AnsiColors::Red,
|
||||
)?;
|
||||
error_count += 1;
|
||||
continue;
|
||||
}
|
||||
return Err(err.into());
|
||||
}
|
||||
}
|
||||
};
|
||||
info!("Upload succeeded");
|
||||
@@ -324,6 +379,12 @@ pub(crate) async fn publish(
|
||||
}
|
||||
}
|
||||
|
||||
if error_count > 0 {
|
||||
let failed = if error_count == 1 { "file" } else { "files" };
|
||||
writeln!(printer.stderr(), "Found issues with {error_count} {failed}")?;
|
||||
return Ok(ExitStatus::Failure);
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
}
|
||||
|
||||
|
||||
@@ -684,3 +684,44 @@ async fn upload_error_problem_details() {
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
/// Test that `--dry-run` checks all files and reports all errors instead of
|
||||
/// stopping at the first failure.
|
||||
#[test]
|
||||
fn dry_run_reports_all_errors() {
|
||||
let context = TestContext::new("3.12");
|
||||
|
||||
// Create two fake wheel files that will fail metadata reading.
|
||||
let wheel_a = context.temp_dir.child("a-1.0.0-py3-none-any.whl");
|
||||
wheel_a.touch().unwrap();
|
||||
let wheel_b = context.temp_dir.child("b-1.0.0-py3-none-any.whl");
|
||||
wheel_b.touch().unwrap();
|
||||
|
||||
uv_snapshot!(context.filters(), context.publish()
|
||||
.arg("--dry-run")
|
||||
.arg("--publish-url")
|
||||
.arg("https://test.pypi.org/legacy/")
|
||||
.arg("--token")
|
||||
.arg("dummy")
|
||||
.arg(wheel_a.path())
|
||||
.arg(wheel_b.path()), @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
Checking 2 files against https://test.pypi.org/legacy/
|
||||
Checking a-1.0.0-py3-none-any.whl ([SIZE])
|
||||
error: Failed to publish: `a-1.0.0-py3-none-any.whl`
|
||||
Caused by: Failed to read metadata
|
||||
Caused by: Failed to read from zip file
|
||||
Caused by: unable to locate the end of central directory record
|
||||
Checking b-1.0.0-py3-none-any.whl ([SIZE])
|
||||
error: Failed to publish: `b-1.0.0-py3-none-any.whl`
|
||||
Caused by: Failed to read metadata
|
||||
Caused by: Failed to read from zip file
|
||||
Caused by: unable to locate the end of central directory record
|
||||
Found issues with 2 files
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user