Skip to content

Add set auto-install disable #4254

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

Merged
merged 1 commit into from
Mar 15, 2025
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
10 changes: 10 additions & 0 deletions src/cli/rustup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use itertools::Itertools;
use tracing::{info, trace, warn};
use tracing_subscriber::{EnvFilter, Registry, reload::Handle};

use crate::dist::AutoInstallMode;
use crate::{
cli::{
common::{self, PackageUpdate, update_console_filter},
Expand Down Expand Up @@ -539,6 +540,12 @@ enum SetSubcmd {
#[arg(value_enum, default_value_t)]
auto_self_update_mode: SelfUpdateMode,
},

/// The auto toolchain install mode
AutoInstall {
#[arg(value_enum, default_value_t)]
auto_install_mode: AutoInstallMode,
},
}

#[tracing::instrument(level = "trace", fields(args = format!("{:?}", process.args_os().collect::<Vec<_>>())))]
Expand Down Expand Up @@ -715,6 +722,9 @@ pub async fn main(
SetSubcmd::AutoSelfUpdate {
auto_self_update_mode,
} => set_auto_self_update(cfg, auto_self_update_mode),
SetSubcmd::AutoInstall { auto_install_mode } => cfg
.set_auto_install(auto_install_mode)
.map(|_| utils::ExitCode(0)),
},
RustupSubcmd::Completions { shell, command } => {
output_completion_script(shell, command, process)
Expand Down
34 changes: 25 additions & 9 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use thiserror::Error as ThisError;
use tokio_stream::StreamExt;
use tracing::trace;

use crate::dist::AutoInstallMode;
use crate::{
cli::{common, self_update::SelfUpdateMode},
dist::{
Expand Down Expand Up @@ -375,6 +376,24 @@ impl<'a> Cfg<'a> {
self.toolchain_override = Some(toolchain_override.to_owned());
}

pub(crate) fn set_auto_install(&mut self, mode: AutoInstallMode) -> Result<()> {
self.settings_file.with_mut(|s| {
s.auto_install = Some(mode);
Ok(())
})?;
(self.notify_handler)(Notification::SetAutoInstall(mode.as_str()));
Ok(())
}

pub(crate) fn should_auto_install(&self) -> Result<bool> {
if let Ok(mode) = self.process.var("RUSTUP_AUTO_INSTALL") {
Ok(mode != "0")
} else {
self.settings_file
.with(|s| Ok(s.auto_install != Some(AutoInstallMode::Disable)))
}
}

// Returns a profile, if one exists in the settings file.
//
// Returns `Err` if the settings file could not be read or the profile is
Expand Down Expand Up @@ -527,11 +546,11 @@ impl<'a> Cfg<'a> {
}
};

let should_install_active = force_install_active.unwrap_or_else(|| {
self.process
.var("RUSTUP_AUTO_INSTALL")
.map_or(true, |it| it != "0")
});
let should_install_active = if let Some(force) = force_install_active {
force
} else {
self.should_auto_install()?
};

if !should_install_active {
return Ok(Some(((&toolchain).into(), reason)));
Expand Down Expand Up @@ -775,10 +794,7 @@ impl<'a> Cfg<'a> {
async fn local_toolchain(&self, name: Option<LocalToolchainName>) -> Result<Toolchain<'_>> {
match name {
Some(tc) => {
let install_if_missing = self
.process
.var("RUSTUP_AUTO_INSTALL")
.map_or(true, |it| it != "0");
let install_if_missing = self.should_auto_install()?;
Toolchain::from_local(tc, install_if_missing, self).await
}
None => {
Expand Down
53 changes: 53 additions & 0 deletions src/dist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,59 @@ impl FromStr for Profile {
}
}

#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum AutoInstallMode {
#[default]
Enable,
Disable,
}

impl AutoInstallMode {
pub(crate) fn as_str(&self) -> &'static str {
match self {
Self::Enable => "enable",
Self::Disable => "disable",
}
}
}

impl ValueEnum for AutoInstallMode {
fn value_variants<'a>() -> &'a [Self] {
&[Self::Enable, Self::Disable]
}

fn to_possible_value(&self) -> Option<PossibleValue> {
Some(PossibleValue::new(self.as_str()))
}

fn from_str(input: &str, _: bool) -> Result<Self, String> {
<Self as FromStr>::from_str(input).map_err(|e| e.to_string())
}
}

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

fn from_str(mode: &str) -> Result<Self> {
match mode {
"enable" => Ok(Self::Enable),
"disable" => Ok(Self::Disable),
_ => Err(anyhow!(format!(
"unknown auto install mode: '{}'; valid modes are {}",
mode,
Self::value_variants().iter().join(", ")
))),
}
}
}

impl std::fmt::Display for AutoInstallMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}

impl fmt::Display for TargetTriple {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
Expand Down
5 changes: 4 additions & 1 deletion src/notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub(crate) enum Notification<'a> {
Utils(crate::utils::Notification<'a>),
Temp(temp::Notification<'a>),

SetAutoInstall(&'a str),
SetDefaultToolchain(Option<&'a ToolchainName>),
SetOverrideToolchain(&'a Path, &'a str),
SetProfile(&'a str),
Expand Down Expand Up @@ -69,7 +70,8 @@ impl Notification<'_> {
| ReadMetadataVersion(_)
| InstalledToolchain(_)
| UpdateHashMatches => NotificationLevel::Debug,
SetDefaultToolchain(_)
SetAutoInstall(_)
| SetDefaultToolchain(_)
| SetOverrideToolchain(_, _)
| SetProfile(_)
| SetSelfUpdate(_)
Expand All @@ -91,6 +93,7 @@ impl Display for Notification<'_> {
Install(n) => n.fmt(f),
Utils(n) => n.fmt(f),
Temp(n) => n.fmt(f),
SetAutoInstall(auto) => write!(f, "auto install set to '{auto}'"),
SetDefaultToolchain(None) => write!(f, "default toolchain unset"),
SetDefaultToolchain(Some(name)) => write!(f, "default toolchain set to '{name}'"),
SetOverrideToolchain(path, name) => write!(
Expand Down
4 changes: 3 additions & 1 deletion src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use anyhow::{Context, Result};
use serde::{Deserialize, Serialize};

use crate::cli::self_update::SelfUpdateMode;
use crate::dist::Profile;
use crate::dist::{AutoInstallMode, Profile};
use crate::errors::*;
use crate::notifications::*;
use crate::utils;
Expand Down Expand Up @@ -93,6 +93,8 @@ pub struct Settings {
pub pgp_keys: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auto_self_update: Option<SelfUpdateMode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub auto_install: Option<AutoInstallMode>,
}

impl Settings {
Expand Down
7 changes: 6 additions & 1 deletion src/test/clitools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,12 @@ impl Config {

/// Expect an ok status
pub async fn expect_ok(&mut self, args: &[&str]) {
let out = self.run(args[0], &args[1..], &[]).await;
self.expect_ok_env(args, &[]).await
}

/// Expect an ok status with extra environment variables
pub async fn expect_ok_env(&self, args: &[&str], env: &[(&str, &str)]) {
let out = self.run(args[0], &args[1..], env).await;
if !out.ok {
print_command(args, &out);
println!("expected.ok: true");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
bin.name = "rustup"
args = ["set", "auto-install", "--help"]
stdout = """
...
The auto toolchain install mode

Usage: rustup[EXE] set auto-install [AUTO_INSTALL_MODE]

Arguments:
[AUTO_INSTALL_MODE] [default: enable] [possible values: enable, disable]

Options:
-h, --help Print help
"""
stderr = ""
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Commands:
default-host The triple used to identify toolchains when not specified
profile The default components installed with a toolchain
auto-self-update The rustup auto self update mode
auto-install The auto toolchain install mode
help Print this message or the help of the given subcommand(s)

Options:
Expand Down
28 changes: 28 additions & 0 deletions tests/suite/cli_v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,34 @@ async fn list_targets_no_toolchain() {
.await;
}

#[tokio::test]
async fn set_auto_install_disable() {
let mut cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect_ok(&["rustup", "set", "auto-install", "disable"])
.await;
cx.config
.expect_err(
&["rustup", "target", "list", "--toolchain=nightly"],
for_host!("toolchain 'nightly-{0}' is not installed"),
)
.await;
cx.config
.expect_err_env(
&["rustup", "target", "list", "--toolchain=nightly"],
&[("RUSTUP_AUTO_INSTALL", "0")],
for_host!("toolchain 'nightly-{0}' is not installed"),
)
.await;
// The environment variable takes precedence over the setting.
cx.config
.expect_ok_env(
&["rustup", "target", "list", "--toolchain=nightly"],
&[("RUSTUP_AUTO_INSTALL", "1")],
)
.await;
}

#[tokio::test]
async fn list_targets_v1_toolchain() {
let mut cx = CliTestContext::new(Scenario::SimpleV1).await;
Expand Down