aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-06-03 14:38:08 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-06-03 14:38:08 +0300
commite584f93f367650a2b752a4e3a889603fb81b08f4 (patch)
tree975fdfce6352d53f52a4fd68ffebdb5146beb915 /src
parent22c99852298b7c5a91657f278d5b1c213b6fd6cc (diff)
fix: reconcile update channel switches
Diffstat (limited to 'src')
-rw-r--r--src/cli.rs77
-rw-r--r--src/update.rs56
2 files changed, 123 insertions, 10 deletions
diff --git a/src/cli.rs b/src/cli.rs
index b6e2fc94..f756b96f 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -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(&current_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(&current_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
));
}