Flow.Launcher/Flow.Launcher.Core/Updater.cs

170 lines
6.5 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Net;
2016-05-10 18:24:15 +00:00
using System.Net.Http;
using System.Net.Sockets;
using System.Linq;
using System.Text.Json;
2025-04-03 14:25:04 +00:00
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
2017-03-06 00:25:17 +00:00
using System.Windows;
2020-04-21 09:12:17 +00:00
using Flow.Launcher.Plugin.SharedCommands;
using Flow.Launcher.Infrastructure;
using Flow.Launcher.Infrastructure.Http;
using Flow.Launcher.Infrastructure.UserSettings;
using Flow.Launcher.Plugin;
2025-04-03 14:25:04 +00:00
using JetBrains.Annotations;
using Squirrel;
2020-04-21 09:12:17 +00:00
namespace Flow.Launcher.Core
{
public class Updater
{
public string GitHubRepository { get; init; }
2025-04-13 09:26:21 +00:00
private static readonly string ClassName = nameof(Updater);
2025-02-23 11:48:38 +00:00
private readonly IPublicAPI _api;
public Updater(IPublicAPI publicAPI, string gitHubRepository)
2025-02-23 05:27:39 +00:00
{
2025-02-23 11:48:38 +00:00
_api = publicAPI;
GitHubRepository = gitHubRepository;
}
private SemaphoreSlim UpdateLock { get; } = new SemaphoreSlim(1);
public async Task UpdateAppAsync(bool silentUpdate = true)
{
await UpdateLock.WaitAsync().ConfigureAwait(false);
try
{
if (!silentUpdate)
2025-02-23 11:48:38 +00:00
_api.ShowMsg(_api.GetTranslation("pleaseWait"),
_api.GetTranslation("update_flowlauncher_update_check"));
using var updateManager = await GitHubUpdateManagerAsync(GitHubRepository).ConfigureAwait(false);
2017-03-26 23:08:56 +00:00
// UpdateApp CheckForUpdate will return value only if the app is squirrel installed
var newUpdateInfo = await updateManager.CheckForUpdate().NonNull().ConfigureAwait(false);
var newReleaseVersion = Version.Parse(newUpdateInfo.FutureReleaseEntry.Version.ToString());
var currentVersion = Version.Parse(Constant.Version);
2025-04-13 09:26:21 +00:00
_api.LogInfo(ClassName, $"Future Release <{Formatted(newUpdateInfo.FutureReleaseEntry)}>");
if (newReleaseVersion <= currentVersion)
{
if (!silentUpdate)
2025-02-23 11:48:38 +00:00
_api.ShowMsgBox(_api.GetTranslation("update_flowlauncher_already_on_latest"));
return;
}
if (!silentUpdate)
2025-02-23 11:48:38 +00:00
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_update_found"),
_api.GetTranslation("update_flowlauncher_updating"));
2020-12-31 05:06:31 +00:00
await updateManager.DownloadReleases(newUpdateInfo.ReleasesToApply).ConfigureAwait(false);
2020-12-31 05:06:31 +00:00
await updateManager.ApplyReleases(newUpdateInfo).ConfigureAwait(false);
if (DataLocation.PortableDataLocationInUse())
{
2025-04-03 14:25:04 +00:00
var targetDestination = updateManager.RootAppDirectory + $"\\app-{newReleaseVersion}\\{DataLocation.PortableFolderName}";
2025-02-23 11:48:38 +00:00
FilesFolders.CopyAll(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s));
if (!FilesFolders.VerifyBothFolderFilesEqual(DataLocation.PortableDataPath, targetDestination, (s) => _api.ShowMsgBox(s)))
_api.ShowMsgBox(string.Format(_api.GetTranslation("update_flowlauncher_fail_moving_portable_user_profile_data"),
DataLocation.PortableDataPath,
targetDestination));
}
else
{
2020-12-31 05:06:31 +00:00
await updateManager.CreateUninstallerRegistryEntry().ConfigureAwait(false);
}
var newVersionTips = NewVersionTips(newReleaseVersion.ToString());
2025-04-13 09:26:21 +00:00
_api.LogInfo(ClassName, $"Update success:{newVersionTips}");
2025-02-23 11:48:38 +00:00
if (_api.ShowMsgBox(newVersionTips, _api.GetTranslation("update_flowlauncher_new_update"), MessageBoxButton.YesNo) == MessageBoxResult.Yes)
{
UpdateManager.RestartApp(Constant.ApplicationFileName);
}
}
catch (Exception e)
{
if (e is HttpRequestException or WebException or SocketException || e.InnerException is TimeoutException)
{
2025-04-13 09:26:21 +00:00
_api.LogException(ClassName, $"Check your connection and proxy settings to github-cloud.s3.amazonaws.com.", e);
}
else
{
2025-04-13 09:26:21 +00:00
_api.LogException(ClassName, $"Error Occurred", e);
}
if (!silentUpdate)
2025-02-23 11:48:38 +00:00
_api.ShowMsg(_api.GetTranslation("update_flowlauncher_fail"),
_api.GetTranslation("update_flowlauncher_check_connection"));
}
finally
{
UpdateLock.Release();
}
2017-03-06 01:30:47 +00:00
}
2017-03-07 19:37:38 +00:00
[UsedImplicitly]
private class GithubRelease
{
2020-12-29 10:33:53 +00:00
[JsonPropertyName("prerelease")]
2017-03-26 23:08:56 +00:00
public bool Prerelease { get; [UsedImplicitly] set; }
2020-12-29 10:33:53 +00:00
[JsonPropertyName("published_at")]
2017-03-26 23:08:56 +00:00
public DateTime PublishedAt { get; [UsedImplicitly] set; }
2020-12-29 10:33:53 +00:00
[JsonPropertyName("html_url")]
2017-03-26 23:08:56 +00:00
public string HtmlUrl { get; [UsedImplicitly] set; }
}
2022-12-30 13:04:56 +00:00
// https://github.com/Squirrel/Squirrel.Windows/blob/master/src/Squirrel/UpdateManager.Factory.cs
2025-04-03 14:25:04 +00:00
private static async Task<UpdateManager> GitHubUpdateManagerAsync(string repository)
{
var uri = new Uri(repository);
2017-03-26 23:08:56 +00:00
var api = $"https://api.github.com/repos{uri.AbsolutePath}/releases";
await using var jsonStream = await Http.GetStreamAsync(api).ConfigureAwait(false);
2025-04-09 13:19:09 +00:00
var releases = await JsonSerializer.DeserializeAsync<List<GithubRelease>>(jsonStream).ConfigureAwait(false);
2017-03-26 23:08:56 +00:00
var latest = releases.Where(r => !r.Prerelease).OrderByDescending(r => r.PublishedAt).First();
var latestUrl = latest.HtmlUrl.Replace("/tag/", "/download/");
var client = new WebClient
{
Proxy = Http.WebProxy
};
var downloader = new FileDownloader(client);
var manager = new UpdateManager(latestUrl, urlDownloader: downloader);
return manager;
}
2017-03-06 00:25:17 +00:00
2025-04-09 13:19:09 +00:00
private string NewVersionTips(string version)
2017-03-06 00:25:17 +00:00
{
2025-04-09 13:19:09 +00:00
var tips = string.Format(_api.GetTranslation("newVersionTips"), version);
2017-03-06 00:25:17 +00:00
return tips;
}
2025-04-02 11:30:15 +00:00
private static string Formatted<T>(T t)
{
var formatted = JsonSerializer.Serialize(t, new JsonSerializerOptions
{
WriteIndented = true
});
return formatted;
}
}
}