Skip to content

Commit

Permalink
Support self-update modes
Browse files Browse the repository at this point in the history
test: clean up "--no-self-update" args
test: add check-only and enable tests
docs: update docs and add comment
  • Loading branch information
Rustin170506 authored and rbtcollins committed May 27, 2021
1 parent a1d5912 commit 3670473
Show file tree
Hide file tree
Showing 13 changed files with 427 additions and 444 deletions.
27 changes: 20 additions & 7 deletions doc/src/basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,33 @@ info: downloading self-updates

## Keeping `rustup` up to date

Running `rustup update` also checks for updates to `rustup` itself and automatically
installs the latest version. To manually update `rustup` only,
without updating installed toolchains, type `rustup self update`:
If your `rustup` was built with the `no-self-update` feature, it can not update
itself. This is not the default, and only versions of `rustup` built with
`--no-default-features`, or obtained from a third-party distributor who has
disabled it (such as the Ubuntu snap store).

Otherwise Rustup can update itself. It is possible to control Rustup's automatic
self update mechanism with the `auto-self-update` configuration variable. This
setting supports three values: `enable` and `disable` and `check-only`.

* `disable` will ensure that no automatic self updating actions are taken.
* `enable` will mean that `rustup update` and similar commands will also check for, and install, any update to Rustup.
* `check-only` will cause any automatic self update to check and report on any updates, but not to automatically install them.

Whether `auto-self-update` is `enable` or not, you can request that Rustup
update itself to the latest version of `rustup` by running `rustup self update`.
This will not download new toolchains:

```console
$ rustup self update
info: checking for self-updates
info: downloading self-updates
```

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

Expand Down
64 changes: 43 additions & 21 deletions src/cli/rustup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ use super::self_update;
use super::term2;
use super::term2::Terminal;
use super::topical_doc;
use super::{common, self_update::get_available_rustup_version};
use super::{
common,
self_update::{check_rustup_update, SelfUpdateMode},
};
use crate::cli::errors::CLIError;
use crate::dist::dist::{
PartialTargetTriple, PartialToolchainDesc, Profile, TargetTriple, ToolchainDesc,
};
Expand Down Expand Up @@ -185,6 +189,7 @@ pub fn main() -> Result<utils::ExitCode> {
("set", Some(c)) => match c.subcommand() {
("default-host", Some(m)) => set_default_host_triple(cfg, m)?,
("profile", Some(m)) => set_profile(cfg, m)?,
("auto-self-update", Some(m)) => set_auto_self_update(cfg, m)?,
(_, _) => unreachable!(),
},
("completions", Some(c)) => {
Expand Down Expand Up @@ -707,6 +712,16 @@ pub fn cli() -> App<'static, 'static> {
.possible_values(Profile::names())
.default_value(Profile::default_name()),
),
)
.subcommand(
SubCommand::with_name("auto-self-update")
.about("The rustup auto self update mode")
.arg(
Arg::with_name("auto-self-update-mode")
.required(true)
.possible_values(SelfUpdateMode::modes())
.default_value(SelfUpdateMode::default_mode()),
),
),
);

Expand Down Expand Up @@ -911,31 +926,20 @@ fn check_updates(cfg: &Cfg) -> Result<utils::ExitCode> {
}
}

// Get current rustup version
let current_version = env!("CARGO_PKG_VERSION");

// Get available rustup version
let available_version = get_available_rustup_version()?;
check_rustup_update()?;

let _ = t.attr(term2::Attr::Bold);
write!(t, "rustup - ")?;

if current_version != available_version {
let _ = t.fg(term2::color::YELLOW);
write!(t, "Update available")?;
let _ = t.reset();
writeln!(t, " : {} -> {}", current_version, available_version)?;
} else {
let _ = t.fg(term2::color::GREEN);
write!(t, "Up to date")?;
let _ = t.reset();
writeln!(t, " : {}", current_version)?;
}
Ok(utils::ExitCode(0))
}

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

if !self_update::NEVER_SELF_UPDATE && self_update_mode == SelfUpdateMode::CheckOnly {
check_rustup_update()?;
}

Ok(utils::ExitCode(0))
}

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

fn set_auto_self_update(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result<utils::ExitCode> {
if self_update::NEVER_SELF_UPDATE {
let mut args = crate::process().args_os();
let arg0 = args.next().map(PathBuf::from);
let arg0 = arg0
.as_ref()
.and_then(|a| a.to_str())
.ok_or(CLIError::NoExeName)?;
warn!("{} is built with the no-self-update feature: setting auto-self-update will not have any effect.",arg0);
}
cfg.set_auto_self_update(&m.value_of("auto-self-update-mode").unwrap())?;
Ok(utils::ExitCode(0))
}

fn show_profile(cfg: &Cfg) -> Result<utils::ExitCode> {
writeln!(process().stdout(), "{}", cfg.get_profile()?)?;
Ok(utils::ExitCode(0))
Expand Down
83 changes: 82 additions & 1 deletion src/cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ use std::borrow::Cow;
use std::env;
use std::env::consts::EXE_SUFFIX;
use std::fs;
use std::io::Write;
use std::path::{Component, Path, PathBuf, MAIN_SEPARATOR};
use std::process::Command;
use std::str::FromStr;

use anyhow::{anyhow, Context, Result};
use cfg_if::cfg_if;
Expand All @@ -59,6 +61,7 @@ use super::common::{self, ignorable_error, Confirm};
use super::errors::*;
use super::markdown::md;
use super::term2;
use crate::cli::term2::Terminal;
use crate::dist::dist::{self, Profile, TargetTriple};
use crate::process;
use crate::toolchain::{DistributableToolchain, Toolchain};
Expand Down Expand Up @@ -86,6 +89,51 @@ pub const NEVER_SELF_UPDATE: bool = true;
#[cfg(not(feature = "no-self-update"))]
pub const NEVER_SELF_UPDATE: bool = false;

#[derive(Clone, Debug, PartialEq)]
pub enum SelfUpdateMode {
Enable,
Disable,
CheckOnly,
}

impl SelfUpdateMode {
pub fn modes() -> &'static [&'static str] {
&["enable", "disable", "check-only"]
}

pub fn default_mode() -> &'static str {
"enable"
}
}

