diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-08-18 22:39:38 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-08-18 22:39:38 +0300 |
| commit | 9fac51722653fa0b0b6a5786166fb18c763424e0 (patch) | |
| tree | a8efd3b30cf29a8fb11138b7444bb9baa0dac2fa /src | |
| parent | 0bd445020ea19e0cea13f42a28212db7b2df196d (diff) | |
feat: make Windows generally available
Diffstat (limited to 'src')
| -rw-r--r-- | src/cli.rs | 30 | ||||
| -rw-r--r-- | src/config/model.rs | 34 | ||||
| -rw-r--r-- | src/main.rs | 4 | ||||
| -rw-r--r-- | src/update.rs | 73 |
4 files changed, 94 insertions, 47 deletions
@@ -216,12 +216,6 @@ fn channel_set_rejection( channel: &str, install_rejection: Option<&'static str>, ) -> Option<&'static str> { - if cfg!(windows) && channel == "stable" { - return Some( - "stable channel is not available on Windows yet; Windows builds are preview-only", - ); - } - if channel == "preview" { return install_rejection; } @@ -1052,39 +1046,19 @@ mod tests { } #[test] - fn channel_set_rejects_package_managed_preview_before_config_write() { + fn channel_set_only_applies_package_rejection_to_preview() { assert_eq!( super::channel_set_rejection("preview", Some("no preview")), Some("no preview") ); assert_eq!( super::channel_set_rejection("stable", Some("no preview")), - if cfg!(windows) { - Some( - "stable channel is not available on Windows yet; Windows builds are preview-only", - ) - } else { - None - } + None ); assert_eq!(super::channel_set_rejection("preview", None), None); } #[test] - fn channel_set_rejects_stable_only_on_windows() { - assert_eq!( - super::channel_set_rejection("stable", None), - if cfg!(windows) { - Some( - "stable channel is not available on Windows yet; Windows builds are preview-only", - ) - } else { - None - } - ); - } - - #[test] fn channel_set_skips_self_update_for_package_manager_guidance() { assert_eq!( super::channel_set_install_action(Some("use package manager")), diff --git a/src/config/model.rs b/src/config/model.rs index ad779d26..c10cd733 100644 --- a/src/config/model.rs +++ b/src/config/model.rs @@ -47,7 +47,11 @@ impl Default for UpdateConfig { } fn default_update_channel() -> UpdateChannelConfig { - if cfg!(windows) { + default_update_channel_for_build(cfg!(windows), crate::build_info::is_preview()) +} + +fn default_update_channel_for_build(is_windows: bool, is_preview: bool) -> UpdateChannelConfig { + if is_windows && is_preview { UpdateChannelConfig::Preview } else { UpdateChannelConfig::Stable @@ -1251,21 +1255,33 @@ manifest_check = false assert!(!config.update.manifest_check); } - #[cfg(windows)] #[test] - fn windows_update_config_defaults_to_preview() { + fn update_channel_default_follows_windows_build_identity() { + assert_eq!( + default_update_channel_for_build(true, true), + UpdateChannelConfig::Preview + ); + assert_eq!( + default_update_channel_for_build(true, false), + UpdateChannelConfig::Stable + ); + assert_eq!( + default_update_channel_for_build(false, true), + UpdateChannelConfig::Stable + ); + } + + #[test] + fn missing_update_channel_uses_build_default() { let empty: Config = toml::from_str("").unwrap(); let without_update_channel: Config = toml::from_str("[update]\nversion_check = false").unwrap(); - assert_eq!( - Config::default().update.channel, - UpdateChannelConfig::Preview - ); - assert_eq!(empty.update.channel, UpdateChannelConfig::Preview); + assert_eq!(Config::default().update.channel, default_update_channel()); + assert_eq!(empty.update.channel, default_update_channel()); assert_eq!( without_update_channel.update.channel, - UpdateChannelConfig::Preview + default_update_channel() ); } diff --git a/src/main.rs b/src/main.rs index d2fc0595..36b1d021 100644 --- a/src/main.rs +++ b/src/main.rs @@ -153,8 +153,8 @@ const DEFAULT_CONFIG: &str = r##"# herdr configuration [update] # Update channel used by background version checks and `herdr update`. -# Defaults to "stable" on Linux/macOS and "preview" on Windows. -# Set explicitly to choose stable releases or opt-in preview builds. +# Stable builds default to "stable". Windows preview builds default to "preview" +# so existing preview installs stay there until explicitly switched. # channel = "stable" # Check herdr.dev for new Herdr versions in the background. diff --git a/src/update.rs b/src/update.rs index 161386f9..5359944a 100644 --- a/src/update.rs +++ b/src/update.rs @@ -511,6 +511,14 @@ fn release_info_from_preview_manifest( } /// Check the hosted update manifest for the latest release. Returns release info if newer. +fn first_windows_stable_is_pending( + manifest: &UpdateManifest, + is_windows: bool, + installed_is_preview: bool, +) -> bool { + is_windows && installed_is_preview && !manifest.assets.contains_key("windows-x86_64") +} + fn check_latest() -> Result<Option<ReleaseInfo>, String> { let channel = UpdateChannel::configured(); if channel == UpdateChannel::Preview { @@ -518,6 +526,10 @@ fn check_latest() -> Result<Option<ReleaseInfo>, String> { } let manifest = fetch_update_manifest()?; + if first_windows_stable_is_pending(&manifest, cfg!(windows), crate::build_info::is_preview()) { + tracing::info!("waiting for the first stable Windows release"); + return Ok(None); + } let release = release_info_from_manifest(&manifest)?; if let Some(release) = &release { if let Some(metadata) = manifest.metadata_for_version(&release.version) { @@ -2058,12 +2070,6 @@ fn homebrew_cellar_keg_root(path: &Path) -> Option<PathBuf> { /// Manual self-update command (`herdr update`). pub fn self_update(options: SelfUpdateOptions) -> Result<Version, String> { let channel = UpdateChannel::configured(); - #[cfg(windows)] - if channel == UpdateChannel::Stable { - return Err( - "Windows builds are preview-only for now; run `herdr channel set preview`".into(), - ); - } if is_homebrew_managed_install() { if channel == UpdateChannel::Preview { @@ -3553,6 +3559,35 @@ mod tests { } #[test] + fn preview_windows_build_waits_for_first_stable_asset() { + let without_windows: UpdateManifest = serde_json::from_str( + r#"{"version":"9.9.9","notes":"notes","assets":{},"announcement":null}"#, + ) + .unwrap(); + assert!(first_windows_stable_is_pending( + &without_windows, + true, + true + )); + assert!(!first_windows_stable_is_pending( + &without_windows, + true, + false + )); + assert!(!first_windows_stable_is_pending( + &without_windows, + false, + true + )); + + let with_windows: UpdateManifest = serde_json::from_str( + r#"{"version":"9.9.9","notes":"notes","assets":{"windows-x86_64":"https://example.com/herdr-windows-x86_64.zip"},"announcement":null}"#, + ) + .unwrap(); + assert!(!first_windows_stable_is_pending(&with_windows, true, true)); + } + + #[test] fn checked_in_website_manifest_matches_update_schema() { #[derive(Deserialize)] struct LegacyUpdateManifest { @@ -3562,7 +3597,7 @@ mod tests { let json = include_str!("../website/latest.json"); let legacy: LegacyUpdateManifest = serde_json::from_str(json) .expect("website/latest.json should keep legacy string asset URLs"); - assert_eq!(legacy.assets.len(), 4); + assert!(legacy.assets.len() >= 4); let manifest: UpdateManifest = serde_json::from_str(json).expect("website/latest.json should match updater schema"); @@ -3576,7 +3611,7 @@ mod tests { // current unreleased checkout. Its protocol is updated by the release // flow together with the release assets. assert!(manifest.protocol.is_some()); - assert_eq!(manifest.assets.len(), 4); + assert!(manifest.assets.len() >= 4); assert!(manifest.releases.contains_key(&manifest.version)); for target in [ @@ -3605,6 +3640,15 @@ mod tests { ); } + if let Some(windows) = manifest.assets.get("windows-x86_64") { + assert!(windows.url.ends_with("/herdr-windows-x86_64.zip")); + assert_eq!( + manifest.sha256.get("windows-x86_64").map(String::len), + Some(64), + "missing SHA-256 checksum for windows-x86_64" + ); + } + for (version, release) in &manifest.releases { let assets = release .get("assets") @@ -3632,6 +3676,19 @@ mod tests { "unexpected asset name for {version} {target}: {url}" ); } + if let Some(windows) = assets.get("windows-x86_64") { + let windows: AssetRef = serde_json::from_value(windows.clone()) + .unwrap_or_else(|_| panic!("invalid Windows asset for release {version}")); + assert!(windows.url.ends_with("/herdr-windows-x86_64.zip")); + let checksums = release + .get("sha256") + .and_then(serde_json::Value::as_object) + .unwrap_or_else(|| panic!("missing checksums for release {version}")); + assert!(checksums + .get("windows-x86_64") + .and_then(serde_json::Value::as_str) + .is_some_and(|value| value.len() == 64)); + } } } } |
