Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement application update using AutoUpdaterDotNET #94

Merged
merged 1 commit into from
May 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 112 additions & 65 deletions Components/AppilcationUpdate/Lua/ApplicationUpdateInstanceThread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

using Slipstream.Shared;
using Slipstream.Shared.Lua;
using Squirrel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AutoUpdaterDotNET;
using System.Reflection;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Net;

namespace Slipstream.Components.AppilcationUpdate.Lua
{
Expand All @@ -16,8 +19,10 @@ public class ApplicationUpdateInstanceThread : BaseInstanceThread, IApplicationU
private readonly IApplicationUpdateEventFactory ApplicationUpdateEventFactory;
private readonly IEventBus EventBus;
private readonly IEventBusSubscription Subscription;
private readonly IApplicationVersionService ApplicationVersionService;
private readonly string UpdateLocation;
private readonly bool Prerelease;
private UpdateInfoEventArgs? LastUpdateInfoEventArgs;

public ApplicationUpdateInstanceThread(
string instanceId,
Expand All @@ -27,116 +32,158 @@ public ApplicationUpdateInstanceThread(
IEventHandlerController eventHandlerController,
IApplicationUpdateEventFactory applicationUpdateEventFactory,
IEventBus eventBus,
IEventBusSubscription subscription) : base(instanceId, logger)
IEventBusSubscription subscription,
IApplicationVersionService applicationVersionService
) : base(instanceId, logger)
{
UpdateLocation = location;
Prerelease = prerelease;
EventHandlerController = eventHandlerController;
ApplicationUpdateEventFactory = applicationUpdateEventFactory;
EventBus = eventBus;
Subscription = subscription;
ApplicationVersionService = applicationVersionService;
}

protected override void Main()
private void AutoUpdaterOnParseUpdateInfoEvent(ParseUpdateInfoEventArgs args)
{
if (Debugger.IsAttached)
// We're expecting a github endpoint, returning a JSON of what is available on the release page.
// see https://github.com/gitapi/repos/dennis/slipstream/releases
// We will always use the latest version and check if that differs from the installed version
// When looking for which asset to download, we'll just download the first .exe file we find.

// AutoUpdaterDotNET also features UI, giving a dialog box that asks user if they want to update,
// skip or remind later. But to utilize this, we need to be doing this from the UI thread.
// It might make sense to move it to WinForm?

if (!(JsonConvert.DeserializeObject(args.RemoteData) is JArray json))
{
Logger.Information("Auto update is disabled when a Debugger is attached");
Logger.Information("Auto update, can't read json returned from update server");
return;
}

Logger.Information($"Auto update, updating from {UpdateLocation}, prerelease: {Prerelease} {Thread.CurrentThread.ManagedThreadId}");
static string GetVersionFromTag(string tag) => tag.Remove(0, tag.IndexOf('v') + 1);

Init();
var latestRelease = json[0];
if (latestRelease == null)
{
Logger.Information("Auto update, didn't find any information for last version");
return;
}

while (!Stopping)
if (!(latestRelease["tag_name"] is JValue newestRelease))
{
IEvent? @event = Subscription.NextEvent(100);
Logger.Information("Auto update, can't read newest version information");
return;
}

if (@event != null)
var newestReleaseStr = newestRelease.Value as string;
var CurrentVersion = GetVersionFromTag(newestReleaseStr!);
var ChangelogUrl = string.Empty;

if (!(latestRelease["assets"] is JArray assets))
{
Logger.Information("Auto update, no assets found");
return;
}

foreach (var asset in assets)
{
if (asset["browser_download_url"] is JValue downloadUrl && downloadUrl.Value is string downloadUrlStr && downloadUrlStr.EndsWith(".exe"))
{
EventHandlerController.HandleEvent(@event);
args.UpdateInfo = new UpdateInfoEventArgs
{
CurrentVersion = CurrentVersion,
ChangelogURL = ChangelogUrl,
DownloadURL = downloadUrlStr
};

break;
}
}
}

private void Init()
protected override void Main()
{
if (string.IsNullOrEmpty(UpdateLocation))
{
Logger.Information("Auto update is disabled, no update location specified");
return;
}

using var updateManager = CreateUpdateManager();
Logger.Information($"Auto update, updating from {UpdateLocation}, prerelease: {Prerelease} {Thread.CurrentThread.ManagedThreadId}");

if (updateManager?.IsInstalledApp == true)
{
Logger.Information($"Installed application: auto update enabled {Thread.CurrentThread.ManagedThreadId}");
AutoUpdater.HttpUserAgent = $"{Assembly.GetExecutingAssembly().GetName().Name} v{ApplicationVersionService.Version}";
AutoUpdater.ParseUpdateInfoEvent += AutoUpdaterOnParseUpdateInfoEvent;
AutoUpdater.CheckForUpdateEvent += AutoUpdaterOnCheckFOrUpdateEvent;
AutoUpdater.PersistenceProvider = new JsonFilePersistenceProvider(Path.Combine(Environment.CurrentDirectory, "autoupdate.json"));

var applicationUpdate = EventHandlerController.Get<EventHandler.ApplicationUpdateEventHandler>();
var applicationUpdate = EventHandlerController.Get<EventHandler.ApplicationUpdateEventHandler>();
applicationUpdate.OnApplicationUpdateCommandCheckLatestVersion += (s, e) => CheckForAppUpdates();
applicationUpdate.OnApplicationUpdateLatestVersionChanged += (s, e) => OnApplicationVersionChanged();

applicationUpdate.OnApplicationUpdateCommandCheckLatestVersion += async (s, e) => await CheckForAppUpdates();
applicationUpdate.OnApplicationUpdateLatestVersionChanged += async (s, e) => await OnApplicationVersionChanged();
while (!Stopping)
{
IEvent? @event = Subscription.NextEvent(100);

// Send update event to check for update at startup
EventBus.PublishEvent(ApplicationUpdateEventFactory.CreateApplicationUpdateCommandCheckLatestVersion());
if (@event != null)
{
EventHandlerController.HandleEvent(@event);
}
}
}

private UpdateManager? CreateUpdateManager()
private void OnApplicationVersionChanged()
{
if (string.IsNullOrEmpty(UpdateLocation))
if (LastUpdateInfoEventArgs == null)
return;

try
{
return null;
if (AutoUpdater.DownloadUpdate(LastUpdateInfoEventArgs))
{
Logger.Information("Auto update: Updated version. Restart to use it");
}
else
{
Logger.Information("Auto update cancelled");
}
}

var isGitHub = UpdateLocation.StartsWith("https://github.com");

if (isGitHub)
catch (Exception exception)
{
var asyncUpdateManager = UpdateManager.GitHubUpdateManager(UpdateLocation, prerelease: Prerelease);
asyncUpdateManager.Wait();
return asyncUpdateManager.Result;
Logger.Error("Auto update error: " + exception.Message);
}

return new UpdateManager(UpdateLocation);
LastUpdateInfoEventArgs = null;
}

private async Task CheckForAppUpdates()
private void AutoUpdaterOnCheckFOrUpdateEvent(UpdateInfoEventArgs args)
{
Logger.Information("Auto update, checking lastest version");
using var updateManager = CreateUpdateManager();

if (updateManager == null)
return;
if (args.Error == null)
{
if (args.IsUpdateAvailable)
{
Logger.Information($"Auto update, new version {args.CurrentVersion} available. You are using version {args.InstalledVersion}.");

var canUpdate = await updateManager.CheckForUpdate();
LastUpdateInfoEventArgs = args;

if (canUpdate.ReleasesToApply.Any())
{
Logger.Information("Auto update, new version available, raising the event");
EventBus.PublishEvent(ApplicationUpdateEventFactory.CreateApplicationUpdateLatestVersionChanged(canUpdate.FutureReleaseEntry.Version.ToString()));
EventBus.PublishEvent(ApplicationUpdateEventFactory.CreateApplicationUpdateLatestVersionChanged(args.CurrentVersion));
}
else
{
Logger.Information("Auto update, no update available");
}
}
else
{
Logger.Information($"Auto update, no update available {Thread.CurrentThread.ManagedThreadId}");
if (args.Error is WebException)
{
Logger.Error("Auto update, can't reach update server");
}
else
{
Logger.Error("Auto update error: " + args.Error.Message);
}
}
}

private async Task OnApplicationVersionChanged()
{
using var updateManager = CreateUpdateManager();
if (updateManager == null)
return;
await DoAppUpdates(updateManager);
}

private async Task DoAppUpdates(UpdateManager updateManager)
private void CheckForAppUpdates()
{
Logger.Information("Auto updating to the latest version");
var releaseInfo = await updateManager.UpdateApp();
Logger.Information($"Auto update, update completed for {releaseInfo.Version}");
AutoUpdater.Start(UpdateLocation);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace Slipstream.Components.AppilcationUpdate.Lua
{
public class ApplicationUpdateLuaLibrary : BaseLuaLibrary<IApplicationUpdateInstanceThread, IApplicationUpdateReference>
public class ApplicationUpdateLuaLibrary : SingletonLuaLibrary<IApplicationUpdateInstanceThread, IApplicationUpdateReference>
{
private static readonly DictionaryValidator ConfigurationValidator;

Expand Down
17 changes: 15 additions & 2 deletions Components/AppilcationUpdate/Lua/ApplicationUpdateReference.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
namespace Slipstream.Components.AppilcationUpdate.Lua
using Slipstream.Shared;

namespace Slipstream.Components.AppilcationUpdate.Lua
{
public class ApplicationUpdateReference : IApplicationUpdateReference
{
private ApplicationUpdateLuaLibrary LuaLibrary { get; }
public string InstanceId { get; }

public ApplicationUpdateReference(ApplicationUpdateLuaLibrary luaLibrary, string instanceId)
private readonly IApplicationUpdateEventFactory ApplicationUpdateEventFactory;
private readonly IEventBus EventBus;

public ApplicationUpdateReference(ApplicationUpdateLuaLibrary luaLibrary, string instanceId, IEventBus eventBus, IApplicationUpdateEventFactory eventFactory)
{
LuaLibrary = luaLibrary;
InstanceId = instanceId;
ApplicationUpdateEventFactory = eventFactory;
EventBus = eventBus;
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "This is expose in Lua, so we want to keep that naming style")]
public void start()
{
EventBus.PublishEvent(ApplicationUpdateEventFactory.CreateApplicationUpdateCommandCheckLatestVersion());
}

public void Dispose()
Expand Down
12 changes: 4 additions & 8 deletions Slipstream.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="packages\squirrel.windows.2.0.1\build\squirrel.windows.props" Condition="Exists('packages\squirrel.windows.2.0.1\build\squirrel.windows.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
Expand Down Expand Up @@ -59,6 +58,9 @@
<Reference Include="Autofac, Version=6.1.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<HintPath>packages\Autofac.6.1.0\lib\netstandard2.0\Autofac.dll</HintPath>
</Reference>
<Reference Include="AutoUpdater.NET, Version=1.6.4.0, Culture=neutral, PublicKeyToken=501435c91b35f4bc, processorArchitecture=MSIL">
<HintPath>packages\Autoupdater.NET.Official.1.6.4\lib\net45\AutoUpdater.NET.dll</HintPath>
</Reference>
<Reference Include="DeltaCompressionDotNet, Version=1.1.0.0, Culture=neutral, PublicKeyToken=1d14d6e5194e7f4a, processorArchitecture=MSIL">
<HintPath>packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.dll</HintPath>
</Reference>
Expand Down Expand Up @@ -122,9 +124,6 @@
<Reference Include="NLua, Version=1.5.7.0, Culture=neutral, PublicKeyToken=6a194c04b9c89217, processorArchitecture=MSIL">
<HintPath>packages\NLua.1.5.7\lib\net46\NLua.dll</HintPath>
</Reference>
<Reference Include="NuGet.Squirrel, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\squirrel.windows.2.0.1\lib\Net45\NuGet.Squirrel.dll</HintPath>
</Reference>
<Reference Include="Serilog, Version=2.0.0.0, Culture=neutral, PublicKeyToken=24c2f752a8e58a10, processorArchitecture=MSIL">
<HintPath>packages\Serilog.2.10.0\lib\net46\Serilog.dll</HintPath>
</Reference>
Expand All @@ -134,9 +133,6 @@
<Reference Include="SharpCompress, Version=0.17.1.0, Culture=neutral, PublicKeyToken=afb0a02973931d96, processorArchitecture=MSIL">
<HintPath>packages\SharpCompress.0.17.1\lib\net45\SharpCompress.dll</HintPath>
</Reference>
<Reference Include="Squirrel, Version=2.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\squirrel.windows.2.0.1\lib\Net45\Squirrel.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
Expand Down Expand Up @@ -173,6 +169,7 @@
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
</Reference>
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
Expand Down Expand Up @@ -497,7 +494,6 @@
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('packages\squirrel.windows.2.0.1\build\squirrel.windows.props')" Text="$([System.String]::Format('$(ErrorText)', 'packages\squirrel.windows.2.0.1\build\squirrel.windows.props'))" />
<Error Condition="!Exists('packages\KeraLua.1.2.13\build\net46\KeraLua.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\KeraLua.1.2.13\build\net46\KeraLua.targets'))" />
</Target>
<Target Name="AfterBuild" Condition=" '$(Configuration)' == 'Release'">
Expand Down
2 changes: 1 addition & 1 deletion packages.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Autofac" version="6.1.0" targetFramework="net472" />
<package id="Autoupdater.NET.Official" version="1.6.4" targetFramework="net472" />
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net472" />
<package id="DSharpPlus" version="3.2.3" targetFramework="net472" />
<package id="KeraLua" version="1.2.13" targetFramework="net472" />
Expand All @@ -20,7 +21,6 @@
<package id="Serilog" version="2.10.0" targetFramework="net472" />
<package id="Serilog.Sinks.Console" version="3.1.1" targetFramework="net472" />
<package id="sharpcompress" version="0.17.1" targetFramework="net472" />
<package id="squirrel.windows" version="2.0.1" targetFramework="net472" />
<package id="System.Buffers" version="4.5.1" targetFramework="net472" />
<package id="System.Diagnostics.DiagnosticSource" version="4.7.1" targetFramework="net472" />
<package id="System.Memory" version="4.5.4" targetFramework="net472" />
Expand Down