Skip to content

Commit 3b28b0a

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. The timeout was 5min but I dropped it to 1m. Unfortunately, I don't have data from `cargo-release` to know what a reasonable timeout is, so going ahead and dropping to 60s and assuming anything more is an outage. Fixes rust-lang#9507
1 parent 863ed32 commit 3b28b0a

File tree

7 files changed

+105
-46
lines changed

7 files changed

+105
-46
lines changed

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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ 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;
2223
use crate::core::resolver::CliFeatures;
2324
use crate::core::source::Source;
25+
use crate::core::QueryKind;
2426
use crate::core::{Package, SourceId, Workspace};
2527
use crate::ops;
2628
use crate::ops::Packages;
@@ -183,6 +185,10 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
183185
reg_id,
184186
opts.dry_run,
185187
)?;
188+
if !opts.dry_run {
189+
let timeout = std::time::Duration::from_secs(60);
190+
wait_for_publish(opts.config, reg_id, pkg, timeout)?;
191+
}
186192

187193
Ok(())
188194
}
@@ -374,6 +380,70 @@ fn transmit(
374380
Ok(())
375381
}
376382

383+
fn wait_for_publish(
384+
config: &Config,
385+
registry_src: SourceId,
386+
pkg: &Package,
387+
timeout: std::time::Duration,
388+
) -> CargoResult<()> {
389+
let version_req = format!("={}", pkg.version());
390+
let mut source = registry_src.load(config, &HashSet::new())?;
391+
let source_description = source.describe();
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+
{
399+
let _lock = config.acquire_package_cache_lock()?;
400+
// Force re-fetching the source
401+
//
402+
// As pulling from a git source is expensive, we track when we've done it within the
403+
// process to only do it once, but we are one of the rare cases that needs to do it
404+
// multiple times
405+
config.updated_sources().remove(&registry_src);
406+
source.invalidate_cache();
407+
let summaries = loop {
408+
// Exact to avoid returning all for path/git
409+
match source.query_vec(&query, QueryKind::Exact) {
410+
std::task::Poll::Ready(res) => {
411+
break res?;
412+
}
413+
std::task::Poll::Pending => source.block_until_ready()?,
414+
}
415+
};
416+
if !summaries.is_empty() {
417+
break;
418+
}
419+
}
420+
421+
if timeout < now.elapsed() {
422+
config.shell().warn(format!(
423+
"timed out waiting for `{}` to be in {}",
424+
pkg.name(),
425+
source_description
426+
))?;
427+
break;
428+
}
429+
430+
if !logged {
431+
config.shell().status(
432+
"Waiting",
433+
format!(
434+
"on `{}` to propagate to {} (ctrl-c to wait asynchronously)",
435+
pkg.name(),
436+
source_description
437+
),
438+
)?;
439+
logged = true;
440+
}
441+
std::thread::sleep(sleep_time);
442+
}
443+
444+
Ok(())
445+
}
446+
377447
/// Returns the index and token from the config file for the given registry.
378448
///
379449
/// `registry` is typically the registry specified on the command-line. If

tests/testsuite/artifact_dep.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1920,6 +1920,7 @@ fn publish_artifact_dep() {
19201920
[UPDATING] [..]
19211921
[PACKAGING] foo v0.1.0 [..]
19221922
[UPLOADING] foo v0.1.0 [..]
1923+
[UPDATING] [..]
19231924
",
19241925
)
19251926
.run();

tests/testsuite/credential_process.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ Only one of these values may be set, remove one or the other to proceed.
125125
[UPDATING] [..]
126126
[PACKAGING] foo v0.1.0 [..]
127127
[UPLOADING] foo v0.1.0 [..]
128+
[UPDATING] [..]
128129
",
129130
)
130131
.run();
@@ -198,6 +199,7 @@ fn publish() {
198199
[UPDATING] [..]
199200
[PACKAGING] foo v0.1.0 [..]
200201
[UPLOADING] foo v0.1.0 [..]
202+
[UPDATING] [..]
201203
",
202204
)
203205
.run();

