mirror of
https://github.com/Stone-Red-Code/AlwaysUpToDate.git
synced 2026-09-04 00:46:05 +02:00
Add architecture support and refine OS handling in update manifest parsing
This commit is contained in:
@@ -5,7 +5,7 @@ namespace AlwaysUpToDate.Test;
|
|||||||
|
|
||||||
internal class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
private static readonly Updater updater = new Updater(new TimeSpan(0, 0, 1), "https://raw.githubusercontent.com/Stone-Red-Code/Test/main/test.xml", true);
|
private static readonly Updater updater = new Updater(new TimeSpan(0, 0, 1), "https://echohub.voidcube.cloud/api/app/version", true);
|
||||||
|
|
||||||
private static async Task Main(string[] args)
|
private static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -178,11 +178,13 @@ namespace AlwaysUpToDate
|
|||||||
using HttpResponseMessage response = await httpClient.GetAsync(updateInfoUrl);
|
using HttpResponseMessage response = await httpClient.GetAsync(updateInfoUrl);
|
||||||
_ = response.EnsureSuccessStatusCode();
|
_ = response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
using Stream stream = await response.Content.ReadAsStreamAsync();
|
string xmlManifest = await response.Content.ReadAsStringAsync();
|
||||||
UpdateManifest manifest = (UpdateManifest)manifestSerializer.Deserialize(stream);
|
UpdateManifest manifest = (UpdateManifest)manifestSerializer.Deserialize(new StringReader(xmlManifest));
|
||||||
|
|
||||||
TargetOS currentOS = GetCurrentOS();
|
TargetOS currentOS = GetCurrentOS();
|
||||||
UpdateItem updateItem = manifest.Items?.FirstOrDefault(i => i.OS == currentOS);
|
TargetArchitecture currentArch = GetCurrentArchitecture();
|
||||||
|
UpdateItem updateItem = manifest.Items?.FirstOrDefault(i => i.OS == currentOS && i.Architecture == currentArch)
|
||||||
|
?? manifest.Items?.FirstOrDefault(i => i.OS == currentOS && i.Architecture == TargetArchitecture.Any);
|
||||||
|
|
||||||
if (updateItem == null)
|
if (updateItem == null)
|
||||||
{
|
{
|
||||||
@@ -246,6 +248,18 @@ namespace AlwaysUpToDate
|
|||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TargetArchitecture GetCurrentArchitecture()
|
||||||
|
{
|
||||||
|
return RuntimeInformation.ProcessArchitecture switch
|
||||||
|
{
|
||||||
|
Architecture.X86 => TargetArchitecture.X86,
|
||||||
|
Architecture.X64 => TargetArchitecture.X64,
|
||||||
|
Architecture.Arm => TargetArchitecture.Arm,
|
||||||
|
Architecture.Arm64 => TargetArchitecture.Arm64,
|
||||||
|
_ => TargetArchitecture.Any,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private async Task DownloadFile()
|
private async Task DownloadFile()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -255,6 +269,8 @@ namespace AlwaysUpToDate
|
|||||||
|
|
||||||
UpdateStarted?.Invoke(pendingUpdateItem?.Version);
|
UpdateStarted?.Invoke(pendingUpdateItem?.Version);
|
||||||
|
|
||||||
|
TriggerProgressChanged(UpdateStep.Downloading, null, 0);
|
||||||
|
|
||||||
using HttpResponseMessage response = await httpClient.GetAsync(updateUrl);
|
using HttpResponseMessage response = await httpClient.GetAsync(updateUrl);
|
||||||
_ = response.EnsureSuccessStatusCode();
|
_ = response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
@@ -366,6 +382,7 @@ namespace AlwaysUpToDate
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = Process.Start(new ProcessStartInfo(executablePath) { UseShellExecute = false });
|
_ = Process.Start(new ProcessStartInfo(executablePath) { UseShellExecute = false });
|
||||||
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -383,29 +400,31 @@ namespace AlwaysUpToDate
|
|||||||
byte[] buffer = new byte[8192];
|
byte[] buffer = new byte[8192];
|
||||||
bool isMoreToRead = true;
|
bool isMoreToRead = true;
|
||||||
|
|
||||||
using FileStream fileStream = new FileStream(Path.Join(installPath, "Update.zip"), FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
|
using (FileStream fileStream = new FileStream(Path.Join(installPath, "Update.zip"), FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
|
||||||
do
|
|
||||||
{
|
{
|
||||||
int bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
|
do
|
||||||
if (bytesRead == 0)
|
|
||||||
{
|
{
|
||||||
isMoreToRead = false;
|
int bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
|
||||||
TriggerProgressChanged(UpdateStep.Downloading, totalDownloadSize, totalBytesRead);
|
if (bytesRead == 0)
|
||||||
continue;
|
{
|
||||||
}
|
isMoreToRead = false;
|
||||||
|
TriggerProgressChanged(UpdateStep.Downloading, totalDownloadSize, totalBytesRead);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
await fileStream.WriteAsync(buffer, 0, bytesRead);
|
await fileStream.WriteAsync(buffer, 0, bytesRead);
|
||||||
|
|
||||||
totalBytesRead += bytesRead;
|
totalBytesRead += bytesRead;
|
||||||
readCount += 1;
|
readCount += 1;
|
||||||
|
|
||||||
if (readCount >= 10)
|
if (readCount >= 10)
|
||||||
{
|
{
|
||||||
readCount = 0;
|
readCount = 0;
|
||||||
TriggerProgressChanged(UpdateStep.Downloading, totalDownloadSize, totalBytesRead);
|
TriggerProgressChanged(UpdateStep.Downloading, totalDownloadSize, totalBytesRead);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
while (isMoreToRead);
|
||||||
}
|
}
|
||||||
while (isMoreToRead);
|
|
||||||
|
|
||||||
ExtractZipFile();
|
ExtractZipFile();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,42 @@ namespace AlwaysUpToDate
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a single update entry in the manifest, targeting a specific OS.
|
/// Represents a single update entry in the manifest, targeting a specific OS.
|
||||||
|
/// Supports both combined OS-architecture values (e.g., <c>osx-arm64</c>) and
|
||||||
|
/// separate <c><os></c> / <c><arch></c> elements.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UpdateItem
|
public class UpdateItem
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the target operating system for this update.
|
/// Gets or sets the raw OS string from the manifest (e.g., <c>"windows"</c>, <c>"osx-arm64"</c>, <c>"linux"</c>).
|
||||||
|
/// Supports combined OS-architecture values for backward compatibility.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[XmlElement("os")]
|
[XmlElement("os")]
|
||||||
public TargetOS OS { get; set; }
|
public string RawOS { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the raw architecture string from the manifest.
|
||||||
|
/// When present, overrides any architecture embedded in <see cref="RawOS"/>.
|
||||||
|
/// </summary>
|
||||||
|
[XmlElement("arch")]
|
||||||
|
public string RawArchitecture { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parsed target operating system, extracted from <see cref="RawOS"/>.
|
||||||
|
/// Recognizes <c>windows</c>/<c>win</c>, <c>macos</c>/<c>osx</c>, and <c>linux</c> prefixes.
|
||||||
|
/// </summary>
|
||||||
|
[XmlIgnore]
|
||||||
|
public TargetOS OS => ParseTargetOS(RawOS);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parsed target architecture. Uses <see cref="RawArchitecture"/> if present;
|
||||||
|
/// otherwise extracts the architecture suffix from <see cref="RawOS"/> (e.g., <c>osx-arm64</c> → <see cref="TargetArchitecture.Arm64"/>).
|
||||||
|
/// Defaults to <see cref="TargetArchitecture.Any"/> when no architecture is specified.
|
||||||
|
/// </summary>
|
||||||
|
[XmlIgnore]
|
||||||
|
public TargetArchitecture Architecture =>
|
||||||
|
!string.IsNullOrEmpty(RawArchitecture)
|
||||||
|
? ParseArchitectureString(RawArchitecture)
|
||||||
|
: ParseEmbeddedArchitecture(RawOS);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the version string of the update in <c>X.X.X.X</c> format.
|
/// Gets or sets the version string of the update in <c>X.X.X.X</c> format.
|
||||||
@@ -57,6 +85,60 @@ namespace AlwaysUpToDate
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[XmlElement("checksum")]
|
[XmlElement("checksum")]
|
||||||
public Checksum Checksum { get; set; }
|
public Checksum Checksum { get; set; }
|
||||||
|
|
||||||
|
private static TargetOS ParseTargetOS(string raw)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(raw))
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dashIndex = raw.IndexOf('-');
|
||||||
|
string osPart = dashIndex >= 0 ? raw[..dashIndex] : raw;
|
||||||
|
|
||||||
|
switch (osPart.ToLowerInvariant())
|
||||||
|
{
|
||||||
|
case "windows":
|
||||||
|
case "win":
|
||||||
|
return TargetOS.Windows;
|
||||||
|
case "macos":
|
||||||
|
case "osx":
|
||||||
|
return TargetOS.MacOS;
|
||||||
|
case "linux":
|
||||||
|
return TargetOS.Linux;
|
||||||
|
default:
|
||||||
|
throw new System.NotSupportedException($"Unknown operating system: '{osPart}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TargetArchitecture ParseEmbeddedArchitecture(string raw)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(raw))
|
||||||
|
{
|
||||||
|
return TargetArchitecture.Any;
|
||||||
|
}
|
||||||
|
|
||||||
|
int dashIndex = raw.IndexOf('-');
|
||||||
|
if (dashIndex < 0)
|
||||||
|
{
|
||||||
|
return TargetArchitecture.Any;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseArchitectureString(raw[(dashIndex + 1)..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TargetArchitecture ParseArchitectureString(string raw)
|
||||||
|
{
|
||||||
|
return (raw?.ToLowerInvariant()) switch
|
||||||
|
{
|
||||||
|
"x86" => TargetArchitecture.X86,
|
||||||
|
"x64" => TargetArchitecture.X64,
|
||||||
|
"arm" => TargetArchitecture.Arm,
|
||||||
|
"arm64" => TargetArchitecture.Arm64,
|
||||||
|
"any" => TargetArchitecture.Any,
|
||||||
|
_ => throw new System.NotSupportedException($"Unknown architecture: '{raw}'"),
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -117,6 +199,33 @@ namespace AlwaysUpToDate
|
|||||||
Linux,
|
Linux,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the target processor architecture for an update item.
|
||||||
|
/// <see cref="Any"/> means the update applies to all architectures.
|
||||||
|
/// </summary>
|
||||||
|
public enum TargetArchitecture
|
||||||
|
{
|
||||||
|
/// <summary>Any architecture (default). Used when the update is architecture-independent.</summary>
|
||||||
|
[XmlEnum("any")]
|
||||||
|
Any,
|
||||||
|
|
||||||
|
/// <summary>Intel/AMD 32-bit (x86).</summary>
|
||||||
|
[XmlEnum("x86")]
|
||||||
|
X86,
|
||||||
|
|
||||||
|
/// <summary>Intel/AMD 64-bit (x64).</summary>
|
||||||
|
[XmlEnum("x64")]
|
||||||
|
X64,
|
||||||
|
|
||||||
|
/// <summary>ARM 32-bit.</summary>
|
||||||
|
[XmlEnum("arm")]
|
||||||
|
Arm,
|
||||||
|
|
||||||
|
/// <summary>ARM 64-bit.</summary>
|
||||||
|
[XmlEnum("arm64")]
|
||||||
|
Arm64,
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Describes the current phase of the update process, reported via <see cref="Updater.ProgressChanged"/>.
|
/// Describes the current phase of the update process, reported via <see cref="Updater.ProgressChanged"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
Reference in New Issue
Block a user