From 61f9fcb166e0fc27cefa25ccb1eddf8087560ee8 Mon Sep 17 00:00:00 2001 From: Ojus Chugh Date: Fri, 1 Aug 2025 21:07:12 +0530 Subject: [PATCH 1/4] tests(publish): add new test case for publish --- tests/testsuite/publish.rs | 82 +++++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index c40c9e9abb9..ff4290d7731 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -2261,7 +2261,10 @@ 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 + +Caused by: + failed to publish 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 @@ -2309,7 +2312,10 @@ 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 + +Caused by: + failed to publish to registry at http://127.0.0.1:[..]/ Caused by: the remote server responded with an [ERROR] max upload size is 123 @@ -2357,7 +2363,10 @@ 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 + +Caused by: + failed to publish to registry at http://127.0.0.1:[..]/ Caused by: failed to get a 200 OK response, got 400 @@ -2414,7 +2423,10 @@ 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 + +Caused by: + failed to publish to registry at http://127.0.0.1:[..]/ Caused by: [52] Server returned nothing (no headers, no data) (Empty reply from server) @@ -2462,7 +2474,10 @@ 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 + +Caused by: + failed to publish to registry at http://127.0.0.1:[..]/ Caused by: invalid response body from server @@ -3609,7 +3624,10 @@ 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 + +Caused by: + failed to publish to registry at http://127.0.0.1:[..]/ Caused by: token contains invalid characters. @@ -4330,6 +4348,58 @@ fn all_unpublishable_packages() { .run(); } +#[cargo_test] +fn workspace_publish_failure_reports_remaining_packages() { + use cargo_test_support::project; + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a", "b", "c"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + "#, + ) + .file("a/src/lib.rs", "") + .file( + "b/Cargo.toml", + r#" + [package] + name = "b" + version = "0.1.0" + edition = "2021" + "#, + ) + .file("b/src/lib.rs", "") + .file( + "c/Cargo.toml", + r#" + [package] + name = "c" + version = "0.1.0" + edition = "2021" + "#, + ) + .file("c/src/lib.rs", "") + .build(); + + // Simulate a publish failure for package 'a' (e.g., already published) + // The error message should match the actual output from the verify step + p.cargo("publish --workspace") + .env("CARGO_REGISTRY_TOKEN", "dummy-token") + .with_status(101) + .with_stderr_contains("[ERROR] crate a@0.1.0 already exists on crates.io index") + .run(); +} + #[cargo_test] fn checksum_changed() { let registry = RegistryBuilder::new().http_api().http_index().build(); From 93c3513528393ca331ac38ba2f1e9391108d417b Mon Sep 17 00:00:00 2001 From: Ojus Chugh Date: Fri, 1 Aug 2025 21:11:04 +0530 Subject: [PATCH 2/4] refactor: clean up publish error context and remove unused variables --- src/cargo/ops/registry/publish.rs | 48 +++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/src/cargo/ops/registry/publish.rs b/src/cargo/ops/registry/publish.rs index f9839bb3545..cbe369a712c 100644 --- a/src/cargo/ops/registry/publish.rs +++ b/src/cargo/ops/registry/publish.rs @@ -210,7 +210,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,7 +236,8 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { )?)); } - transmit( + // Always wrap transmit with context for enhanced error reporting + let transmit_result = transmit( opts.gctx, ws, pkg, @@ -244,7 +245,43 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { &mut registry, source_ids.original, opts.dry_run, - )?; + ).map_err(|e| { + // Collect remaining packages that have not been published yet + let mut remaining: Vec<_> = plan + .iter() + .filter(|id| *id != pkg_id) + .map(|id| { + let pkg = &pkg_dep_graph.packages[&id].0; + format!("{} v{}", pkg.name(), pkg.version()) + }) + .collect(); + // 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); + } + } + + let message = if !remaining.is_empty() { + format!( + "failed to publish `{}` v{}; the following crates have not been published yet: {}", + pkg.name(), + pkg.version(), + remaining.join(", ") + ) + } else { + format!("failed to publish `{}` v{}", pkg.name(), pkg.version()) + }; + + e.context(message) + }); + + if let Err(e) = transmit_result { + return Err(e); + } + to_confirm.insert(pkg_id); if !opts.dry_run { @@ -450,12 +487,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() - ); + )); } } From 8ccc0e272fd8616740386d8334aff2a0c8ce3d57 Mon Sep 17 00:00:00 2001 From: Ojus Chugh Date: Fri, 22 Aug 2025 01:28:43 +0530 Subject: [PATCH 3/4] publish: report failing crate and remaining unpublished on workspace failure - Keep single-crate error format unchanged - Add test for workspace failure messaging --- src/cargo/ops/registry/publish.rs | 96 +++++++++++++++++++------------ tests/testsuite/publish.rs | 86 +++++++++++++++++++++++++-- 2 files changed, 140 insertions(+), 42 deletions(-) diff --git a/src/cargo/ops/registry/publish.rs b/src/cargo/ops/registry/publish.rs index cbe369a712c..e47d35938d6 100644 --- a/src/cargo/ops/registry/publish.rs +++ b/src/cargo/ops/registry/publish.rs @@ -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. @@ -236,47 +238,67 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { )?)); } - // Always wrap transmit with context for enhanced error reporting - let transmit_result = transmit( - opts.gctx, - ws, - pkg, - tarball.file(), - &mut registry, - source_ids.original, - opts.dry_run, - ).map_err(|e| { - // Collect remaining packages that have not been published yet - let mut remaining: Vec<_> = plan - .iter() - .filter(|id| *id != pkg_id) - .map(|id| { + let transmit_result = if original_packages.len() > 1 { + // For workspace publishes, wrap transmit with enhanced error reporting + transmit( + opts.gctx, + ws, + pkg, + tarball.file(), + &mut registry, + source_ids.original, + opts.dry_run, + ).map_err(|e| { + // Collect remaining packages that have not been published yet + 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(); + // 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; - format!("{} v{}", pkg.name(), pkg.version()) - }) - .collect(); - // 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); + let entry = format!("{} v{}", pkg.name(), pkg.version()); + if !remaining.contains(&entry) { + remaining.push(entry); + } } - } - let message = if !remaining.is_empty() { - format!( - "failed to publish `{}` v{}; the following crates have not been published yet: {}", + let message = if !remaining.is_empty() { + format!( + "failed to publish `{}` v{}; the following crates have not been published yet: {}", + pkg.name(), + pkg.version(), + remaining.join(", ") + ) + } else { + format!("failed to publish `{}` v{}", pkg.name(), pkg.version()) + }; + + e.context(message) + }) + } else { + // For single package publishes, preserve original top-level error message with package name + transmit( + opts.gctx, + ws, + pkg, + tarball.file(), + &mut registry, + source_ids.original, + opts.dry_run, + ) + .map_err(|e| { + e.context(format!( + "failed to publish `{}` v{}", pkg.name(), - pkg.version(), - remaining.join(", ") - ) - } else { - format!("failed to publish `{}` v{}", pkg.name(), pkg.version()) - }; - - e.context(message) - }); + pkg.version() + )) + }) + }; if let Err(e) = transmit_result { return Err(e); diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index ff4290d7731..5e1e94860e4 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -4351,6 +4351,38 @@ fn all_unpublishable_packages() { #[cargo_test] fn workspace_publish_failure_reports_remaining_packages() { use cargo_test_support::project; + use cargo_test_support::registry::RegistryBuilder; + + // Create a registry that will fail for package 'a' but succeed for others + let _registry = RegistryBuilder::new() + .alternative() + .http_api() + .add_responder("/api/v1/crates/new", |req, _| { + if let Some(body) = &req.body { + let body_str = String::from_utf8_lossy(body); + if body_str.contains("\"name\":\"a\"") { + Response { + body: b"{\"errors\":[{\"detail\":\"crate a@0.1.0 already exists on crates.io index\"}]}".to_vec(), + code: 400, + headers: vec!["Content-Type: application/json".to_string()], + } + } else { + Response { + body: b"{\"ok\":true}".to_vec(), + code: 200, + headers: vec!["Content-Type: application/json".to_string()], + } + } + } else { + Response { + body: b"{\"ok\":true}".to_vec(), + code: 200, + headers: vec!["Content-Type: application/json".to_string()], + } + } + }) + .build(); + let p = project() .file( "Cargo.toml", @@ -4366,6 +4398,9 @@ fn workspace_publish_failure_reports_remaining_packages() { name = "a" version = "0.1.0" edition = "2021" + authors = [] + license = "MIT" + description = "Package A" "#, ) .file("a/src/lib.rs", "") @@ -4376,6 +4411,9 @@ fn workspace_publish_failure_reports_remaining_packages() { name = "b" version = "0.1.0" edition = "2021" + authors = [] + license = "MIT" + description = "Package B" "#, ) .file("b/src/lib.rs", "") @@ -4386,17 +4424,55 @@ fn workspace_publish_failure_reports_remaining_packages() { name = "c" version = "0.1.0" edition = "2021" + authors = [] + license = "MIT" + description = "Package C" "#, ) .file("c/src/lib.rs", "") .build(); - // Simulate a publish failure for package 'a' (e.g., already published) - // The error message should match the actual output from the verify step - p.cargo("publish --workspace") - .env("CARGO_REGISTRY_TOKEN", "dummy-token") + // Test that when package 'a' fails to publish, the error message includes + // information about which packages remain unpublished + p.cargo("publish --workspace --registry alternative") .with_status(101) - .with_stderr_contains("[ERROR] crate a@0.1.0 already exists on crates.io index") + .with_stderr_data(str![[r#" +[WARNING] virtual workspace defaulting to `resolver = "1"` despite one or more workspace members being on edition 2021 which implies `resolver = "2"` +[NOTE] to keep the current resolver, specify `workspace.resolver = "1"` in the workspace root's manifest +[NOTE] to use the edition 2021 resolver, specify `workspace.resolver = "2"` in the workspace root's manifest +[NOTE] for more details see https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions +[UPDATING] `alternative` index +[WARNING] manifest has no documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] a v0.1.0 ([ROOT]/foo/a) +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[WARNING] manifest has no documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] b v0.1.0 ([ROOT]/foo/b) +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[WARNING] manifest has no documentation, homepage or repository. +See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. +[PACKAGING] c v0.1.0 ([ROOT]/foo/c) +[PACKAGED] 4 files, [FILE_SIZE]B ([FILE_SIZE]B compressed) +[VERIFYING] a v0.1.0 ([ROOT]/foo/a) +[COMPILING] a v0.1.0 ([ROOT]/foo/target/package/a-0.1.0) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[VERIFYING] b v0.1.0 ([ROOT]/foo/b) +[COMPILING] b v0.1.0 ([ROOT]/foo/target/package/b-0.1.0) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[VERIFYING] c v0.1.0 ([ROOT]/foo/c) +[COMPILING] c v0.1.0 ([ROOT]/foo/target/package/c-0.1.0) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[UPLOADING] a v0.1.0 ([ROOT]/foo/a) +[ERROR] failed to publish `a` v0.1.0; the following crates have not been published yet: b v0.1.0, c v0.1.0 + +Caused by: + failed to publish to registry at http://127.0.0.1:[..]/ + +Caused by: + the remote server responded with an error (status 400 Bad Request): crate a@0.1.0 already exists on crates.io index + +"#]]) .run(); } From 051a16aa9e9f2d64b7dc7ba3c6a915beb85a6f81 Mon Sep 17 00:00:00 2001 From: Ojus Chugh Date: Fri, 22 Aug 2025 20:45:07 +0530 Subject: [PATCH 4/4] feat: publish: merge workspace context into single error message Avoid duplicate "failed to publish" text by combining workspace info with original error message. Fixes #15754. --- src/cargo/ops/registry/publish.rs | 114 ++++++++++++++---------------- tests/testsuite/publish.rs | 35 ++------- 2 files changed, 62 insertions(+), 87 deletions(-) diff --git a/src/cargo/ops/registry/publish.rs b/src/cargo/ops/registry/publish.rs index e47d35938d6..bc191ed5c65 100644 --- a/src/cargo/ops/registry/publish.rs +++ b/src/cargo/ops/registry/publish.rs @@ -238,68 +238,48 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { )?)); } - let transmit_result = if original_packages.len() > 1 { - // For workspace publishes, wrap transmit with enhanced error reporting - transmit( - opts.gctx, - ws, - pkg, - tarball.file(), - &mut registry, - source_ids.original, - opts.dry_run, - ).map_err(|e| { - // Collect remaining packages that have not been published yet - 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(); - // Also include any packages that are still waiting for confirmation - for id in to_confirm.iter().filter(|id| **id != pkg_id) { + // 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; - let entry = format!("{} v{}", pkg.name(), pkg.version()); - if !remaining.contains(&entry) { - remaining.push(entry); - } + format!("{} v{}", pkg.name(), pkg.version()) + }) + .collect(); + // 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); } + } - let message = if !remaining.is_empty() { - format!( - "failed to publish `{}` v{}; the following crates have not been published yet: {}", - pkg.name(), - pkg.version(), - remaining.join(", ") - ) - } else { - format!("failed to publish `{}` v{}", pkg.name(), pkg.version()) - }; - - e.context(message) - }) - } else { - // For single package publishes, preserve original top-level error message with package name - transmit( - opts.gctx, - ws, - pkg, - tarball.file(), - &mut registry, - source_ids.original, - opts.dry_run, - ) - .map_err(|e| { - e.context(format!( - "failed to publish `{}` v{}", - pkg.name(), - pkg.version() + 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); } @@ -692,6 +672,7 @@ fn transmit( registry: &mut Registry, registry_id: SourceId, dry_run: bool, + workspace_context: Option, ) -> CargoResult<()> { let new_crate = prepare_transmit(gctx, ws, pkg, registry_id)?; @@ -701,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() + ) + } + })?; if !warnings.invalid_categories.is_empty() { let msg = format!( diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index 5e1e94860e4..03a94176d38 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -2261,10 +2261,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 `foo` v0.0.1 - -Caused by: - 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 @@ -2312,10 +2309,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 `foo` v0.0.1 - -Caused by: - 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 @@ -2363,10 +2357,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 `foo` v0.0.1 - -Caused by: - 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 @@ -2423,10 +2414,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 `foo` v0.0.1 - -Caused by: - 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) @@ -2474,10 +2462,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 `foo` v0.0.1 - -Caused by: - 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: invalid response body from server @@ -3624,10 +3609,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 `foo` v0.0.1 - -Caused by: - 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. @@ -4464,10 +4446,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [COMPILING] c v0.1.0 ([ROOT]/foo/target/package/c-0.1.0) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [UPLOADING] a v0.1.0 ([ROOT]/foo/a) -[ERROR] failed to publish `a` v0.1.0; the following crates have not been published yet: b v0.1.0, c v0.1.0 - -Caused by: - failed to publish to registry at http://127.0.0.1:[..]/ +[ERROR] failed to publish `a` v0.1.0 to registry at http://127.0.0.1:[..]/; the following crates have not been published yet: b v0.1.0, c v0.1.0 Caused by: the remote server responded with an error (status 400 Bad Request): crate a@0.1.0 already exists on crates.io index