tests/testsuite/features_namespaced.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,7 @@ fn publish_no_implicit() {
903903
[UPDATING] [..]
904904
[PACKAGING] foo v0.1.0 [..]
905905
[UPLOADING] foo v0.1.0 [..]
906+
[UPDATING] [..]
906907
",
907908
)
908909
.run();
@@ -1029,6 +1030,7 @@ fn publish() {
10291030
[COMPILING] foo v0.1.0 [..]
10301031
[FINISHED] [..]
10311032
[UPLOADING] foo v0.1.0 [..]
1033+
[UPDATING] [..]
10321034
",
10331035
)
10341036
.run();

tests/testsuite/publish.rs

Lines changed: 28 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ fn simple() {
123123
See [..]
124124
[PACKAGING] foo v0.0.1 ([CWD])
125125
[UPLOADING] foo v0.0.1 ([CWD])
126+
[UPDATING] [..]
126127
",
127128
)
128129
.run();
@@ -176,6 +177,7 @@ fn old_token_location() {
176177
See [..]
177178
[PACKAGING] foo v0.0.1 ([CWD])
178179
[UPLOADING] foo v0.0.1 ([CWD])
180+
[UPDATING] [..]
179181
",
180182
)
181183
.run();
@@ -410,7 +412,9 @@ fn publish_clean() {
410412
[VERIFYING] foo v0.0.1 ([CWD])
411413
[..]
412414
[..]
413-
[UPLOADING] foo v0.0.1 ([CWD])",
415+
[UPLOADING] foo v0.0.1 ([CWD])
416+
[UPDATING] [..]
417+
",
414418
)
415419
.run();
416420

@@ -453,7 +457,9 @@ fn publish_in_sub_repo() {
453457
[VERIFYING] foo v0.0.1 ([CWD])
454458
[..]
455459
[..]
456-
[UPLOADING] foo v0.0.1 ([CWD])",
460+
[UPLOADING] foo v0.0.1 ([CWD])
461+
[UPDATING] [..]
462+
",
457463
)
458464
.run();
459465

@@ -496,7 +502,9 @@ fn publish_when_ignored() {
496502
[VERIFYING] foo v0.0.1 ([CWD])
497503
[..]
498504
[..]
499-
[UPLOADING] foo v0.0.1 ([CWD])",
505+
[UPLOADING] foo v0.0.1 ([CWD])
506+
[UPDATING] [..]
507+
",
500508
)
501509
.run();
502510

@@ -538,7 +546,9 @@ fn ignore_when_crate_ignored() {
538546
[VERIFYING] foo v0.0.1 ([CWD])
539547
[..]
540548
[..]
541-
[UPLOADING] foo v0.0.1 ([CWD])",
549+
[UPLOADING] foo v0.0.1 ([CWD])
550+
[UPDATING] [..]
551+
",
542552
)
543553
.run();
544554

@@ -2240,7 +2250,9 @@ fn http_api_not_noop() {
22402250
[VERIFYING] foo v0.0.1 ([CWD])
22412251
[..]
22422252
[..]
2243-
[UPLOADING] foo v0.0.1 ([CWD])",
2253+
[UPLOADING] foo v0.0.1 ([CWD])
2254+
[UPDATING] [..]
2255+
",
22442256
)
22452257
.run();
22462258

@@ -2266,7 +2278,7 @@ fn http_api_not_noop() {
22662278
}
22672279

