Skip to content

Commit

Permalink
Merge pull request #94 from dennis/use-autoupdaternet
Browse files Browse the repository at this point in the history
Reimplement application update using AutoUpdaterDotNET
  • Loading branch information
lawrab authored May 22, 2021
2 parents cce5c41 + d1205cd commit 7028c30
Show file tree
Hide file tree
Showing 5 changed files with 133 additions and 77 deletions.
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://api.github.com/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 @@ -509,7 +506,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

0 comments on commit 7028c30

Please sign in to comment.