diff --git a/ApplicationUpdate.sln.DotSettings b/ApplicationUpdate.sln.DotSettings new file mode 100644 index 0000000..c960585 --- /dev/null +++ b/ApplicationUpdate.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/GitHubRelease.cs b/GitHubRelease.cs index ac14279..2155461 100644 --- a/GitHubRelease.cs +++ b/GitHubRelease.cs @@ -1,30 +1,84 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using System.Globalization; +using System.Net.Http.Headers; +using System.Text.RegularExpressions; namespace ChrisKaczor.ApplicationUpdate { public partial class GitHubRelease { - [JsonProperty("tag_name")] - public string? TagName { get; set; } + private const string LatestSegment = "/latest"; - [JsonProperty("assets")] - public List? Assets { get; set; } + private static readonly HttpClient HttpClient = new() + { + DefaultRequestHeaders = + { UserAgent = { ProductInfoHeaderValue.Parse(UpdateCheck.ApplicationName.Replace(" ", "")) } } + }; + + private static GitHubRelease? FromJson(string json) => + JsonConvert.DeserializeObject(json, Converter.Settings); + + private static List? FromJsonArray(string json) => + JsonConvert.DeserializeObject>(json, Converter.Settings); + + [JsonProperty("tag_name")] public string? TagName { get; set; } + + [JsonProperty("assets")] public List? Assets { get; set; } + + [JsonProperty("prerelease")] public bool Prerelease { get; set; } + + public static async Task GetLatestAsync(string baseUrl) + { + try + { + var url = baseUrl.EndsWith(LatestSegment) ? baseUrl : baseUrl + LatestSegment; + + var json = await HttpClient.GetStringAsync(url); + + var release = FromJson(json); + + var versionInfo = VersionInfo.CreateFromGitHubRelease(release); + + return versionInfo; + } + catch (Exception) + { + return null; + } + } + + public static async Task GetLatestPrereleaseAsync(string baseUrl) + { + try + { + var url = baseUrl.EndsWith(LatestSegment) ? baseUrl[..^LatestSegment.Length] : baseUrl; + + var json = await HttpClient.GetStringAsync(url); + + var releases = FromJsonArray(json); + + var release = releases?.FirstOrDefault(); + + if (release == null) + return null; + + var versionInfo = VersionInfo.CreateFromGitHubRelease(release); + + return versionInfo; + } + catch (Exception) + { + return null; + } + } } public class Asset { - [JsonProperty("created_at")] - public DateTimeOffset? CreatedAt { get; set; } + [JsonProperty("created_at")] public DateTimeOffset? CreatedAt { get; set; } - [JsonProperty("browser_download_url")] - public string? BrowserDownloadUrl { get; set; } - } - - public partial class GitHubRelease - { - public static GitHubRelease? FromJson(string json) => JsonConvert.DeserializeObject(json, Converter.Settings); + [JsonProperty("browser_download_url")] public string? BrowserDownloadUrl { get; set; } } internal class Converter diff --git a/UpdateCheck.cs b/UpdateCheck.cs index 0adecee..45630b0 100644 --- a/UpdateCheck.cs +++ b/UpdateCheck.cs @@ -45,9 +45,9 @@ public static class UpdateCheck ApplicationUpdateMessage = applicationUpdateMessageDelegate; } - public static async Task CheckForUpdate() + public static async Task CheckForUpdate(bool includePrerelease) { - RemoteVersion = await VersionInfo.Load(UpdateServerType, UpdateServer, UpdateFile); + RemoteVersion = await VersionInfo.Load(UpdateServerType, UpdateServer, UpdateFile, includePrerelease); if (RemoteVersion == null) return false; @@ -98,9 +98,9 @@ public static class UpdateCheck return true; } - public static async void DisplayUpdateInformation(bool showIfCurrent) + public static async Task DisplayUpdateInformation(bool showIfCurrent, bool includePrerelease) { - await CheckForUpdate(); + await CheckForUpdate(includePrerelease); // Check for an update if (UpdateAvailable) diff --git a/VersionInfo.cs b/VersionInfo.cs index e05de66..fa84f27 100644 --- a/VersionInfo.cs +++ b/VersionInfo.cs @@ -1,28 +1,35 @@ -using System.Xml.Linq; +using System.Net.Http.Headers; +using System.Xml.Linq; namespace ChrisKaczor.ApplicationUpdate; public class VersionInfo { + private static readonly HttpClient HttpClient = new() { DefaultRequestHeaders = { UserAgent = { ProductInfoHeaderValue.Parse(UpdateCheck.ApplicationName) } } }; + public Version? Version { get; set; } public string? InstallFile { get; set; } public DateTimeOffset? InstallCreated { get; set; } + public bool? Prerelease { get; set; } - public static async Task Load(ServerType updateServerType, string server, string file) + public static async Task Load(ServerType updateServerType, string url, string file, bool includePrerelease) { return updateServerType switch { - ServerType.Generic => LoadFile(server, file), - ServerType.GitHub => await LoadGitHub(server), + ServerType.Generic => LoadFile(url, file, includePrerelease), + ServerType.GitHub => await LoadGitHub(url, includePrerelease), _ => null }; } - private static VersionInfo? LoadFile(string server, string file) + private static VersionInfo? LoadFile(string url, string file, bool includePrerelease) { try { - var document = XDocument.Load(server + file); + if (includePrerelease) + throw new NotSupportedException("Prerelease not currently supported for generic server type"); + + var document = XDocument.Load(url + file); var versionInformationElement = document.Element("versionInformation"); @@ -51,33 +58,26 @@ public class VersionInfo } } - private static async Task LoadGitHub(string server) + private static async Task LoadGitHub(string baseUrl, bool includePrerelease) { - try - { - var httpClient = new HttpClient(); + var versionInfo = includePrerelease ? await GitHubRelease.GetLatestPrereleaseAsync(baseUrl) : await GitHubRelease.GetLatestAsync(baseUrl); + + return versionInfo; + } - httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(UpdateCheck.ApplicationName); - - var json = await httpClient.GetStringAsync(server); - - var release = GitHubRelease.FromJson(json); - - if (release?.TagName == null || release.Assets == null || release.Assets.Count == 0) - return null; - - var versionInfo = new VersionInfo - { - Version = Version.Parse(release.TagName), - InstallFile = release.Assets[0].BrowserDownloadUrl, - InstallCreated = release.Assets[0].CreatedAt?.UtcDateTime - }; - - return versionInfo; - } - catch (Exception) - { + internal static VersionInfo? CreateFromGitHubRelease(GitHubRelease? release) + { + if (release?.TagName == null || release.Assets == null || release.Assets.Count == 0) return null; - } + + var versionInfo = new VersionInfo + { + Version = Version.Parse(release.TagName), + InstallFile = release.Assets[0].BrowserDownloadUrl, + InstallCreated = release.Assets[0].CreatedAt?.UtcDateTime, + Prerelease = release.Prerelease + }; + + return versionInfo; } } \ No newline at end of file