From 0556852500d6fff52467581af1f6e48700bfc90b Mon Sep 17 00:00:00 2001 From: Salvador Cipolla Date: Sat, 23 May 2026 16:23:00 -0300 Subject: [PATCH 1/4] Add legacy update channel --- Knossos.NET/Classes/KnUtils.cs | 23 ++++ Knossos.NET/Models/GitHubApi.cs | 115 +++++++++++++++++- .../ViewModels/GlobalSettingsViewModel.cs | 12 +- Knossos.NET/Views/GlobalSettingsView.axaml | 1 + 4 files changed, 144 insertions(+), 7 deletions(-) diff --git a/Knossos.NET/Classes/KnUtils.cs b/Knossos.NET/Classes/KnUtils.cs index 09c15d64..dedc740c 100644 --- a/Knossos.NET/Classes/KnUtils.cs +++ b/Knossos.NET/Classes/KnUtils.cs @@ -1391,5 +1391,28 @@ public static void CreateDesktopShortcut(string shortcutName, string destFileFul Log.Add(Log.LogSeverity.Error, "KnUtils.CreateDesktopShortcut()", ex); } } + + /// + /// Determines if the current OS meets the requirements for .NET 10 / "latest" channel updates. + /// Windows 10+ or macOS 12.2+ qualify. + /// + public static bool IsModernOS() + { + if (IsWindows) + { + // Windows build 10240 = Windows 10 RTM + return Environment.OSVersion.Version.Major >= 10; + } + + if (IsMacOS) + { + var ver = Environment.OSVersion.Version; + // macOS 12.0 + return ver.Major >= 12; + } + + // Linux and anything else: treat as modern (no restriction planned) + return true; + } } } diff --git a/Knossos.NET/Models/GitHubApi.cs b/Knossos.NET/Models/GitHubApi.cs index e9bc2470..a2af7702 100644 --- a/Knossos.NET/Models/GitHubApi.cs +++ b/Knossos.NET/Models/GitHubApi.cs @@ -1,6 +1,10 @@ -using System; +using Knossos.NET.Classes; +using System; +using System.Collections.Generic; +using System.Linq; using System.Net.Http; using System.Net.Http.Headers; +using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; @@ -9,11 +13,27 @@ namespace Knossos.NET.Models public static class GitHubApi { /// - /// Get the last release on Knet github repo - /// URL of this repo is set on the Knossos class + /// Gets the latest applicable release from GitHub. + /// - Modern platforms (Win10+ / macOS 12.2+): uses /releases/latest (current behaviour). + /// - Legacy platforms: scans /releases, keeps only v1.3.x tags, returns the newest one. /// - /// GitHubRelease or null if the api call failed + /// GitHubRelease or null if the API call failed. public static async Task GetLastRelease() + { + if (KnUtils.IsModernOS()) + { + return await GetLatestRelease(); + } + else + { + return await GetLatestLegacyRelease(); + } + } + + /// + /// Calls /releases/latest — for modern platforms. + /// + private static async Task GetLatestRelease() { try { @@ -21,16 +41,99 @@ public static class GitHubApi client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("product", "1")); using var response = await client.GetAsync(Knossos.GitHubUpdateRepoURL + "/releases/latest"); var json = await response.Content.ReadAsStringAsync(); - return JsonSerializer.Deserialize(json)!; + return JsonSerializer.Deserialize(json); } catch (Exception ex) { - Log.Add(Log.LogSeverity.Error, "GitHubApi.GetLastRelease()", ex); + Log.Add(Log.LogSeverity.Error, "GitHubApi.GetLatestRelease()", ex); return null; } } + + /// + /// Scans /releases pages looking for v1.3.x tags and returns the newest one. + /// Stops paginating early once tags leave the 1.3.x range (assumes releases + /// are returned newest-first by the API). + /// + private static async Task GetLatestLegacyRelease() + { + try + { + var client = KnUtils.GetHttpClient(); + client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("product", "1")); + + var candidates = new List(); + int page = 1; + const int perPage = 30; // GitHub default; max is 100 + + while (true) + { + var url = $"{Knossos.GitHubUpdateRepoURL}/releases?per_page={perPage}&page={page}"; + using var response = await client.GetAsync(url); + var json = await response.Content.ReadAsStringAsync(); + var releases = JsonSerializer.Deserialize>(json); + + if (releases == null || releases.Count == 0) + break; + + foreach (var release in releases) + { + if (IsLegacyTag(release.tag_name)) + { + candidates.Add(release); + } + } + + // If the oldest release on this page is already below 1.3.7, no need to go further + var lastTag = releases.Last().tag_name; + if (lastTag != null && IsBelowLegacyMinor(lastTag)) + break; + + if (releases.Count < perPage) + break; // last page + + page++; + } + + // Return the candidate with the highest semantic version + return candidates + .Where(r => r.tag_name != null) + .MaxBy(r => new SemanticVersion(NormalizeTag(r.tag_name!))); + } + catch (Exception ex) + { + Log.Add(Log.LogSeverity.Error, "GitHubApi.GetLatestLegacyRelease()", ex); + return null; + } + } + + /// + /// Returns true if the tag is a v1.3.x release (e.g. "v1.3.7", "v1.3.10-rc1"). + /// + private static bool IsLegacyTag(string? tag) + { + if (string.IsNullOrWhiteSpace(tag)) return false; + var version = new SemanticVersion(NormalizeTag(tag)); + // Check major == 1 and minor == 3 by comparing against sentinels + return SemanticVersion.Compare(NormalizeTag(tag), "1.3.0") >= 0 && + SemanticVersion.Compare(NormalizeTag(tag), "1.4.0") < 0; + } + + /// + /// Returns true if the tag is strictly older than 1.3.7 — used as an early-exit + /// hint while paginating (GitHub returns releases newest-first). + /// + private static bool IsBelowLegacyMinor(string tag) + { + return SemanticVersion.Compare(NormalizeTag(tag), "1.3.7") < 0; + } + + /// Strips the leading "v" that GitHub tags typically have. + private static string NormalizeTag(string tag) => + tag.ToLower().Replace("v", "").Trim(); } + public class GitHubRelease { public string? url { get; set; } diff --git a/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs b/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs index 8f3ea01f..7860bde1 100644 --- a/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs +++ b/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs @@ -36,7 +36,9 @@ public partial class GlobalSettingsViewModel : ViewModelBase private const long speed10MB = 170000000; /* For display only */ - [ObservableProperty] + [ObservableProperty] + internal string knossosUpdateChannelInfo = ""; + [ObservableProperty] internal bool isPortableMode = false; [ObservableProperty] internal bool flagDataLoaded = false; @@ -552,6 +554,14 @@ public string GlobalCmd public GlobalSettingsViewModel() { isPortableMode = Knossos.inPortableMode; + if (KnUtils.IsModernOS()) + { + knossosUpdateChannelInfo = "(Main Update Channel)"; + } + else + { + knossosUpdateChannelInfo = "(Legacy Update Channel)"; + } } public void CheckDisplaySettingsWarning() diff --git a/Knossos.NET/Views/GlobalSettingsView.axaml b/Knossos.NET/Views/GlobalSettingsView.axaml index db98d926..d8b6d4d0 100644 --- a/Knossos.NET/Views/GlobalSettingsView.axaml +++ b/Knossos.NET/Views/GlobalSettingsView.axaml @@ -73,6 +73,7 @@ + From fa7432e2c2f4094babc2b41ae41b9407ec99ca63 Mon Sep 17 00:00:00 2001 From: Salvador Cipolla Date: Sat, 23 May 2026 16:42:59 -0300 Subject: [PATCH 2/4] correct summary --- Knossos.NET/Classes/KnUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Knossos.NET/Classes/KnUtils.cs b/Knossos.NET/Classes/KnUtils.cs index dedc740c..e543645a 100644 --- a/Knossos.NET/Classes/KnUtils.cs +++ b/Knossos.NET/Classes/KnUtils.cs @@ -1394,7 +1394,7 @@ public static void CreateDesktopShortcut(string shortcutName, string destFileFul /// /// Determines if the current OS meets the requirements for .NET 10 / "latest" channel updates. - /// Windows 10+ or macOS 12.2+ qualify. + /// Windows 10+ or macOS 12.0+ qualify. /// public static bool IsModernOS() { From 25fa37d6973ab2b14e76fcea1dab2ce335dabf47 Mon Sep 17 00:00:00 2001 From: Salvador Cipolla Date: Sat, 23 May 2026 16:44:42 -0300 Subject: [PATCH 3/4] correct summary 2 --- Knossos.NET/Models/GitHubApi.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Knossos.NET/Models/GitHubApi.cs b/Knossos.NET/Models/GitHubApi.cs index a2af7702..02c11194 100644 --- a/Knossos.NET/Models/GitHubApi.cs +++ b/Knossos.NET/Models/GitHubApi.cs @@ -14,7 +14,7 @@ public static class GitHubApi { /// /// Gets the latest applicable release from GitHub. - /// - Modern platforms (Win10+ / macOS 12.2+): uses /releases/latest (current behaviour). + /// - Modern platforms (Win10+ / macOS 12.0+): uses /releases/latest (current behaviour). /// - Legacy platforms: scans /releases, keeps only v1.3.x tags, returns the newest one. /// /// GitHubRelease or null if the API call failed. From 2c0bdbc21ffc5352d9d3f29fe76759acd8272d15 Mon Sep 17 00:00:00 2001 From: Salvador Cipolla Date: Sun, 7 Jun 2026 10:31:52 -0300 Subject: [PATCH 4/4] give more information for the legacy channel --- Knossos.NET/ViewModels/GlobalSettingsViewModel.cs | 13 +++++++------ Knossos.NET/Views/GlobalSettingsView.axaml | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs b/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs index 7860bde1..662fe40a 100644 --- a/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs +++ b/Knossos.NET/ViewModels/GlobalSettingsViewModel.cs @@ -39,6 +39,8 @@ public partial class GlobalSettingsViewModel : ViewModelBase [ObservableProperty] internal string knossosUpdateChannelInfo = ""; [ObservableProperty] + internal string knossosUpdateTooltip = ""; + [ObservableProperty] internal bool isPortableMode = false; [ObservableProperty] internal bool flagDataLoaded = false; @@ -554,13 +556,12 @@ public string GlobalCmd public GlobalSettingsViewModel() { isPortableMode = Knossos.inPortableMode; - if (KnUtils.IsModernOS()) - { - knossosUpdateChannelInfo = "(Main Update Channel)"; - } - else + if (!KnUtils.IsModernOS()) { - knossosUpdateChannelInfo = "(Legacy Update Channel)"; + KnossosUpdateChannelInfo = "(Legacy Update Channel)"; + var current = KnUtils.IsWindows ? $"Windows {Environment.OSVersion.Version.Major}" : $"MacOS {Environment.OSVersion.Version}"; + var minimum = KnUtils.IsWindows ? "Windows 10+" : "MacOS 12+"; + KnossosUpdateTooltip = $"Your version of {current} is not compatible with version 1.4 of Knossos.NET and above. To receive new features, you should upgrade to version {minimum}. You will still receive bugfix updates when they apply to version 1.3."; } } diff --git a/Knossos.NET/Views/GlobalSettingsView.axaml b/Knossos.NET/Views/GlobalSettingsView.axaml index d8b6d4d0..5523dd9c 100644 --- a/Knossos.NET/Views/GlobalSettingsView.axaml +++ b/Knossos.NET/Views/GlobalSettingsView.axaml @@ -73,7 +73,7 @@ -