diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-03 14:38:08 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-06-03 14:38:08 +0300 |
| commit | e584f93f367650a2b752a4e3a889603fb81b08f4 (patch) | |
| tree | 975fdfce6352d53f52a4fd68ffebdb5146beb915 /src | |
| parent | 22c99852298b7c5a91657f278d5b1c213b6fd6cc (diff) | |
fix: reconcile update channel switches
Diffstat (limited to 'src')
| -rw-r--r-- | src/cli.rs | 77 | ||||
| -rw-r--r-- | src/update.rs | 56 |
2 files changed, 123 insertions, 10 deletions
@@ -77,6 +77,14 @@ fn channel_set(args: &[String]) -> std::io::Result<i32> { return Ok(2); }; + if let Some(reason) = channel_set_preview_rejection( + channel, + crate::update::preview_channel_rejection_for_current_install(), + ) { + eprintln!("{reason}."); + return Ok(1); + } + let path = crate::config::config_path(); let content = if path.exists() { std::fs::read_to_string(&path)? @@ -112,11 +120,23 @@ fn channel_set(args: &[String]) -> std::io::Result<i32> { "Herdr update channel set to {channel} in {}.", path.display() ); - if channel == "preview" { - println!("Run `herdr update` to install the latest preview build when ready."); - } else { - println!("Run `herdr update` to install the latest stable release when ready."); + + match channel_set_install_action( + crate::update::package_manager_channel_update_guidance_for_current_install(), + ) { + ChannelSetInstallAction::PrintGuidance(guidance) => { + println!("{guidance}"); + return Ok(0); + } + ChannelSetInstallAction::RunSelfUpdate => {} } + + if let Err(err) = crate::update::self_update(crate::update::SelfUpdateOptions::default()) { + eprintln!("update failed: {err}"); + eprintln!("Run `herdr update` to retry."); + return Ok(1); + } + Ok(0) } @@ -129,6 +149,30 @@ fn parse_channel_set_arg(args: &[String]) -> Option<&str> { } } +fn channel_set_preview_rejection( + channel: &str, + install_rejection: Option<&'static str>, +) -> Option<&'static str> { + (channel == "preview") + .then_some(install_rejection) + .flatten() +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum ChannelSetInstallAction { + RunSelfUpdate, + PrintGuidance(&'static str), +} + +fn channel_set_install_action( + package_manager_guidance: Option<&'static str>, +) -> ChannelSetInstallAction { + match package_manager_guidance { + Some(guidance) => ChannelSetInstallAction::PrintGuidance(guidance), + None => ChannelSetInstallAction::RunSelfUpdate, + } +} + fn print_channel_help() { eprintln!("herdr channel commands:"); eprintln!(" herdr channel show print the configured update channel"); @@ -848,4 +892,29 @@ mod tests { None ); } + + #[test] + fn channel_set_rejects_package_managed_preview_before_config_write() { + assert_eq!( + super::channel_set_preview_rejection("preview", Some("no preview")), + Some("no preview") + ); + assert_eq!( + super::channel_set_preview_rejection("stable", Some("no preview")), + None + ); + assert_eq!(super::channel_set_preview_rejection("preview", None), None); + } + + #[test] + fn channel_set_skips_self_update_for_package_manager_guidance() { + assert_eq!( + super::channel_set_install_action(Some("use package manager")), + super::ChannelSetInstallAction::PrintGuidance("use package manager") + ); + assert_eq!( + super::channel_set_install_action(None), + super::ChannelSetInstallAction::RunSelfUpdate + ); + } } diff --git a/src/update.rs b/src/update.rs index 0d73b5b1..6d1ad49b 100644 --- a/src/update.rs +++ b/src/update.rs @@ -1662,6 +1662,37 @@ fn is_nix_managed_install() -> bool { is_nix_store_exe_path_following_links(¤t_exe) } +pub(crate) fn preview_channel_rejection_for_current_install() -> Option<&'static str> { + let Ok(current_exe) = env::current_exe() else { + return None; + }; + + preview_channel_rejection_for_exe_path(¤t_exe) +} + +pub(crate) fn package_manager_channel_update_guidance_for_current_install() -> Option<&'static str> +{ + if is_homebrew_managed_install() { + Some("Use `brew update && brew upgrade herdr` to update Homebrew installs.") + } else if is_nix_managed_install() { + Some("Update through Nix to update Nix-managed Herdr installs.") + } else { + None + } +} + +fn preview_channel_rejection_for_exe_path(path: &Path) -> Option<&'static str> { + if is_homebrew_managed_exe_path_following_links(path) { + Some( + "preview channel is only available for direct Herdr installs; Homebrew installs update through `brew update && brew upgrade herdr`", + ) + } else if is_nix_store_exe_path_following_links(path) { + Some("preview channel is only available for direct Herdr installs; Nix installs update through Nix") + } else { + None + } +} + pub(crate) fn is_package_manager_managed_exe_path(path: &Path) -> bool { is_homebrew_managed_exe_path_following_links(path) || is_nix_store_exe_path_following_links(path) @@ -2155,6 +2186,19 @@ mod tests { } #[test] + fn preview_channel_is_rejected_for_package_manager_paths() { + let homebrew = Path::new("/opt/homebrew/Cellar/herdr/0.6.6/bin/herdr"); + let nix = Path::new("/nix/store/abc123-herdr-0.6.6/bin/herdr"); + let direct = Path::new("/home/user/.local/bin/herdr"); + + assert!(preview_channel_rejection_for_exe_path(homebrew) + .is_some_and(|message| message.contains("Homebrew"))); + assert!(preview_channel_rejection_for_exe_path(nix) + .is_some_and(|message| message.contains("Nix"))); + assert!(preview_channel_rejection_for_exe_path(direct).is_none()); + } + + #[test] fn non_nix_store_path_is_not_detected() { let path = Path::new("/usr/local/bin/herdr"); @@ -2984,16 +3028,16 @@ mod tests { #[test] fn stable_channel_installs_stable_asset_when_current_binary_is_preview() { - let older_stable = Version::parse("0.6.5").unwrap(); - let preview_base = Version::parse("0.6.6").unwrap(); + let latest_stable = Version::parse("0.6.6").unwrap(); + let installed_base = Version::parse("0.6.6").unwrap(); assert!(stable_channel_should_install( - &older_stable, - &preview_base, + &latest_stable, + &installed_base, true )); assert!(!stable_channel_should_install( - &older_stable, - &preview_base, + &latest_stable, + &installed_base, false )); } |
