diff --git a/App.axaml.cs b/App.axaml.cs index 32f1deb..0183e31 100644 --- a/App.axaml.cs +++ b/App.axaml.cs @@ -5,6 +5,7 @@ using Avalonia.Markup.Xaml; using Avalonia.Media; using Avalonia.Threading; +using GitHubLauncher.Core.Services; using System; using System.ComponentModel; using System.Diagnostics; @@ -743,9 +744,9 @@ private string NormalizeVersionString(string version) private async Task DownloadAndApplyUpdate(GitHubRelease latestRelease, string currentAppDirectory, UpdateCheckInfo updateCheckInfo) { - string platformIdentifier = GetPlatformIdentifier(); + string platformIdentifier = PlatformAssetMatcher.GetPlatformIdentifier(GitHubLauncher.Core.Models.TargetOS.Auto); var asset = latestRelease.assets.FirstOrDefault(a => - a.name.Contains(platformIdentifier, StringComparison.OrdinalIgnoreCase) && + PlatformAssetMatcher.MatchesPlatform(a.name, platformIdentifier) && (a.name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) || a.name.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase)) ); @@ -1291,30 +1292,4 @@ private void ShutdownForUpdate() Environment.Exit(0); }); } - - private string GetPlatformIdentifier() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - return "Windows"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - { - return "macOS"; - } - else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - var arch = RuntimeInformation.OSArchitecture; - return arch switch - { - Architecture.Arm64 => "Linux-ARM64", - Architecture.X64 => "Linux-X64", - Architecture.X86 => "Linux-X86", - Architecture.Arm => "Linux-ARM", - _ => "Linux-X64" - }; - } - - throw new PlatformNotSupportedException("Unsupported operating system"); - } } diff --git a/MainWindow.axaml.cs b/MainWindow.axaml.cs index 243e94d..4a4bac7 100644 --- a/MainWindow.axaml.cs +++ b/MainWindow.axaml.cs @@ -1428,7 +1428,7 @@ private async Task HandleUpdateNowAsync(Control anchor, GameInfo game) { game.IsLoading = true; var releases = await game.FetchReleasesAsync(_gameManager.HttpClient); - var latestRelease = releases.FirstOrDefault(); + var latestRelease = GameInfo.SelectLatestRelease(releases); if (latestRelease == null) { await ShowMessageBoxAsync($"No downloadable releases were found for {game.Name}.", "No Releases"); diff --git a/Models/GameInfo.cs b/Models/GameInfo.cs index 010b4b5..eb5343a 100644 --- a/Models/GameInfo.cs +++ b/Models/GameInfo.cs @@ -1217,7 +1217,7 @@ private async Task CheckLatestVersionAsync(HttpClient httpClient, bool forceChec return; } - var latestRelease = result.Releases.FirstOrDefault(); + var latestRelease = SelectLatestRelease(result.Releases); if (latestRelease != null && !string.IsNullOrWhiteSpace(latestRelease.tag_name)) { LatestVersion = latestRelease.tag_name; @@ -1396,6 +1396,28 @@ buttonPanel.Children[0] is Button yesButton && return _cachedRelease; } + /// + /// Selects the release that should be treated as "latest". Releases from + /// the GitHub API are newest-first but include pre-releases, so a repo + /// whose newest release is a pre-release (e.g. nightly/beta builds) would + /// otherwise be offered as the current version. Prefer the newest stable + /// release; fall back to the newest pre-release only when a repo publishes + /// no stable releases at all. Mirrors GitHub's own /releases/latest. + /// + public static GitHubRelease? SelectLatestRelease(IReadOnlyList releases) + { + if (releases == null || releases.Count == 0) + return null; + + for (int i = 0; i < releases.Count; i++) + { + if (!releases[i].prerelease) + return releases[i]; + } + + return releases[0]; + } + public async Task> FetchReleasesAsync(HttpClient httpClient) { if (string.IsNullOrWhiteSpace(Repository)) @@ -1463,7 +1485,7 @@ private async Task DownloadAndInstallAsync(HttpClient httpClient, string gamesFo return; } - latestRelease = releaseResult.Releases.FirstOrDefault(); + latestRelease = SelectLatestRelease(releaseResult.Releases); if (latestRelease == null) { diff --git a/Services/CLIHandler.cs b/Services/CLIHandler.cs index e2bddb1..b41e29f 100644 --- a/Services/CLIHandler.cs +++ b/Services/CLIHandler.cs @@ -858,9 +858,9 @@ private async Task UpdateLauncher() return 0; } - string platformIdentifier = GetPlatformIdentifier(); + string platformIdentifier = PlatformAssetMatcher.GetPlatformIdentifier(TargetOS.Auto); var asset = release.assets.FirstOrDefault(a => - a.name.Contains(platformIdentifier, StringComparison.OrdinalIgnoreCase) && + PlatformAssetMatcher.MatchesPlatform(a.name, platformIdentifier) && (a.name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) || a.name.EndsWith(".tar.gz", StringComparison.OrdinalIgnoreCase))); if (asset == null) @@ -1119,28 +1119,6 @@ exit 1 }); } } - - private static string GetPlatformIdentifier() - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return "Windows"; - if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - return "macOS"; - if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - { - return RuntimeInformation.OSArchitecture switch - { - Architecture.Arm64 => "Linux-ARM64", - Architecture.X64 => "Linux-X64", - Architecture.X86 => "Linux-X86", - Architecture.Arm => "Linux-ARM", - _ => "Linux-X64" - }; - } - - throw new PlatformNotSupportedException("Unsupported operating system"); - } - private static bool IsNewerVersion(string latestVersion, string currentVersion) { try