Files
Blueberry/Blueberry/UpdateManager.cs

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; }
}
}
}