From 6dfc95361b2ec6aca7789b038bf4678d6a6f02f3 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 29 Sep 2025 15:40:41 -0400 Subject: [PATCH 1/6] don't show update messages when not interactive --- src/bin/julialauncher.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bin/julialauncher.rs b/src/bin/julialauncher.rs index 154ed2a3..8e662deb 100644 --- a/src/bin/julialauncher.rs +++ b/src/bin/julialauncher.rs @@ -305,7 +305,7 @@ fn check_channel_uptodate( })? .version; - if latest_version != current_version { + if latest_version != current_version && is_interactive() { eprintln!("The latest version of Julia in the `{}` channel is {}. You currently have `{}` installed. Run:", channel, latest_version, current_version); eprintln!(); eprintln!(" juliaup update"); @@ -486,7 +486,7 @@ fn get_julia_path_from_installed_channel( server_etag, version: _, } => { - if local_etag != server_etag { + if local_etag != server_etag && is_interactive() { if channel.starts_with("nightly") { // Nightly is updateable several times per day so this message will show // more often than not unless folks update a couple of times a day. From 6c0ec29982cec1d1841fcb2b3fdcc7ec62645f32 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 29 Sep 2025 15:47:20 -0400 Subject: [PATCH 2/6] add tests --- tests/channel_selection.rs | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/channel_selection.rs b/tests/channel_selection.rs index ab836193..bf98c2b0 100644 --- a/tests/channel_selection.rs +++ b/tests/channel_selection.rs @@ -1,4 +1,5 @@ use predicates::str::contains; +use predicates::boolean::PredicateBooleanExt; mod utils; use utils::TestEnv; @@ -184,3 +185,44 @@ fn auto_install_valid_channel() { "Info: Installing Julia 1.10.10 automatically per juliaup settings...", )); } + +#[test] +fn no_update_messages_in_non_interactive_mode() { + let env = TestEnv::new(); + + // Set up julia installation + env.juliaup() + .arg("add") + .arg("1.11") + .assert() + .success(); + + env.juliaup() + .arg("default") + .arg("1.11") + .assert() + .success(); + + // Test that update messages are not shown when using -e flag (non-interactive) + env.julia() + .arg("-e") + .arg("println(\"test\")") + .assert() + .success() + .stderr( + contains("new version").not() + .and(contains("juliaup update").not()) + .and(contains("available").not()) + ); + + // Test that update messages are not shown when using --version flag (non-interactive) + env.julia() + .arg("--version") + .assert() + .success() + .stderr( + contains("new version").not() + .and(contains("juliaup update").not()) + .and(contains("available").not()) + ); +} From e516acd05d32b7f9cdf7c49979b8278886e9383d Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 29 Sep 2025 15:49:10 -0400 Subject: [PATCH 3/6] fmt --- tests/channel_selection.rs | 34 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/tests/channel_selection.rs b/tests/channel_selection.rs index bf98c2b0..5b60a75f 100644 --- a/tests/channel_selection.rs +++ b/tests/channel_selection.rs @@ -1,5 +1,5 @@ -use predicates::str::contains; use predicates::boolean::PredicateBooleanExt; +use predicates::str::contains; mod utils; use utils::TestEnv; @@ -191,17 +191,9 @@ fn no_update_messages_in_non_interactive_mode() { let env = TestEnv::new(); // Set up julia installation - env.juliaup() - .arg("add") - .arg("1.11") - .assert() - .success(); + env.juliaup().arg("add").arg("1.11").assert().success(); - env.juliaup() - .arg("default") - .arg("1.11") - .assert() - .success(); + env.juliaup().arg("default").arg("1.11").assert().success(); // Test that update messages are not shown when using -e flag (non-interactive) env.julia() @@ -210,19 +202,17 @@ fn no_update_messages_in_non_interactive_mode() { .assert() .success() .stderr( - contains("new version").not() + contains("new version") + .not() .and(contains("juliaup update").not()) - .and(contains("available").not()) + .and(contains("available").not()), ); // Test that update messages are not shown when using --version flag (non-interactive) - env.julia() - .arg("--version") - .assert() - .success() - .stderr( - contains("new version").not() - .and(contains("juliaup update").not()) - .and(contains("available").not()) - ); + env.julia().arg("--version").assert().success().stderr( + contains("new version") + .not() + .and(contains("juliaup update").not()) + .and(contains("available").not()), + ); } From b335cfa3bcaa5db787e119bdde92e96ac00331c6 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 29 Sep 2025 20:57:09 -0400 Subject: [PATCH 4/6] better tests. actually test that upgrades work --- tests/channel_selection.rs | 31 ------- tests/command_update.rs | 176 +++++++++++++++++++++++++++++++++++++ tests/utils.rs | 11 +++ 3 files changed, 187 insertions(+), 31 deletions(-) diff --git a/tests/channel_selection.rs b/tests/channel_selection.rs index 5b60a75f..93ab6ddd 100644 --- a/tests/channel_selection.rs +++ b/tests/channel_selection.rs @@ -185,34 +185,3 @@ fn auto_install_valid_channel() { "Info: Installing Julia 1.10.10 automatically per juliaup settings...", )); } - -#[test] -fn no_update_messages_in_non_interactive_mode() { - let env = TestEnv::new(); - - // Set up julia installation - env.juliaup().arg("add").arg("1.11").assert().success(); - - env.juliaup().arg("default").arg("1.11").assert().success(); - - // Test that update messages are not shown when using -e flag (non-interactive) - env.julia() - .arg("-e") - .arg("println(\"test\")") - .assert() - .success() - .stderr( - contains("new version") - .not() - .and(contains("juliaup update").not()) - .and(contains("available").not()), - ); - - // Test that update messages are not shown when using --version flag (non-interactive) - env.julia().arg("--version").assert().success().stderr( - contains("new version") - .not() - .and(contains("juliaup update").not()) - .and(contains("available").not()), - ); -} diff --git a/tests/command_update.rs b/tests/command_update.rs index 63869dc7..98cec028 100644 --- a/tests/command_update.rs +++ b/tests/command_update.rs @@ -1,3 +1,8 @@ +use predicates::boolean::PredicateBooleanExt; +use predicates::str::contains; +use serde_json::Value; +use std::fs; + mod utils; use utils::TestEnv; @@ -46,3 +51,174 @@ fn command_update_all_with_alias() { env.juliaup().arg("update").assert().success(); } + +#[test] +fn command_update_outdated_channel() { + let env = TestEnv::new(); + + env.juliaup().arg("add").arg("1.10").assert().success(); + env.juliaup().arg("add").arg("1.10.9").assert().success(); + + let config_path = env.config_path(); + let config_content = fs::read_to_string(&config_path).expect("Failed to read config file"); + let mut config: Value = serde_json::from_str(&config_content).expect("Failed to parse config"); + + // Find the actual version key for 1.10.9 in InstalledVersions + let installed_versions = config["InstalledVersions"] + .as_object() + .expect("InstalledVersions should be an object"); + + let version_1_10_9_key = installed_versions + .keys() + .find(|k| k.starts_with("1.10.9")) + .expect("Should have 1.10.9 version installed") + .clone(); + + let channels = config["InstalledChannels"] + .as_object_mut() + .expect("InstalledChannels should be an object"); + + if let Some(channel_110) = channels.get_mut("1.10") { + if let Some(version) = channel_110.get_mut("Version") { + *version = Value::String(version_1_10_9_key); + } + } + + fs::write( + &config_path, + serde_json::to_string_pretty(&config).expect("Failed to serialize config"), + ) + .expect("Failed to write config file"); + + env.juliaup() + .arg("status") + .assert() + .success() + .stdout(contains("1.10").and(contains("Update"))); + + // Update hints should not appear when using julia noninteractively. + // (ideally we'd test the interactive case too, but that's tricky in CI) + env.julia() + .arg("+1.10") + .arg("--version") + .assert() + .success() + .stderr( + contains("latest version") + .not() + .and(contains("juliaup update").not()) + .and(contains("You currently have").not()), + ); + + env.julia() + .arg("+1.10") + .arg("-e") + .arg("1+1") + .assert() + .success() + .stderr( + contains("latest version") + .not() + .and(contains("juliaup update").not()) + .and(contains("You currently have").not()), + ); + + env.juliaup().arg("update").arg("1.10").assert().success(); + + let updated_config_content = + fs::read_to_string(&config_path).expect("Failed to read config file after update"); + let updated_config: Value = + serde_json::from_str(&updated_config_content).expect("Failed to parse updated config"); + + let updated_channels = updated_config["InstalledChannels"] + .as_object() + .expect("InstalledChannels should be an object"); + + let new_version = updated_channels + .get("1.10") + .and_then(|c| c.get("Version")) + .and_then(|v| v.as_str()) + .expect("1.10 channel should have a version"); + + assert!( + new_version.starts_with("1.10.10"), + "Channel 1.10 should have been updated to 1.10.10 (was {})", + new_version + ); +} + +#[test] +fn command_update_all_outdated_channels() { + let env = TestEnv::new(); + + env.juliaup().arg("add").arg("1.10").assert().success(); + env.juliaup().arg("add").arg("1.10.9").assert().success(); + env.juliaup().arg("add").arg("1.11").assert().success(); + env.juliaup().arg("add").arg("1.11.1").assert().success(); + + let config_path = env.config_path(); + let config_content = fs::read_to_string(&config_path).expect("Failed to read config file"); + let mut config: Value = serde_json::from_str(&config_content).expect("Failed to parse config"); + + let channels = config["InstalledChannels"] + .as_object_mut() + .expect("InstalledChannels should be an object"); + + if let Some(channel_110) = channels.get_mut("1.10") { + if let Some(version) = channel_110.get_mut("Version") { + *version = Value::String("1.10.9".to_string()); + } + } + + if let Some(channel_111) = channels.get_mut("1.11") { + if let Some(version) = channel_111.get_mut("Version") { + *version = Value::String("1.11.1".to_string()); + } + } + + fs::write( + &config_path, + serde_json::to_string_pretty(&config).expect("Failed to serialize config"), + ) + .expect("Failed to write config file"); + + env.juliaup().arg("update").assert().success(); + + let updated_config_content = + fs::read_to_string(&config_path).expect("Failed to read config file after update"); + let updated_config: Value = + serde_json::from_str(&updated_config_content).expect("Failed to parse updated config"); + + let updated_channels = updated_config["InstalledChannels"] + .as_object() + .expect("InstalledChannels should be an object"); + + let new_version_110 = updated_channels + .get("1.10") + .and_then(|c| c.get("Version")) + .and_then(|v| v.as_str()) + .expect("1.10 channel should have a version"); + + let new_version_111 = updated_channels + .get("1.11") + .and_then(|c| c.get("Version")) + .and_then(|v| v.as_str()) + .expect("1.11 channel should have a version"); + + assert!( + new_version_110.starts_with("1.10.10"), + "Channel 1.10 should have been updated to 1.10.10 (was {})", + new_version_110 + ); + + assert!( + new_version_111.starts_with("1.11."), + "Channel 1.11 should have been updated to latest 1.11.x (was {})", + new_version_111 + ); + assert!( + !new_version_111.starts_with("1.11.1"), + "Channel 1.11 should have been updated past 1.11.1 (was {})", + new_version_111 + ); +} diff --git a/tests/utils.rs b/tests/utils.rs index 811f90ec..f5e2eae1 100644 --- a/tests/utils.rs +++ b/tests/utils.rs @@ -1,5 +1,6 @@ use assert_cmd::Command; use assert_fs::TempDir; +use std::path::{Path, PathBuf}; /// A test environment that provides convenient methods for running juliaup and julia commands /// with isolated depot directories. @@ -33,4 +34,14 @@ impl TestEnv { cmd.env("JULIAUP_DEPOT_PATH", self.depot_dir.path()); cmd } + + /// Get the path to the juliaup config file + pub fn config_path(&self) -> PathBuf { + self.depot_dir.path().join("juliaup").join("juliaup.json") + } + + /// Get the depot directory path + pub fn depot_path(&self) -> &Path { + self.depot_dir.path() + } } From 213170dddbf449acc07d334a518aa05dba7f1224 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 29 Sep 2025 21:06:50 -0400 Subject: [PATCH 5/6] fix --- tests/command_update.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/command_update.rs b/tests/command_update.rs index 98cec028..b0a54422 100644 --- a/tests/command_update.rs +++ b/tests/command_update.rs @@ -1,4 +1,3 @@ -use predicates::boolean::PredicateBooleanExt; use predicates::str::contains; use serde_json::Value; use std::fs; From 8850d8e46a0791e9c20234c1e68d021e52ca226d Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Mon, 29 Sep 2025 21:37:12 -0400 Subject: [PATCH 6/6] fix fix --- tests/channel_selection.rs | 1 - tests/command_update.rs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/channel_selection.rs b/tests/channel_selection.rs index 93ab6ddd..ab836193 100644 --- a/tests/channel_selection.rs +++ b/tests/channel_selection.rs @@ -1,4 +1,3 @@ -use predicates::boolean::PredicateBooleanExt; use predicates::str::contains; mod utils; diff --git a/tests/command_update.rs b/tests/command_update.rs index b0a54422..98cec028 100644 --- a/tests/command_update.rs +++ b/tests/command_update.rs @@ -1,3 +1,4 @@ +use predicates::boolean::PredicateBooleanExt; use predicates::str::contains; use serde_json::Value; use std::fs;