121 lines
3.8 KiB
C#
121 lines
3.8 KiB
C#
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Text.Json;
|
|
|
|
namespace Blueberry
|
|
{
|
|
public class UpdateManager
|
|
{
|
|
private const string releaseUrl = "https://git.technopunk.space/api/v1/repos/tomi/Blueberry/releases/latest";
|
|
public const string CurrentVersion = "0.1.1";
|
|
|
|
public async Task CheckAndInstallAsync()
|
|
{
|
|
using var client = new HttpClient();
|
|
|
|
client.DefaultRequestHeaders.Add("User-Agent", "Blueberry-Updater");
|
|
|
|
try
|
|
{
|
|
var json = client.GetStringAsync(releaseUrl).ConfigureAwait(false).GetAwaiter().GetResult();
|
|
var release = JsonSerializer.Deserialize<Root>(json);
|
|
|
|
if (release == null)
|
|
throw new NullReferenceException();
|
|
|
|
if (release.tag_name != CurrentVersion)
|
|
{
|
|
var file = release.assets.Find(x => x.name.Contains(".zip")) ?? throw new NullReferenceException();
|
|
string downloadUrl = file.browser_download_url;
|
|
await PerformUpdate(client, downloadUrl);
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
|
|
}
|
|
}
|
|
|
|
private async Task PerformUpdate(HttpClient client, string url)
|
|
{
|
|
string tempZip = Path.Combine(Path.GetTempPath(), "blueberry_update.zip");
|
|
string currentExe = Process.GetCurrentProcess().MainModule.FileName;
|
|
string appDir = AppDomain.CurrentDomain.BaseDirectory;
|
|
|
|
// 1. Download
|
|
var data = await client.GetByteArrayAsync(url);
|
|
File.WriteAllBytes(tempZip, data);
|
|
|
|
// 2. Create a temporary batch script to handle the swap
|
|
// We use a small delay (timeout) to allow the main app to close fully
|
|
string psScript = $@"
|
|
# Wait for the main app to close completely
|
|
Start-Sleep -Seconds 2
|
|
|
|
$exePath = '{currentExe}'
|
|
$zipPath = '{tempZip}'
|
|
$destDir = '{appDir}'
|
|
|
|
# Retry logic for deletion (in case antivirus or OS holds the lock)
|
|
$maxRetries = 10
|
|
$retryCount = 0
|
|
|
|
while ($retryCount -lt $maxRetries) {{
|
|
try {{
|
|
# Attempt to delete the old executable
|
|
if (Test-Path $exePath) {{ Remove-Item $exePath -Force -ErrorAction Stop }}
|
|
break # If successful, exit loop
|
|
}}
|
|
catch {{
|
|
Start-Sleep -Milliseconds 500
|
|
$retryCount++
|
|
}}
|
|
}}
|
|
|
|
# Unzip the new version
|
|
Expand-Archive -Path $zipPath -DestinationPath $destDir -Force
|
|
|
|
# CLEANUP: Delete the zip
|
|
Remove-Item $zipPath -Force
|
|
|
|
# RESTART: Launch the new executable
|
|
# 'Start-Process' is the robust way to launch detached processes in PS
|
|
Start-Process -FilePath $exePath -WorkingDirectory $destDir
|
|
|
|
# SELF-DESTRUCT: Remove this script
|
|
Remove-Item -LiteralPath $MyInvocation.MyCommand.Path -Force
|
|
";
|
|
string psPath = Path.Combine(Path.GetTempPath(), "blueberry_updater.ps1");
|
|
File.WriteAllText(psPath, psScript);
|
|
|
|
// 3. Execute the PowerShell script hidden
|
|
var startInfo = new ProcessStartInfo()
|
|
{
|
|
FileName = "powershell.exe",
|
|
Arguments = $"-NoProfile -ExecutionPolicy Bypass -File \"{psPath}\"",
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true,
|
|
WindowStyle = ProcessWindowStyle.Hidden
|
|
};
|
|
|
|
Process.Start(startInfo);
|
|
|
|
// 4. Kill the current app immediately so the script can delete it
|
|
System.Windows.Application.Current.Shutdown();
|
|
}
|
|
|
|
public class Root
|
|
{
|
|
public string tag_name { get; set; }
|
|
public List<Asset> assets { get; set; }
|
|
}
|
|
|
|
public class Asset
|
|
{
|
|
public string name { get; set; }
|
|
public string browser_download_url { get; set; }
|
|
}
|
|
}
|
|
}
|