Skip to content

Commit 3933bf6

Browse files
committed
fix(publish): Block until it is in index
Originally, crates.io would block on publish requests until the publish was complete, giving `cargo publish` this behavior by extension. When crates.io switched to asynchronous publishing, this intermittently broke people's workflows when publishing multiple crates. I say interittent because it usually works until it doesn't and it is unclear why to the end user because it will be published by the time they check. In the end, callers tend to either put in timeouts (and pray), poll the server's API, or use `crates-index` crate to poll the index. This isn't sufficient because - For any new interested party, this is a pit of failure they'll fall into - crates-index has re-implemented index support incorrectly in the past, currently doesn't handle auth, doesn't support `git-cli`, etc. - None of these previous options work if we were to implement workspace-publish support (rust-lang#1169) - The new sparse registry might increase the publish times, making the delay easier to hit manually - The new sparse registry goes through CDNs so checking the server's API might not be sufficient - Once the sparse registry is available, crates-index users will find out when the package is ready in git but it might not be ready through the sparse registry because of CDNs So now `cargo` will block until it sees the package in the index. - This is checking via the index instead of server APIs in case there are propagation delays. This has the side effect of being noisy because of all of the "Updating index" messages. - This is done unconditionally because cargo used to block and that didn't seem to be a problem, blocking by default is the less error prone case, and there doesn't seem to be enough justification for a "don't block" flag. Fixes rust-lang#9507
1 parent 566a3fa commit 3933bf6

File tree

3 files changed

+77
-41
lines changed

3 files changed

+77
-41
lines changed

crates/cargo-test-support/src/compare.rs

+1
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ fn substitute_macros(input: &str) -> String {
197197
("[MIGRATING]", " Migrating"),
198198
("[EXECUTABLE]", " Executable"),
199199
("[SKIPPING]", " Skipping"),
200+
("[WAITING]", " Waiting"),
200201
];
201202
let mut result = input.to_owned();
202203
for &(pat, subst) in &macros {

src/cargo/ops/registry.rs

+63
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,13 @@ use termcolor::Color::Green;
1818
use termcolor::ColorSpec;
1919

2020
use crate::core::dependency::DepKind;
21+
use crate::core::dependency::Dependency;
2122
use crate::core::manifest::ManifestMetadata;
23+
use crate::core::registry::PackageRegistry;
24+
use crate::core::registry::Registry as _;
2225
use crate::core::resolver::CliFeatures;
2326
use crate::core::source::Source;
27+
use crate::core::QueryKind;
2428
use crate::core::{Package, SourceId, Workspace};
2529
use crate::ops;
2630
use crate::ops::Packages;
@@ -183,6 +187,10 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
183187
reg_id,
184188
opts.dry_run,
185189
)?;
190+
if !opts.dry_run {
191+
let timeout = std::time::Duration::from_secs(300);
192+
wait_for_publish(opts.config, reg_id, pkg, timeout)?;
193+
}
186194

187195
Ok(())
188196
}
@@ -374,6 +382,61 @@ fn transmit(
374382
Ok(())
375383
}
376384

385+
fn wait_for_publish(
386+
config: &Config,
387+
registry_src: SourceId,
388+
pkg: &Package,
389+
timeout: std::time::Duration,
390+
) -> CargoResult<()> {
391+
let version_req = format!("={}", pkg.version());
392+
let query = Dependency::parse(pkg.name(), Some(&version_req), registry_src)?;
393+
394+
let now = std::time::Instant::now();
395+
let sleep_time = std::time::Duration::from_secs(1);
396+
let mut logged = false;
397+
loop {
398+
let registry_description;
399+
{
400+
let mut registry = PackageRegistry::new(config)?;
401+
let _lock = config.acquire_package_cache_lock()?;
402+
registry.lock_patches();
403+
registry_description = registry.describe_source(registry_src);
404+
405+
let summaries = loop {
406+
// Exact to avoid returning all for path/git
407+
match registry.query_vec(&query, QueryKind::Exact) {
408+
std::task::Poll::Ready(res) => {
409+
break res?;
410+
}
411+
std::task::Poll::Pending => registry.block_until_ready()?,
412+
}
413+
};
414+
if !summaries.is_empty() {
415+
break;
416+
}
417+
}
418+
419+
if timeout < now.elapsed() {
420+
config.shell().warn(format!(
421+
"timed out waiting for publishing of {} to complete",
422+
pkg.name(),
423+
))?;
424+
break;
425+
}
426+
427+
if !logged {
428+
config.shell().status(
429+
"Waiting",
430+
format!("on {} to finish publish", registry_description),
431+
)?;
432+
logged = true;
433+
}
434+
std::thread::sleep(sleep_time);
435+
}
436+
437+
Ok(())
438+
}
439+
377440
/// Returns the index and token from the config file for the given registry.
378441
///
379442
/// `registry` is typically the registry specified on the command-line. If

