Skip to content

Commit 3670473

Browse files
0xPoerbtcollins
authored andcommitted
Support self-update modes
test: clean up "--no-self-update" args test: add check-only and enable tests docs: update docs and add comment
1 parent a1d5912 commit 3670473

File tree

13 files changed

+427
-444
lines changed

13 files changed

+427
-444
lines changed

doc/src/basics.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,33 @@ info: downloading self-updates
3030

3131
## Keeping `rustup` up to date
3232

33-
Running `rustup update` also checks for updates to `rustup` itself and automatically
34-
installs the latest version. To manually update `rustup` only,
35-
without updating installed toolchains, type `rustup self update`:
33+
If your `rustup` was built with the `no-self-update` feature, it can not update
34+
itself. This is not the default, and only versions of `rustup` built with
35+
`--no-default-features`, or obtained from a third-party distributor who has
36+
disabled it (such as the Ubuntu snap store).
37+
38+
Otherwise Rustup can update itself. It is possible to control Rustup's automatic
39+
self update mechanism with the `auto-self-update` configuration variable. This
40+
setting supports three values: `enable` and `disable` and `check-only`.
41+
42+
* `disable` will ensure that no automatic self updating actions are taken.
43+
* `enable` will mean that `rustup update` and similar commands will also check for, and install, any update to Rustup.
44+
* `check-only` will cause any automatic self update to check and report on any updates, but not to automatically install them.
45+
46+
Whether `auto-self-update` is `enable` or not, you can request that Rustup
47+
update itself to the latest version of `rustup` by running `rustup self update`.
48+
This will not download new toolchains:
3649

3750
```console
3851
$ rustup self update
3952
info: checking for self-updates
4053
info: downloading self-updates
4154
```
4255

43-
> #### Disable automatic self-updates
44-
> `rustup` will automatically update itself at the end of any toolchain installation.
45-
> You can prevent this automatic behaviour by passing the `--no-self-update` argument
46-
> when running `rustup update` or `rustup toolchain install`.
56+
### Disabling self updates on a per-invocation basis
57+
> Self updates can also be suppressed on individual invocations of `rustup` by
58+
> passing the argurment `--no-self-update` when running `rustup update` or
59+
> `rustup toolchain install`.
4760
4861
## Help system
4962

src/cli/rustup_mode.rs

Lines changed: 43 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ use super::self_update;
1313
use super::term2;
1414
use super::term2::Terminal;
1515
use super::topical_doc;
16-
use super::{common, self_update::get_available_rustup_version};
16+
use super::{
17+
common,
18+
self_update::{check_rustup_update, SelfUpdateMode},
19+
};
20+
use crate::cli::errors::CLIError;
1721
use crate::dist::dist::{
1822
PartialTargetTriple, PartialToolchainDesc, Profile, TargetTriple, ToolchainDesc,
1923
};
@@ -185,6 +189,7 @@ pub fn main() -> Result<utils::ExitCode> {
185189
("set", Some(c)) => match c.subcommand() {
186190
("default-host", Some(m)) => set_default_host_triple(cfg, m)?,
187191
("profile", Some(m)) => set_profile(cfg, m)?,
192+
("auto-self-update", Some(m)) => set_auto_self_update(cfg, m)?,
188193
(_, _) => unreachable!(),
189194
},
190195
("completions", Some(c)) => {
@@ -707,6 +712,16 @@ pub fn cli() -> App<'static, 'static> {
707712
.possible_values(Profile::names())
708713
.default_value(Profile::default_name()),
709714
),
715+
)
716+
.subcommand(
717+
SubCommand::with_name("auto-self-update")
718+
.about("The rustup auto self update mode")
719+
.arg(
720+
Arg::with_name("auto-self-update-mode")
721+
.required(true)
722+
.possible_values(SelfUpdateMode::modes())
723+
.default_value(SelfUpdateMode::default_mode()),
724+
),
710725
),
711726
);
712727

@@ -911,31 +926,20 @@ fn check_updates(cfg: &Cfg) -> Result<utils::ExitCode> {
911926
}
912927
}
913928

914-
// Get current rustup version
915-
let current_version = env!("CARGO_PKG_VERSION");
916-
917-
// Get available rustup version
918-
let available_version = get_available_rustup_version()?;
929+
check_rustup_update()?;
919930

920-
let _ = t.attr(term2::Attr::Bold);
921-
write!(t, "rustup - ")?;
922-
923-
if current_version != available_version {
924-
let _ = t.fg(term2::color::YELLOW);
925-
write!(t, "Update available")?;
926-
let _ = t.reset();
927-
writeln!(t, " : {} -> {}", current_version, available_version)?;
928-
} else {
929-
let _ = t.fg(term2::color::GREEN);
930-
write!(t, "Up to date")?;
931-
let _ = t.reset();
932-
writeln!(t, " : {}", current_version)?;
933-
}
934931
Ok(utils::ExitCode(0))
935932
}
936933