22682280
#[cargo_test]
2269-
fn delayed_publish_errors() {
2281+
fn wait_for_publish() {
22702282
// Counter for number of tries before the package is "published"
22712283
let arc: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
22722284
let arc2 = arc.clone();
@@ -2322,13 +2334,15 @@ Use the --token command-line flag to remove this warning.
23222334
See [..]
23232335
[PACKAGING] delay v0.0.1 ([CWD])
23242336
[UPLOADING] delay v0.0.1 ([CWD])
2337+
[UPDATING] `dummy-registry` index
2338+
[WAITING] on `delay` to propagate to `dummy-registry` index (ctrl-c to wait asynchronously)
23252339
",
23262340
)
23272341
.run();
23282342

2329-
// Check nothing has touched the responder
2343+
// Verify the responder has been pinged
23302344
let lock = arc2.lock().unwrap();
2331-
assert_eq!(*lock, 0);
2345+
assert_eq!(*lock, 2);
23322346
drop(lock);
23332347

23342348
let p = project()
@@ -2346,23 +2360,6 @@ See [..]
23462360
.file("src/main.rs", "fn main() {}")
23472361
.build();
23482362

2349-
p.cargo("build -Z sparse-registry")
2350-
.masquerade_as_nightly_cargo(&["sparse-registry"])
2351-
.with_status(101)
2352-
.with_stderr(
2353-
"\
2354-
[UPDATING] [..]
2355-
[ERROR] no matching package named `delay` found
2356-
location searched: registry `crates-io`
2357-
required by package `foo v0.0.1 ([..]/foo)`
2358-
",
2359-
)
2360-
.run();
2361-
2362-
let lock = arc2.lock().unwrap();
2363-
assert_eq!(*lock, 1);
2364-
drop(lock);
2365-
23662363
p.cargo("build -Z sparse-registry")
23672364
.masquerade_as_nightly_cargo(&["sparse-registry"])
23682365
.with_status(0)
@@ -2373,7 +2370,7 @@ required by package `foo v0.0.1 ([..]/foo)`
23732370
/// the responder twice per cargo invocation. If that ever gets changed
23742371
/// this test will need to be changed accordingly.
23752372
#[cargo_test]
2376-
fn delayed_publish_errors_underscore() {
2373+
fn wait_for_publish_underscore() {
23772374
// Counter for number of tries before the package is "published"
23782375
let arc: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
23792376
let arc2 = arc.clone();
@@ -2429,13 +2426,16 @@ Use the --token command-line flag to remove this warning.
24292426
See [..]
24302427
[PACKAGING] delay_with_underscore v0.0.1 ([CWD])
24312428
[UPLOADING] delay_with_underscore v0.0.1 ([CWD])
2429+
[UPDATING] `dummy-registry` index
2430+
[WAITING] on `delay_with_underscore` to propagate to `dummy-registry` index (ctrl-c to wait asynchronously)
24322431
",
24332432
)
24342433
.run();
24352434

2436-
// Check nothing has touched the responder
2435+
// Verify the repsponder has been pinged
24372436
let lock = arc2.lock().unwrap();
2438-
assert_eq!(*lock, 0);
2437+
// NOTE: package names with - or _ hit the responder twice per cargo invocation
2438+
assert_eq!(*lock, 3);
24392439
drop(lock);
24402440

24412441
let p = project()
@@ -2453,24 +2453,6 @@ See [..]
24532453
.file("src/main.rs", "fn main() {}")
24542454
.build();
24552455

2456-
p.cargo("build -Z sparse-registry")
2457-
.masquerade_as_nightly_cargo(&["sparse-registry"])
2458-
.with_status(101)
2459-
.with_stderr(
2460-
"\
2461-
[UPDATING] [..]
2462-
[ERROR] no matching package named `delay_with_underscore` found
2463-
location searched: registry `crates-io`
2464-
required by package `foo v0.0.1 ([..]/foo)`
2465-
",
2466-
)
2467-
.run();
2468-
2469-
let lock = arc2.lock().unwrap();
2470-
// package names with - or _ hit the responder twice per cargo invocation
2471-
assert_eq!(*lock, 2);
2472-
drop(lock);
2473-
24742456
p.cargo("build -Z sparse-registry")
24752457
.masquerade_as_nightly_cargo(&["sparse-registry"])
24762458
.with_status(0)

tests/testsuite/weak_dep_features.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,7 @@ fn publish() {
569569
[COMPILING] foo v0.1.0 [..]
570570
[FINISHED] [..]
571571
[UPLOADING] foo v0.1.0 [..]
572+
[UPDATING] [..]
572573
",
573574
)
574575
.run();

0 commit comments

Comments
 (0)