Skip to content

Commit 6f93cdc

Browse files
authored
Merge pull request #2002 from jonhoo/skip-missing
Correctly handle update across missing nightlies
2 parents 279e802 + fd042e9 commit 6f93cdc

File tree

6 files changed

+80
-19
lines changed

6 files changed

+80
-19
lines changed

src/dist/dist.rs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,7 @@ pub fn update_from_dist<'a>(
564564
profile: Option<Profile>,
565565
prefix: &InstallPrefix,
566566
force_update: bool,
567+
old_date: Option<&str>,
567568
) -> Result<Option<String>> {
568569
let fresh_install = !prefix.path().exists();
569570
let hash_exists = update_hash.map(Path::exists).unwrap_or(false);
@@ -582,6 +583,7 @@ pub fn update_from_dist<'a>(
582583
profile,
583584
prefix,
584585
force_update,
586+
old_date,
585587
);
586588

587589
// Don't leave behind an empty / broken installation directory
@@ -600,11 +602,29 @@ fn update_from_dist_<'a>(
600602
profile: Option<Profile>,
601603
prefix: &InstallPrefix,
602604
force_update: bool,
605+
old_date: Option<&str>,
603606
) -> Result<Option<String>> {
604607
let mut toolchain = toolchain.clone();
605608
let mut fetched = String::new();
606609
let mut first_err = None;
607610
let backtrack = toolchain.channel == "nightly" && toolchain.date.is_none();
611+
612+
// We never want to backtrack further back than the nightly that's already installed.
613+
//
614+
// If no nightly is installed, it makes no sense to backtrack beyond the first ever manifest,
615+
// which is 2014-12-20 according to
616+
// https://static.rust-lang.org/cargo-dist/index.html.
617+
//
618+
// We could arguably use the date of the first rustup release here, but that would break a
619+
// bunch of the tests, which (inexplicably) use 2015-01-01 as their manifest dates.
620+
let first_manifest = old_date
621+
.map(|date| {
622+
Utc.from_utc_date(
623+
&NaiveDate::parse_from_str(date, "%Y-%m-%d").expect("Malformed manifest date"),
624+
)
625+
})
626+
.unwrap_or_else(|| Utc.from_utc_date(&NaiveDate::from_ymd(2014, 12, 20)));
627+
608628
loop {
609629
match try_update_from_dist_(
610630
download,
@@ -629,6 +649,9 @@ fn update_from_dist_<'a>(
629649
if first_err.is_none() {
630650
first_err = Some(e);
631651
}
652+
} else if let ErrorKind::MissingReleaseForToolchain(..) = e.kind() {
653+
// no need to even print anything for missing nightlies,
654+
// since we don't really "skip" them
632655
} else if let Some(e) = first_err {
633656
// if we fail to find a suitable nightly, we abort the search and give the
634657
// original "components unavailable for download" error.
@@ -641,23 +664,28 @@ fn update_from_dist_<'a>(
641664
// the components that the user currently has installed. Let's try the previous
642665
// nightlies in reverse chronological order until we find a nightly that does,
643666
// starting at one date earlier than the current manifest's date.
644-
//
645-
// NOTE: we don't need to explicitly check for the case where the next nightly to
646-
// try is _older_ than the current nightly, since we know that the user's current
647-
// nightlys supports the components they have installed, and thus would always
648-
// terminate the search.
649-
toolchain.date = Some(
650-
Utc.from_utc_date(
667+
let try_next = Utc
668+
.from_utc_date(
651669
&NaiveDate::parse_from_str(
652670
toolchain.date.as_ref().unwrap_or(&fetched),
653671
"%Y-%m-%d",
654672
)
655673
.expect("Malformed manifest date"),
656674
)
657-
.pred()
658-
.format("%Y-%m-%d")
659-
.to_string(),
660-
);
675+
.pred();
676+
677+
if try_next < first_manifest {
678+
// Wouldn't be an update if we go further back than the user's current nightly.
679+
if let Some(e) = first_err {
680+
break Err(e);
681+
} else {
682+
// In this case, all newer nightlies are missing, which means there are no
683+
// updates, so the user is already at the latest nightly.
684+
break Ok(None);
685+
}
686+
}
687+
688+
toolchain.date = Some(try_next.format("%Y-%m-%d").to_string());
661689
}
662690
}
663691
}
@@ -721,7 +749,9 @@ fn try_update_from_dist_<'a>(
721749
let manifest = match dl_v1_manifest(download, toolchain) {
722750
Ok(m) => m,
723751
Err(Error(crate::ErrorKind::DownloadNotExists { .. }, _)) => {
724-
return Err(format!("no release found for '{}'", toolchain.manifest_name()).into());
752+
return Err(Error::from(ErrorKind::MissingReleaseForToolchain(
753+
toolchain.manifest_name(),
754+
)));
725755
}
726756
Err(e @ Error(ErrorKind::ChecksumFailed { .. }, _)) => {
727757
return Err(e);

src/errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ error_chain! {
275275
description("missing package for the target of a rename")
276276
display("server sent a broken manifest: missing package for the target of a rename {}", name)
277277
}
278+
MissingReleaseForToolchain(name: String) {
279+
description("missing release for a toolchain")
280+
display("no release found for '{}'", name)
281+
}
278282
RequestedComponentsUnavailable(c: Vec<Component>, manifest: Manifest, toolchain: String) {
279283
description("some requested components are unavailable to download")
280284
display("{}", component_unavailable_msg(&c, &manifest, &toolchain))

src/install.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ pub enum InstallMethod<'a> {
2626
bool,
2727
// toolchain already exists
2828
bool,
29+
// currently installed date
30+
Option<&'a str>,
2931
),
3032
}
3133

@@ -54,7 +56,15 @@ impl<'a> InstallMethod<'a> {
5456
InstallMethod::tar_gz(src, path, &temp_cfg, notify_handler)?;
5557
Ok(true)
5658
}
57-
InstallMethod::Dist(toolchain, profile, update_hash, dl_cfg, force_update, exists) => {
59+
InstallMethod::Dist(
60+
toolchain,
61+
profile,
62+
update_hash,
63+
dl_cfg,
64+
force_update,
65+
exists,
66+
old_date,
67+
) => {
5868
let prefix = &InstallPrefix::from(path.to_owned());
5969
let maybe_new_hash = dist::update_from_dist(
6070
dl_cfg,
@@ -63,6 +73,7 @@ impl<'a> InstallMethod<'a> {
6373
if exists { None } else { profile },
6474
prefix,
6575
force_update,
76+
old_date,
6677
)?;
6778

6879
if let Some(hash) = maybe_new_hash {

src/toolchain.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,13 +164,15 @@ impl<'a> Toolchain<'a> {
164164

165165
pub fn install_from_dist(&self, force_update: bool) -> Result<UpdateStatus> {
166166
let update_hash = self.update_hash()?;
167+
let old_date = self.get_manifest().ok().and_then(|m| m.map(|m| m.date));
167168
self.install(InstallMethod::Dist(
168169
&self.desc()?,
169170
self.cfg.get_profile()?,
170171
update_hash.as_ref().map(|p| &**p),
171172
self.download_cfg(),
172173
force_update,
173174
self.exists(),
175+
old_date.as_ref().map(|s| &**s),
174176
))
175177
}
176178

@@ -183,6 +185,7 @@ impl<'a> Toolchain<'a> {
183185
self.download_cfg(),
184186
false,
185187
false,
188+
None,
186189
))
187190
}
188191
pub fn is_custom(&self) -> bool {
@@ -470,7 +473,7 @@ impl<'a> Toolchain<'a> {
470473
})
471474
}
472475

473-
pub fn show_version(&self) -> Result<Option<String>> {
476+
pub fn get_manifest(&self) -> Result<Option<Manifest>> {
474477
if !self.exists() {
475478
return Err(ErrorKind::ToolchainNotInstalled(self.name.to_owned()).into());
476479
}
@@ -481,7 +484,11 @@ impl<'a> Toolchain<'a> {
481484
let prefix = InstallPrefix::from(self.path.to_owned());
482485
let manifestation = Manifestation::open(prefix, toolchain.target.clone())?;
483486

484-
match manifestation.load_manifest()? {
487+
manifestation.load_manifest()
488+
}
489+
490+
pub fn show_version(&self) -> Result<Option<String>> {
491+
match self.get_manifest()? {
485492
Some(manifest) => Ok(Some(manifest.get_rust_version()?.to_string())),
486493
None => Ok(None),
487494
}

tests/cli-misc.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -771,14 +771,16 @@ fn update_nightly_even_with_incompat() {
771771

772772
#[test]
773773
fn nightly_backtrack_skips_missing() {
774-
clitools::setup(Scenario::Unavailable, &|config| {
775-
set_current_dist_date(config, "2015-01-01");
774+
clitools::setup(Scenario::MissingNightly, &|config| {
775+
set_current_dist_date(config, "2019-09-16");
776776
expect_ok(config, &["rustup", "default", "nightly"]);
777777

778778
expect_stdout_ok(config, &["rustc", "--version"], "hash-nightly-1");
779+
expect_ok(config, &["rustup", "component", "add", "rls"]);
780+
expect_component_executable(config, "rls");
779781

780-
// nightly is missing on latest
781-
set_current_dist_date(config, "2015-01-02");
782+
// rls is missing on latest, nightly is missing on second-to-latest
783+
set_current_dist_date(config, "2019-09-18");
782784

783785
// update should not change nightly, and should not error
784786
expect_ok(config, &["rustup", "update", "nightly", "--no-self-update"]);

tests/mock/clitools.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ pub enum Scenario {
5252
Unavailable, // Two dates, v2 manifests, everything unavailable in second date.
5353
UnavailableRls, // Two dates, v2 manifests, RLS unavailable in first date, restored on second.
5454
MissingComponent, // Three dates, v2 manifests, RLS available in first and last, not middle
55+
MissingNightly, // Three dates, v2 manifests, RLS available in first, middle missing nightly
5556
}
5657

5758
pub static CROSS_ARCH1: &str = "x86_64-unknown-linux-musl";
@@ -564,6 +565,11 @@ fn create_mock_dist_server(path: &Path, s: Scenario) {
564565
Release::new("nightly", "1.37.0", "2019-09-13", "2"),
565566
Release::new("nightly", "1.37.0", "2019-09-14", "3").with_rls(RlsStatus::Unavailable),
566567
],
568+
Scenario::MissingNightly => vec![
569+
Release::new("nightly", "1.37.0", "2019-09-16", "1"),
570+
Release::stable("1.37.0", "2019-09-17"),
571+
Release::new("nightly", "1.37.0", "2019-09-18", "2").with_rls(RlsStatus::Unavailable),
572+
],
567573
Scenario::Unavailable => vec![
568574
Release::new("nightly", "1.2.0", "2015-01-01", "1"),
569575
Release::beta("1.1.0", "2015-01-01"),
@@ -606,6 +612,7 @@ fn create_mock_dist_server(path: &Path, s: Scenario) {
606612
| Scenario::MultiHost
607613
| Scenario::Unavailable
608614
| Scenario::UnavailableRls
615+
| Scenario::MissingNightly
609616
| Scenario::MissingComponent => vec![ManifestVersion::V2],
610617
};
611618

0 commit comments

Comments
 (0)