impl FromStr for SelfUpdateMode {
type Err = anyhow::Error;

fn from_str(mode: &str) -> Result<Self> {
match mode {
"enable" => Ok(Self::Enable),
"disable" => Ok(Self::Disable),
"check-only" => Ok(Self::CheckOnly),
_ => Err(anyhow!(format!(
"unknown self update mode: '{}'; valid modes are {}",
mode,
valid_self_update_modes(),
))),
}
}
}

impl ToString for SelfUpdateMode {
fn to_string(&self) -> String {
match self {
SelfUpdateMode::Enable => "enable",
SelfUpdateMode::Disable => "disable",
SelfUpdateMode::CheckOnly => "check-only",
}
.into()
}
}

// The big installation messages. These are macros because the first
// argument of format! needs to be a literal.

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

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

pub fn check_rustup_update() -> Result<()> {
let mut t = term2::stdout();
// Get current rustup version
let current_version = env!("CARGO_PKG_VERSION");

// Get available rustup version
let available_version = get_available_rustup_version()?;

let _ = t.attr(term2::Attr::Bold);
write!(t, "rustup - ")?;

if current_version != available_version {
let _ = t.fg(term2::color::YELLOW);
write!(t, "Update available")?;
let _ = t.reset();
writeln!(t, " : {} -> {}", current_version, available_version)?;
} else {
let _ = t.fg(term2::color::GREEN);
write!(t, "Up to date")?;
let _ = t.reset();
writeln!(t, " : {}", current_version)?;
}

Ok(())
}

pub fn cleanup_self_updater() -> Result<()> {
let cargo_home = utils::cargo_home()?;
let setup = cargo_home.join(&format!("bin/rustup-init{}", EXE_SUFFIX));
Expand All @@ -1114,6 +1187,14 @@ pub fn cleanup_self_updater() -> Result<()> {
Ok(())
}

pub fn valid_self_update_modes() -> String {
SelfUpdateMode::modes()
.iter()
.map(|s| format!("'{}'", s))
.collect::<Vec<_>>()
.join(", ")
}

#[cfg(test)]
mod tests {
use std::collections::HashMap;
Expand Down
25 changes: 25 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use pgp::{Deserializable, SignedPublicKey};
use serde::Deserialize;
use thiserror::Error as ThisError;

use crate::cli::self_update::SelfUpdateMode;
use crate::dist::download::DownloadCfg;
use crate::dist::{
dist::{self, valid_profile_names},
Expand Down Expand Up @@ -405,6 +406,20 @@ impl Cfg {
Ok(())
}

pub fn set_auto_self_update(&mut self, mode: &str) -> Result<()> {
match SelfUpdateMode::from_str(mode) {
Ok(update_mode) => {
self.settings_file.with_mut(|s| {
s.auto_self_update = Some(update_mode);
Ok(())
})?;
(self.notify_handler)(Notification::SetSelfUpdate(mode));
Ok(())
}
Err(err) => Err(err),
}
}

pub fn set_toolchain_override(&mut self, toolchain_override: &str) {
self.toolchain_override = Some(toolchain_override.to_owned());
}
Expand All @@ -430,6 +445,16 @@ impl Cfg {
})
}

pub fn get_self_update_mode(&self) -> Result<SelfUpdateMode> {
self.settings_file.with(|s| {
let mode = match &s.auto_self_update {
Some(mode) => mode.clone(),
None => SelfUpdateMode::Enable,
};
Ok(mode)
})
}

pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result<Toolchain<'_>> {
if create_parent {
utils::ensure_dir_exists("toolchains", &self.toolchains_dir, &|n| {
Expand Down
3 changes: 3 additions & 0 deletions src/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub enum Notification<'a> {
SetDefaultToolchain(&'a str),
SetOverrideToolchain(&'a Path, &'a str),
SetProfile(&'a str),
SetSelfUpdate(&'a str),
LookingForToolchain(&'a str),
ToolchainDirectory(&'a Path, &'a str),
UpdatingToolchain(&'a str),
Expand Down Expand Up @@ -73,6 +74,7 @@ impl<'a> Notification<'a> {
SetDefaultToolchain(_)
| SetOverrideToolchain(_, _)
| SetProfile(_)
| SetSelfUpdate(_)
| UsingExistingToolchain(_)
| UninstallingToolchain(_)
| UninstalledToolchain(_)
Expand Down Expand Up @@ -102,6 +104,7 @@ impl<'a> Display for Notification<'a> {
name
),
SetProfile(name) => write!(f, "profile set to '{}'", name),
SetSelfUpdate(mode) => write!(f, "auto-self-update mode set to '{}'", mode),
LookingForToolchain(name) => write!(f, "looking for installed toolchain '{}'", name),
ToolchainDirectory(path, _) => write!(f, "toolchain directory: '{}'", path.display()),
UpdatingToolchain(name) => write!(f, "updating existing install for '{}'", name),
Expand Down
Loading

0 comments on commit 3670473

Please sign in to comment.