Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support set auto-self-update mode #2763

Merged
merged 1 commit into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Rustin170506 marked this conversation as resolved.
Show resolved Hide resolved
// 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<()> {
Rustin170506 marked this conversation as resolved.
Show resolved Hide resolved
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<()> {
Rustin170506 marked this conversation as resolved.
Show resolved Hide resolved
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