937934
fn update(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result<utils::ExitCode> {
938-
let self_update = !m.is_present("no-self-update") && !self_update::NEVER_SELF_UPDATE;
935+
let self_update_mode = cfg.get_self_update_mode()?;
936+
// Priority: no-self-update feature > self_update_mode > no-self-update args.
937+
// Update only if rustup does **not** have the no-self-update feature,
938+
// and auto-self-update is configured to **enable**
939+
// and has **no** no-self-update parameter.
940+
let self_update = !self_update::NEVER_SELF_UPDATE
941+
&& self_update_mode == SelfUpdateMode::Enable
942+
&& !m.is_present("no-self-update");
939943
let forced = m.is_present("force-non-host");
940944
if let Some(p) = m.value_of("profile") {
941945
let p = Profile::from_str(p)?;
@@ -1021,6 +1025,10 @@ fn update(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result<utils::ExitCode> {
10211025
cfg.temp_cfg.clean();
10221026
}
10231027

1028+
if !self_update::NEVER_SELF_UPDATE && self_update_mode == SelfUpdateMode::CheckOnly {
1029+
check_rustup_update()?;
1030+
}
1031+
10241032
Ok(utils::ExitCode(0))
10251033
}
10261034

@@ -1578,6 +1586,20 @@ fn set_profile(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result<utils::ExitCode> {
15781586
Ok(utils::ExitCode(0))
15791587
}
15801588

1589+
fn set_auto_self_update(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result<utils::ExitCode> {
1590+
if self_update::NEVER_SELF_UPDATE {
1591+
let mut args = crate::process().args_os();
1592+
let arg0 = args.next().map(PathBuf::from);
1593+
let arg0 = arg0
1594+
.as_ref()
1595+
.and_then(|a| a.to_str())
1596+
.ok_or(CLIError::NoExeName)?;
1597+
warn!("{} is built with the no-self-update feature: setting auto-self-update will not have any effect.",arg0);
1598+
}
1599+
cfg.set_auto_self_update(&m.value_of("auto-self-update-mode").unwrap())?;
1600+
Ok(utils::ExitCode(0))
1601+
}
1602+
15811603
fn show_profile(cfg: &Cfg) -> Result<utils::ExitCode> {
15821604
writeln!(process().stdout(), "{}", cfg.get_profile()?)?;
15831605
Ok(utils::ExitCode(0))

src/cli/self_update.rs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ use std::borrow::Cow;
4848
use std::env;
4949
use std::env::consts::EXE_SUFFIX;
5050
use std::fs;
51+
use std::io::Write;
5152
use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
5253
use std::process::Command;
54+
use std::str::FromStr;
5355

5456
use anyhow::{anyhow, Context, Result};
5557
use cfg_if::cfg_if;
@@ -59,6 +61,7 @@ use super::common::{self, ignorable_error, Confirm};
5961
use super::errors::*;
6062
use super::markdown::md;
6163
use super::term2;
64+
use crate::cli::term2::Terminal;
6265
use crate::dist::dist::{self, Profile, TargetTriple};
6366
use crate::process;
6467
use crate::toolchain::{DistributableToolchain, Toolchain};
@@ -86,6 +89,51 @@ pub const NEVER_SELF_UPDATE: bool = true;
8689
#[cfg(not(feature = "no-self-update"))]
8790
pub const NEVER_SELF_UPDATE: bool = false;
8891

92+
#[derive(Clone, Debug, PartialEq)]
93+
pub enum SelfUpdateMode {
94+
Enable,
95+
Disable,
96+
CheckOnly,
97+
}
98+
99+
impl SelfUpdateMode {
100+
pub fn modes() -> &'static [&'static str] {
101+
&["enable", "disable", "check-only"]
102+
}
103+
104+
pub fn default_mode() -> &'static str {
105+
"enable"
106+
}
107+
}
108+
109+
impl FromStr for SelfUpdateMode {
110+
type Err = anyhow::Error;
111+
112+
fn from_str(mode: &str) -> Result<Self> {
113+
match mode {
114+
"enable" => Ok(Self::Enable),
115+
"disable" => Ok(Self::Disable),
116+
"check-only" => Ok(Self::CheckOnly),
117+
_ => Err(anyhow!(format!(
118+
"unknown self update mode: '{}'; valid modes are {}",
119+
mode,
120+
valid_self_update_modes(),
121+
))),
122+
}
123+
}
124+
}
125+
126+
impl ToString for SelfUpdateMode {
127+
fn to_string(&self) -> String {
128+
match self {
129+
SelfUpdateMode::Enable => "enable",
130+
SelfUpdateMode::Disable => "disable",
131+
SelfUpdateMode::CheckOnly => "check-only",
132+
}
133+
.into()
134+
}
135+
}
136+
89137
// The big installation messages. These are macros because the first
90138
// argument of format! needs to be a literal.
91139

@@ -491,7 +539,6 @@ fn do_pre_install_sanity_checks(no_prompt: bool) -> Result<()> {
491539
}
492540

493541
fn do_pre_install_options_sanity_checks(opts: &InstallOpts<'_>) -> Result<()> {
494-
use std::str::FromStr;
495542
// Verify that the installation options are vaguely sane
496543
(|| {
497544
let host_triple = opts
@@ -1103,6 +1150,32 @@ pub fn get_available_rustup_version() -> Result<String> {
11031150
Ok(String::from(available_version))
11041151
}
11051152

1153+
pub fn check_rustup_update() -> Result<()> {
1154+
let mut t = term2::stdout();
1155+
// Get current rustup version
1156+
let current_version = env!("CARGO_PKG_VERSION");
1157+
1158+
// Get available rustup version
1159+
let available_version = get_available_rustup_version()?;
1160+
1161+
let _ = t.attr(term2::Attr::Bold);
1162+
write!(t, "rustup - ")?;
1163+
1164+
if current_version != available_version {
1165+
let _ = t.fg(term2::color::YELLOW);
1166+
write!(t, "Update available")?;
1167+
let _ = t.reset();
1168+
writeln!(t, " : {} -> {}", current_version, available_version)?;
1169+
} else {
1170+
let _ = t.fg(term2::color::GREEN);
1171+
write!(t, "Up to date")?;
1172+
let _ = t.reset();
1173+
writeln!(t, " : {}", current_version)?;
1174+
}
1175+
1176+
Ok(())
1177+
}
1178+
11061179
pub fn cleanup_self_updater() -> Result<()> {
11071180
let cargo_home = utils::cargo_home()?;
11081181
let setup = cargo_home.join(&format!("bin/rustup-init{}", EXE_SUFFIX));
@@ -1114,6 +1187,14 @@ pub fn cleanup_self_updater() -> Result<()> {
11141187
Ok(())
11151188
}
11161189

1190+
pub fn valid_self_update_modes() -> String {
1191+
SelfUpdateMode::modes()
1192+
.iter()
1193+
.map(|s| format!("'{}'", s))
1194+
.collect::<Vec<_>>()
1195+
.join(", ")
1196+
}
1197+
11171198
#[cfg(test)]
11181199
mod tests {
11191200
use std::collections::HashMap;

src/config.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use pgp::{Deserializable, SignedPublicKey};
1111
use serde::Deserialize;
1212
use thiserror::Error as ThisError;
1313

14+
use crate::cli::self_update::SelfUpdateMode;
1415
use crate::dist::download::DownloadCfg;
1516
use crate::dist::{
1617
dist::{self, valid_profile_names},
@@ -405,6 +406,20 @@ impl Cfg {
405406
Ok(())
406407
}
407408

409+
pub fn set_auto_self_update(&mut self, mode: &str) -> Result<()> {
410+
match SelfUpdateMode::from_str(mode) {
411+
Ok(update_mode) => {
412+
self.settings_file.with_mut(|s| {
413+
s.auto_self_update = Some(update_mode);
414+
Ok(())
415+
})?;
416+
(self.notify_handler)(Notification::SetSelfUpdate(mode));
417+
Ok(())
418+
}
419+
Err(err) => Err(err),
420+
}
421+
}
422+
408423
pub fn set_toolchain_override(&mut self, toolchain_override: &str) {
409424
self.toolchain_override = Some(toolchain_override.to_owned());
410425
}
@@ -430,6 +445,16 @@ impl Cfg {
430445
})
431446
}
432447

448+
pub fn get_self_update_mode(&self) -> Result<SelfUpdateMode> {
449+
self.settings_file.with(|s| {
450+
let mode = match &s.auto_self_update {
451+
Some(mode) => mode.clone(),
452+
None => SelfUpdateMode::Enable,
453+
};
454+
Ok(mode)
455+
})
456+
}
457+
433458
pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result<Toolchain<'_>> {
434459
if create_parent {
435460
utils::ensure_dir_exists("toolchains", &self.toolchains_dir, &|n| {

src/notifications.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ pub enum Notification<'a> {
1313
SetDefaultToolchain(&'a str),
1414
SetOverrideToolchain(&'a Path, &'a str),
1515
SetProfile(&'a str),
16+
SetSelfUpdate(&'a str),
1617
LookingForToolchain(&'a str),
1718
ToolchainDirectory(&'a Path, &'a str),
1819
UpdatingToolchain(&'a str),
@@ -73,6 +74,7 @@ impl<'a> Notification<'a> {
7374
SetDefaultToolchain(_)
7475
| SetOverrideToolchain(_, _)
7576
| SetProfile(_)
77+
| SetSelfUpdate(_)
7678
| UsingExistingToolchain(_)
7779
| UninstallingToolchain(_)
7880
| UninstalledToolchain(_)
@@ -102,6 +104,7 @@ impl<'a> Display for Notification<'a> {
102104
name
103105
),
104106
SetProfile(name) => write!(f, "profile set to '{}'", name),
107+
SetSelfUpdate(mode) => write!(f, "auto-self-update mode set to '{}'", mode),
105108
LookingForToolchain(name) => write!(f, "looking for installed toolchain '{}'", name),
106109
ToolchainDirectory(path, _) => write!(f, "toolchain directory: '{}'", path.display()),
107110
UpdatingToolchain(name) => write!(f, "updating existing install for '{}'", name),

0 commit comments

Comments
 (0)