tests/testsuite/publish.rs

+13-41
Original file line numberDiff line numberDiff line change
@@ -2051,7 +2051,7 @@ error: package ID specification `bar` did not match any packages
20512051
}
20522052

20532053
#[cargo_test]
2054-
fn delayed_publish_errors() {
2054+
fn wait_for_publish() {
20552055
// Counter for number of tries before the package is "published"
20562056
let arc: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
20572057
let arc2 = arc.clone();
@@ -2107,13 +2107,16 @@ Use the --token command-line flag to remove this warning.
21072107
See [..]
21082108
[PACKAGING] delay v0.0.1 ([CWD])
21092109
[UPLOADING] delay v0.0.1 ([CWD])
2110+
[UPDATING] `dummy-registry` index
2111+
[WAITING] on registry `dummy-registry` to finish publish
2112+
[UPDATING] `dummy-registry` index
21102113
",
21112114
)
21122115
.run();
21132116

2114-
// Check nothing has touched the responder
2117+
// Verify the responder has been pinged
21152118
let lock = arc2.lock().unwrap();
2116-
assert_eq!(*lock, 0);
2119+
assert_eq!(*lock, 2);
21172120
drop(lock);
21182121

21192122
let p = project()
@@ -2131,23 +2134,6 @@ See [..]
21312134
.file("src/main.rs", "fn main() {}")
21322135
.build();
21332136

2134-
p.cargo("build -Z sparse-registry")
2135-
.masquerade_as_nightly_cargo(&["sparse-registry"])
2136-
.with_status(101)
2137-
.with_stderr(
2138-
"\
2139-
[UPDATING] [..]
2140-
[ERROR] no matching package named `delay` found
2141-
location searched: registry `crates-io`
2142-
required by package `foo v0.0.1 ([..]/foo)`
2143-
",
2144-
)
2145-
.run();
2146-
2147-
let lock = arc2.lock().unwrap();
2148-
assert_eq!(*lock, 1);
2149-
drop(lock);
2150-
21512137
p.cargo("build -Z sparse-registry")
21522138
.masquerade_as_nightly_cargo(&["sparse-registry"])
21532139
.with_status(0)
@@ -2158,7 +2144,7 @@ required by package `foo v0.0.1 ([..]/foo)`
21582144
/// the responder twice per cargo invocation. If that ever gets changed
21592145
/// this test will need to be changed accordingly.
21602146
#[cargo_test]
2161-
fn delayed_publish_errors_underscore() {
2147+
fn wait_for_publish_underscore() {
21622148
// Counter for number of tries before the package is "published"
21632149
let arc: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
21642150
let arc2 = arc.clone();
@@ -2214,13 +2200,17 @@ Use the --token command-line flag to remove this warning.
22142200
See [..]
22152201
[PACKAGING] delay_with_underscore v0.0.1 ([CWD])
22162202
[UPLOADING] delay_with_underscore v0.0.1 ([CWD])
2203+
[UPDATING] `dummy-registry` index
2204+
[WAITING] on registry `dummy-registry` to finish publish
2205+
[UPDATING] `dummy-registry` index
22172206
",
22182207
)
22192208
.run();
22202209

2221-
// Check nothing has touched the responder
2210+
// Verify the repsponder has been pinged
22222211
let lock = arc2.lock().unwrap();
2223-
assert_eq!(*lock, 0);
2212+
// NOTE: package names with - or _ hit the responder twice per cargo invocation
2213+
assert_eq!(*lock, 3);
22242214
drop(lock);
22252215

22262216
let p = project()
@@ -2238,24 +2228,6 @@ See [..]
22382228
.file("src/main.rs", "fn main() {}")
22392229
.build();
22402230

2241-
p.cargo("build -Z sparse-registry")
2242-
.masquerade_as_nightly_cargo(&["sparse-registry"])
2243-
.with_status(101)
2244-
.with_stderr(
2245-
"\
2246-
[UPDATING] [..]
2247-
[ERROR] no matching package named `delay_with_underscore` found
2248-
location searched: registry `crates-io`
2249-
required by package `foo v0.0.1 ([..]/foo)`
2250-
",
2251-
)
2252-
.run();
2253-
2254-
let lock = arc2.lock().unwrap();
2255-
// package names with - or _ hit the responder twice per cargo invocation
2256-
assert_eq!(*lock, 2);
2257-
drop(lock);
2258-
22592231
p.cargo("build -Z sparse-registry")
22602232
.masquerade_as_nightly_cargo(&["sparse-registry"])
22612233
.with_status(0)

0 commit comments

Comments
 (0)