-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Fix Non-actionable error messages #15879
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
50f8ecf
5c5fef4
07f44ac
e440470
358df9c
f4a4804
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -197,6 +197,8 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { | |
)?; | ||
|
||
let mut plan = PublishPlan::new(&pkg_dep_graph.graph); | ||
// Store the original list of packages to be published for error reporting | ||
let original_packages: BTreeSet<_> = plan.iter().collect(); | ||
// May contains packages from previous rounds as `wait_for_any_publish_confirmation` returns | ||
// after it confirms any packages, not all packages, requiring us to handle the rest in the next | ||
// iteration. | ||
|
@@ -210,7 +212,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { | |
// `b`, and we uploaded `a` and `b` but only confirmed `a`, then on | ||
// the following pass through the outer loop nothing will be ready for | ||
// upload. | ||
for pkg_id in plan.take_ready() { | ||
for pkg_id in plan.take_ready().into_iter() { | ||
let (pkg, (_features, tarball)) = &pkg_dep_graph.packages[&pkg_id]; | ||
opts.gctx.shell().status("Uploading", pkg.package_id())?; | ||
|
||
|
@@ -236,15 +238,52 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { | |
)?)); | ||
} | ||
|
||
transmit( | ||
// Prepare workspace context for error message | ||
let workspace_context = if original_packages.len() > 1 { | ||
let mut remaining: Vec<_> = original_packages | ||
.iter() | ||
.filter(|id| **id != pkg_id) | ||
.map(|id| { | ||
let pkg = &pkg_dep_graph.packages[&id].0; | ||
format!("{} v{}", pkg.name(), pkg.version()) | ||
}) | ||
.collect(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I'm reading this correctly, this is grabbing the list of all packages, including those that have already been published (except for the current package) |
||
// Also include any packages that are still waiting for confirmation | ||
for id in to_confirm.iter().filter(|id| **id != pkg_id) { | ||
let pkg = &pkg_dep_graph.packages[&id].0; | ||
let entry = format!("{} v{}", pkg.name(), pkg.version()); | ||
if !remaining.contains(&entry) { | ||
remaining.push(entry); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we include these? They have been published, we just don't know if the server has fully finished processing it yet or not. As is, the current message is misleading. It says a package hasn't been published yet. As a user, that means I need to go in and publish it but then I'll get an error saying its published. |
||
|
||
if !remaining.is_empty() { | ||
Some(format!( | ||
"the following crates have not been published yet: {}", | ||
remaining.join(", ") | ||
)) | ||
} else { | ||
None | ||
} | ||
} else { | ||
None | ||
}; | ||
|
||
let transmit_result = transmit( | ||
opts.gctx, | ||
ws, | ||
pkg, | ||
tarball.file(), | ||
&mut registry, | ||
source_ids.original, | ||
opts.dry_run, | ||
)?; | ||
workspace_context, | ||
); | ||
|
||
if let Err(e) = transmit_result { | ||
return Err(e); | ||
} | ||
|
||
to_confirm.insert(pkg_id); | ||
|
||
if !opts.dry_run { | ||
|
@@ -450,12 +489,13 @@ fn verify_unpublished( | |
source.describe() | ||
))?; | ||
} else { | ||
bail!( | ||
// Return error instead of bail! so it can be wrapped with enhanced context | ||
return Err(anyhow::anyhow!( | ||
"crate {}@{} already exists on {}", | ||
pkg.name(), | ||
pkg.version(), | ||
source.describe() | ||
); | ||
)); | ||
} | ||
} | ||
|
||
|
@@ -632,6 +672,7 @@ fn transmit( | |
registry: &mut Registry, | ||
registry_id: SourceId, | ||
dry_run: bool, | ||
workspace_context: Option<String>, | ||
) -> CargoResult<()> { | ||
let new_crate = prepare_transmit(gctx, ws, pkg, registry_id)?; | ||
|
||
|
@@ -641,9 +682,24 @@ fn transmit( | |
return Ok(()); | ||
} | ||
|
||
let warnings = registry | ||
.publish(&new_crate, tarball) | ||
.with_context(|| format!("failed to publish to registry at {}", registry.host()))?; | ||
let warnings = registry.publish(&new_crate, tarball).with_context(|| { | ||
if let Some(context) = workspace_context { | ||
format!( | ||
"failed to publish `{}` v{} to registry at {}; {}", | ||
pkg.name(), | ||
pkg.version(), | ||
registry.host(), | ||
context | ||
) | ||
} else { | ||
format!( | ||
"failed to publish `{}` v{} to registry at {}", | ||
pkg.name(), | ||
pkg.version(), | ||
registry.host() | ||
) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but we do for a lot of errors is include the separator for optional context in that context and use an empty string instead of |
||
})?; | ||
|
||
if !warnings.invalid_categories.is_empty() { | ||
let msg = format!( | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2260,7 +2260,7 @@ fn api_error_json() { | |
[PACKAGING] foo v0.0.1 ([ROOT]/foo) | ||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) | ||
[UPLOADING] foo v0.0.1 ([ROOT]/foo) | ||
[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ | ||
[ERROR] failed to publish `foo` v0.0.1 to registry at http://127.0.0.1:[..]/ | ||
Caused by: | ||
the remote server responded with an error (status 403 Forbidden): you must be logged in | ||
|
@@ -2308,7 +2308,7 @@ fn api_error_200() { | |
[PACKAGING] foo v0.0.1 ([ROOT]/foo) | ||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) | ||
[UPLOADING] foo v0.0.1 ([ROOT]/foo) | ||
[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ | ||
[ERROR] failed to publish `foo` v0.0.1 to registry at http://127.0.0.1:[..]/ | ||
Caused by: | ||
the remote server responded with an [ERROR] max upload size is 123 | ||
|
@@ -2356,7 +2356,7 @@ fn api_error_code() { | |
[PACKAGING] foo v0.0.1 ([ROOT]/foo) | ||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) | ||
[UPLOADING] foo v0.0.1 ([ROOT]/foo) | ||
[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ | ||
[ERROR] failed to publish `foo` v0.0.1 to registry at http://127.0.0.1:[..]/ | ||
Caused by: | ||
failed to get a 200 OK response, got 400 | ||
|
@@ -2413,7 +2413,7 @@ fn api_curl_error() { | |
[PACKAGING] foo v0.0.1 ([ROOT]/foo) | ||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) | ||
[UPLOADING] foo v0.0.1 ([ROOT]/foo) | ||
[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ | ||
[ERROR] failed to publish `foo` v0.0.1 to registry at http://127.0.0.1:[..]/ | ||
Caused by: | ||
[52] Server returned nothing (no headers, no data) (Empty reply from server) | ||
|
@@ -2461,7 +2461,7 @@ fn api_other_error() { | |
[PACKAGING] foo v0.0.1 ([ROOT]/foo) | ||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) | ||
[UPLOADING] foo v0.0.1 ([ROOT]/foo) | ||
[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ | ||
[ERROR] failed to publish `foo` v0.0.1 to registry at http://127.0.0.1:[..]/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Like in #15765, could you split adding the package name to the error in a commit before adding the unpublished packages? |
||
Caused by: | ||
invalid response body from server | ||
|
@@ -3608,7 +3608,7 @@ fn invalid_token() { | |
[PACKAGING] foo v0.0.1 ([ROOT]/foo) | ||
[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) | ||
[UPLOADING] foo v0.0.1 ([ROOT]/foo) | ||
[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ | ||
[ERROR] failed to publish `foo` v0.0.1 to registry at http://127.0.0.1:[..]/ | ||
Caused by: | ||
token contains invalid characters. | ||
|
@@ -4484,8 +4484,8 @@ fn workspace_publish_rate_limit_error() { | |
.file("package_c/src/lib.rs", "") | ||
.build(); | ||
|
||
// This demonstrates the current non-actionable error message | ||
// The user doesn't know which package failed or what packages remain to be published | ||
// This demonstrates the improved error message after the fix | ||
// The user now knows which package failed and what packages remain to be published | ||
p.cargo("publish --workspace") | ||
.replace_crates_io(registry.index_url()) | ||
.with_status(101) | ||
|
@@ -4507,7 +4507,7 @@ fn workspace_publish_rate_limit_error() { | |
[COMPILING] package_c v0.1.0 ([ROOT]/foo/target/package/package_c-0.1.0) | ||
[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s | ||
[UPLOADING] package_a v0.1.0 ([ROOT]/foo/package_a) | ||
[ERROR] failed to publish to registry at http://127.0.0.1:[..]/ | ||
[ERROR] failed to publish `package_a` v0.1.0 to registry at http://127.0.0.1:[..]/; the following crates have not been published yet: package_b v0.1.0, package_c v0.1.0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In #15765, the additional packages were on a separate line
Likely what this should look like is
|
||
Caused by: | ||
failed to get a 200 OK response, got 429 | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this change needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change from
for pkg_id in plan.take_ready()
tofor pkg_id in plan.take_ready().into_iter()
was needed because of a Rust type inference issue.The
take_ready()
method returns aBTreeSet<PackageId>
, and Rust couldn't automatically determine which iterator implementation to use (there are multiple:IntoIterator
,Iter
,IterMut
). By explicitly calling.into_iter()
, we tell Rust to use the consuming iterator, which is what we want sincetake_ready()
takes ownership of the set.This makes the code more explicit and avoids ambiguity in type inference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just did a checkout of your branch and deleted the
into_iter
and it built just fine. In fact,for
is defined as callingIntoIterator::into_iter