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
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -178,11 +178,13 @@ namespace AlwaysUpToDate
|
||||
using HttpResponseMessage response = await httpClient.GetAsync(updateInfoUrl);
|
||||
_ = response.EnsureSuccessStatusCode();
|
||||
|
||||
using Stream stream = await response.Content.ReadAsStreamAsync();
|
||||
UpdateManifest manifest = (UpdateManifest)manifestSerializer.Deserialize(stream);
|
||||
string xmlManifest = await response.Content.ReadAsStringAsync();
|
||||
UpdateManifest manifest = (UpdateManifest)manifestSerializer.Deserialize(new StringReader(xmlManifest));
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -246,6 +248,18 @@ namespace AlwaysUpToDate
|
||||
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()
|
||||
{
|
||||
try
|
||||
@@ -255,6 +269,8 @@ namespace AlwaysUpToDate
|
||||
|
||||
UpdateStarted?.Invoke(pendingUpdateItem?.Version);
|
||||
|
||||
TriggerProgressChanged(UpdateStep.Downloading, null, 0);
|
||||
|
||||
using HttpResponseMessage response = await httpClient.GetAsync(updateUrl);
|
||||
_ = response.EnsureSuccessStatusCode();
|
||||
|
||||
@@ -366,6 +382,7 @@ namespace AlwaysUpToDate
|
||||
}
|
||||
|
||||
_ = Process.Start(new ProcessStartInfo(executablePath) { UseShellExecute = false });
|
||||
Environment.Exit(0);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -383,29 +400,31 @@ namespace AlwaysUpToDate
|
||||
byte[] buffer = new byte[8192];
|
||||
bool isMoreToRead = true;
|
||||
|
||||
using FileStream fileStream = new FileStream(Path.Join(installPath, "Update.zip"), FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);
|
||||
do
|
||||
using (FileStream fileStream = new FileStream(Path.Join(installPath, "Update.zip"), FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
|
||||
{
|
||||
int bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
|
||||
if (bytesRead == 0)
|
||||
do
|
||||
{
|
||||
isMoreToRead = false;
|
||||
TriggerProgressChanged(UpdateStep.Downloading, totalDownloadSize, totalBytesRead);
|
||||
continue;
|
||||
}
|
||||
int bytesRead = await contentStream.ReadAsync(buffer, 0, buffer.Length);
|
||||
if (bytesRead == 0)
|
||||
{
|
||||
isMoreToRead = false;
|
||||
TriggerProgressChanged(UpdateStep.Downloading, totalDownloadSize, totalBytesRead);
|
||||
continue;
|
||||
}
|
||||
|
||||
await fileStream.WriteAsync(buffer, 0, bytesRead);
|
||||
await fileStream.WriteAsync(buffer, 0, bytesRead);
|
||||
|
||||
totalBytesRead += bytesRead;
|
||||
readCount += 1;
|
||||
totalBytesRead += bytesRead;
|
||||
readCount += 1;
|
||||
|
||||
if (readCount >= 10)
|
||||
{
|
||||
readCount = 0;
|
||||
TriggerProgressChanged(UpdateStep.Downloading, totalDownloadSize, totalBytesRead);
|
||||
if (readCount >= 10)
|
||||
{
|
||||
readCount = 0;
|
||||
TriggerProgressChanged(UpdateStep.Downloading, totalDownloadSize, totalBytesRead);
|
||||
}
|
||||
}
|
||||
while (isMoreToRead);
|
||||
}
|
||||
while (isMoreToRead);
|
||||
|
||||
ExtractZipFile();
|
||||
}
|
||||
|
||||
@@ -18,14 +18,42 @@ namespace AlwaysUpToDate
|
||||
|
||||
/// <summary>
|
||||
/// 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>
|
||||
public class UpdateItem
|
||||
{
|
||||
/// <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>
|
||||
[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>
|
||||
/// Gets or sets the version string of the update in <c>X.X.X.X</c> format.
|
||||
@@ -57,6 +85,60 @@ namespace AlwaysUpToDate
|
||||
/// </summary>
|
||||
[XmlElement("checksum")]
|
||||
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>
|
||||
@@ -117,6 +199,33 @@ namespace AlwaysUpToDate
|
||||
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>
|
||||
/// Describes the current phase of the update process, reported via <see cref="Updater.ProgressChanged"/>.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user