Skip to content
Closed
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
14 changes: 12 additions & 2 deletions src/cli/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,10 @@ fn show_channel_updates(
Ok(())
}

pub(crate) async fn update_all_channels(cfg: &Cfg<'_>, force_update: bool) -> Result<ExitCode> {
pub(crate) async fn update_all_channels(
cfg: &Cfg<'_>,
force_update: bool,
) -> Result<(ExitCode, bool)> {
let profile = cfg.get_profile()?;
let channels = cfg.list_channels()?;

Expand Down Expand Up @@ -249,6 +252,13 @@ pub(crate) async fn update_all_channels(cfg: &Cfg<'_>, force_update: bool) -> Re
ExitCode::SUCCESS
};

let any_updates = toolchains.iter().any(|(_, r)| {
matches!(
r,
Ok(UpdateStatus::Installed) | Ok(UpdateStatus::Updated(_))
)
});

if toolchains.is_empty() {
info!("no updatable toolchains installed");
}
Expand All @@ -263,7 +273,7 @@ pub(crate) async fn update_all_channels(cfg: &Cfg<'_>, force_update: bool) -> Re
show_channel_updates(cfg, t)?;
}

Ok(exit_code)
Ok((exit_code, any_updates))
}

/// Print a list of items (targets or components) to stdout.
Expand Down
7 changes: 6 additions & 1 deletion src/cli/help.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,12 @@ pub(crate) fn update_help() -> String {
If given a toolchain argument then `update` updates that
toolchain, the same as `rustup toolchain install`.

{TOOLCHAIN_INSTALL_HINT}"
{TOOLCHAIN_INSTALL_HINT}

{HEADER}Exit status (with --check):{HEADER:#}
{LITERAL}0{LITERAL:#} No updates were applied; everything was already up to date.
{LITERAL}100{LITERAL:#} At least one toolchain was updated.
{LITERAL}1{LITERAL:#} An error occurred."
)
}

Expand Down
26 changes: 23 additions & 3 deletions src/cli/rustup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@ enum RustupSubcmd {
/// Install toolchains that require an emulator. See https://github.com/rust-lang/rustup/wiki/Non-host-toolchains
#[arg(long)]
force_non_host: bool,

/// Set exit code to indicate whether udpates were applied
#[arg(long)]
check: bool,
},

/// Check for updates to Rust toolchains and rustup
Expand Down Expand Up @@ -728,7 +732,7 @@ pub async fn main(

let exit_code = match subcmd {
RustupSubcmd::DumpTestament => common::dump_testament(process),
RustupSubcmd::Install { opts } => update(cfg, opts, true).await,
RustupSubcmd::Install { opts } => update(cfg, opts, true, false).await,
RustupSubcmd::Uninstall { opts } => toolchain_remove(cfg, opts).await,
RustupSubcmd::Show { verbose, subcmd } => handle_epipe(match subcmd {
None => show(cfg, verbose).await,
Expand All @@ -746,6 +750,7 @@ pub async fn main(
no_self_update,
force,
force_non_host,
check,
} => {
update(
cfg,
Expand All @@ -757,11 +762,12 @@ pub async fn main(
..UpdateOpts::default()
},
false,
check,
)
.await
}
RustupSubcmd::Toolchain { subcmd } => match subcmd {
ToolchainSubcmd::Install { opts } => update(cfg, opts, true).await,
ToolchainSubcmd::Install { opts } => update(cfg, opts, true, false).await,
ToolchainSubcmd::List { verbose, quiet } => {
handle_epipe(common::list_toolchains(cfg, verbose, quiet).await)
}
Expand Down Expand Up @@ -1062,8 +1068,10 @@ async fn update(
cfg: &mut Cfg<'_>,
opts: UpdateOpts,
ensure_active_toolchain: bool,
check: bool,
) -> Result<ExitCode> {
let mut exit_code = ExitCode::SUCCESS;
let mut any_updated = false;

common::warn_if_host_is_emulated(cfg.process);
let self_update_mode = SelfUpdateMode::from_cfg(cfg)?;
Expand Down Expand Up @@ -1120,6 +1128,10 @@ async fn update(
Err(e) => Err(e)?,
};

if matches!(status, UpdateStatus::Installed | UpdateStatus::Updated(_)) {
any_updated = true;
}

writeln!(cfg.process.stdout().lock())?;
common::show_channel_update(
cfg,
Expand All @@ -1144,14 +1156,22 @@ async fn update(
info!("it's active because: {}", source.to_reason());
exit_code &= self_update_mode.update(should_self_update, &dl_cfg).await?;
} else {
exit_code &= common::update_all_channels(cfg, opts.force).await?;
let (channels_exit, channels_updated) =
common::update_all_channels(cfg, opts.force).await?;
exit_code &= channels_exit;
any_updated = channels_updated;

exit_code &= self_update_mode.update(should_self_update, &dl_cfg).await?;

info!("cleaning up downloads & tmp directories");
utils::delete_dir_contents_following_links(&cfg.download_dir);
dl_cfg.tmp_cx.clean();
}

if check && exit_code == ExitCode::SUCCESS && any_updated {
return Ok(ExitCode::UPDATES_AVAILABLE);
}

Ok(exit_code)
}

Expand Down
74 changes: 74 additions & 0 deletions tests/suite/cli_exact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,80 @@ nightly-[HOST_TUPLE] - update available: 1.2.0 (hash-nightly-1) -> 1.3.0 (hash-n
"#]]);
}

#[tokio::test]
async fn update_check_no_updates() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
cx.config
.expect(["rustup", "toolchain", "add", "stable"])
.await
.is_ok();
// Updating an already-current toolchain with --check should return 0
cx.config
.expect(["rustup", "update", "--check", "stable"])
.await
.is_ok();
}

#[tokio::test]
async fn update_check_with_updates() {
let mut cx = CliTestContext::new(Scenario::None).await;

{
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "add", "stable"])
.await
.is_ok();
}

let cx = cx.with_dist_dir(Scenario::SimpleV2);
// Updating an outdated toolchain with --check should return 100
cx.config
.expect(["rustup", "update", "--check", "stable"])
.await
.has_code(100);
}

#[tokio::test]
async fn update_without_check_always_succeeds() {
let mut cx = CliTestContext::new(Scenario::None).await;

{
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "add", "stable"])
.await
.is_ok();
}

let cx = cx.with_dist_dir(Scenario::SimpleV2);
// Without --check, update should return 0 even when updates occurred
cx.config
.expect(["rustup", "update", "stable"])
.await
.is_ok();
}

#[tokio::test]
async fn update_check_all_channels() {
let mut cx = CliTestContext::new(Scenario::None).await;

{
let cx = cx.with_dist_dir(Scenario::ArchivesV2_2015_01_01);
cx.config
.expect(["rustup", "toolchain", "add", "stable", "beta", "nightly"])
.await
.is_ok();
}

let cx = cx.with_dist_dir(Scenario::SimpleV2);
// update --check with no specific toolchain updates all and returns 100
cx.config
.expect(["rustup", "update", "--check"])
.await
.has_code(100);
}

#[tokio::test]
async fn default() {
let cx = CliTestContext::new(Scenario::SimpleV2).await;
Expand Down
52 changes: 32 additions & 20 deletions tests/suite/cli_rustup_ui/rustup_up_cmd_help_flag.stdout.term.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading