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

PoC Auto-Update with Netsparkle Updater #2998

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
137 changes: 69 additions & 68 deletions ILSpy/AboutPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,74 +218,6 @@ static async Task<AvailableVersionInfo> GetLatestVersionAsync()
return latestAvailableVersion;
}

sealed class AvailableVersionInfo
{
public Version Version;
public string DownloadUrl;
}

sealed class UpdateSettings : INotifyPropertyChanged
{
public UpdateSettings(ILSpySettings spySettings)
{
XElement s = spySettings["UpdateSettings"];
this.automaticUpdateCheckEnabled = (bool?)s.Element("AutomaticUpdateCheckEnabled") ?? true;
try
{
this.lastSuccessfulUpdateCheck = (DateTime?)s.Element("LastSuccessfulUpdateCheck");
}
catch (FormatException)
{
// avoid crashing on settings files invalid due to
// https://github.com/icsharpcode/ILSpy/issues/closed/#issue/2
}
}

bool automaticUpdateCheckEnabled;

public bool AutomaticUpdateCheckEnabled {
get { return automaticUpdateCheckEnabled; }
set {
if (automaticUpdateCheckEnabled != value)
{
automaticUpdateCheckEnabled = value;
Save();
OnPropertyChanged(nameof(AutomaticUpdateCheckEnabled));
}
}
}

DateTime? lastSuccessfulUpdateCheck;

public DateTime? LastSuccessfulUpdateCheck {
get { return lastSuccessfulUpdateCheck; }
set {
if (lastSuccessfulUpdateCheck != value)
{
lastSuccessfulUpdateCheck = value;
Save();
OnPropertyChanged(nameof(LastSuccessfulUpdateCheck));
}
}
}

public void Save()
{
XElement updateSettings = new XElement("UpdateSettings");
updateSettings.Add(new XElement("AutomaticUpdateCheckEnabled", automaticUpdateCheckEnabled));
if (lastSuccessfulUpdateCheck != null)
updateSettings.Add(new XElement("LastSuccessfulUpdateCheck", lastSuccessfulUpdateCheck));
ILSpySettings.SaveSettings(updateSettings);
}

public event PropertyChangedEventHandler PropertyChanged;

void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

/// <summary>
/// If automatic update checking is enabled, checks if there are any updates available.
/// Returns the download URL if an update is available.
Expand Down Expand Up @@ -349,4 +281,73 @@ public interface IAboutPageAddition
{
void Write(ISmartTextOutput textOutput);
}

sealed class AvailableVersionInfo
{
public Version Version;
public string DownloadUrl;
}

sealed class UpdateSettings : INotifyPropertyChanged
{
public UpdateSettings(ILSpySettings spySettings)
{
XElement s = spySettings["UpdateSettings"];
this.automaticUpdateCheckEnabled = (bool?)s.Element("AutomaticUpdateCheckEnabled") ?? true;
try
{
this.lastSuccessfulUpdateCheck = (DateTime?)s.Element("LastSuccessfulUpdateCheck");
}
catch (FormatException)
{
// avoid crashing on settings files invalid due to
// https://github.com/icsharpcode/ILSpy/issues/closed/#issue/2
}
}

bool automaticUpdateCheckEnabled;

public bool AutomaticUpdateCheckEnabled {
get { return automaticUpdateCheckEnabled; }
set {
if (automaticUpdateCheckEnabled != value)
{
automaticUpdateCheckEnabled = value;
Save();
OnPropertyChanged(nameof(AutomaticUpdateCheckEnabled));
}
}
}

DateTime? lastSuccessfulUpdateCheck;

public DateTime? LastSuccessfulUpdateCheck {
get { return lastSuccessfulUpdateCheck; }
set {
if (lastSuccessfulUpdateCheck != value)
{
lastSuccessfulUpdateCheck = value;
Save();
OnPropertyChanged(nameof(LastSuccessfulUpdateCheck));
}
}
}

public void Save()
{
XElement updateSettings = new XElement("UpdateSettings");
updateSettings.Add(new XElement("AutomaticUpdateCheckEnabled", automaticUpdateCheckEnabled));
if (lastSuccessfulUpdateCheck != null)
updateSettings.Add(new XElement("LastSuccessfulUpdateCheck", lastSuccessfulUpdateCheck));
ILSpySettings.SaveSettings(updateSettings);
}

public event PropertyChangedEventHandler PropertyChanged;

void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

}
1 change: 1 addition & 0 deletions ILSpy/ILSpy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<PackageReference Include="Microsoft.VisualStudio.Composition" Version="17.4.16" />
<PackageReference Include="DataGridExtensions" Version="2.5.14" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="NetSparkleUpdater.UI.WPF" Version="2.3.0-preview20230606002" />
<PackageReference Include="TomsToolbox.Wpf.Styles" Version="$(WpfStylesToolboxVersion)" />
</ItemGroup>

Expand Down
72 changes: 72 additions & 0 deletions ILSpy/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
Expand Down Expand Up @@ -57,6 +58,12 @@

using Microsoft.Win32;

using NetSparkleUpdater;
using NetSparkleUpdater.Configurations;
using NetSparkleUpdater.Enums;
using NetSparkleUpdater.SignatureVerifiers;
using NetSparkleUpdater.UI.WPF;

namespace ICSharpCode.ILSpy
{
/// <summary>
Expand All @@ -72,6 +79,8 @@ partial class MainWindow : Window
AssemblyList assemblyList;
AssemblyListTreeNode assemblyListTreeNode;

SparkleUpdater _sparkle;

static MainWindow instance;

public static MainWindow Instance {
Expand Down Expand Up @@ -895,6 +904,66 @@ void MainWindow_Loaded(object sender, RoutedEventArgs e)
}

Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => OpenAssemblies(spySettings)));


var s = new UpdateSettings(spySettings);
bool automaticCheckingEnabled = s.AutomaticUpdateCheckEnabled;

// TODO: That would need to be way more sophisticated - we ship zip, msi, vsix (actually doing an update-check there is wrong even today)
// Multiple of those could be installed on the same machine in multiple versions (esp. zip)
// Somehow we'd need to "tag" the version running from our msi installer, and only then offering auto-update (eg MsiInstaller.txt marker file)
// And then have an IUpdateService with one impl existing plus one for auto-updating

#if DEBUG
// automaticCheckingEnabled = false;
#endif
// TODO: Additional check if branch version

if (automaticCheckingEnabled)
EnableSparkleUpdateChecking();
}

void EnableSparkleUpdateChecking()
{
// We'd need two appcast.xml's, one for x64 and one for arm64
if (RuntimeInformation.ProcessArchitecture != Architecture.X64)
return;

// This should update with UpdateTheme
bool isLightTheme = ThemeManager.Current.Theme.Contains("light", StringComparison.InvariantCultureIgnoreCase);

var extraHeadAdditionForReleaseNotes = "<style>";
if (!isLightTheme)
{
extraHeadAdditionForReleaseNotes +=
"body {background-color: #212121; } " +
"h1, h2, li { color: #e8e8e8; } ";
}
extraHeadAdditionForReleaseNotes +=
"h1, h2 { margin-top: -8px; margin-left: 6px; } ";
extraHeadAdditionForReleaseNotes += "</style>";

// https://github.com/NetSparkleUpdater/NetSparkle/blob/develop/src/NetSparkle.Samples.NetCore.WPF/MainWindow.xaml.cs
string sparkleSettingsPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "ICSharpCode\\ILSpy-sparkle.json");

_sparkle = new SparkleUpdater(
"https://ilspy.net/appcast.xml",
new Ed25519Checker(SecurityMode.Strict, "s2P6MPexSRSjod77aWUjgVKj/gKYYAeqgHY/0Gf8b78=")
) {
UIFactory = new UIFactory(Images.ILSpyIcon) {
UseStaticUpdateWindowBackgroundColor = false,
AdditionalReleaseNotesHeaderHTML = extraHeadAdditionForReleaseNotes,
ProcessWindowAfterInit = (w, f) => {
w.Style = (Style)Application.Current.FindResource("DialogWindow");
}
},
ShowsUIOnMainThread = true,
RelaunchAfterUpdate = false,
CustomInstallerArguments = "",
Configuration = new JSONConfiguration(new NetSparkleUpdater.AssemblyAccessors.AssemblyReflectionAccessor(null), sparkleSettingsPath)
};

_sparkle.StartLoop(true);
}

void OpenAssemblies(ILSpySettings spySettings)
Expand Down Expand Up @@ -951,6 +1020,9 @@ internal static bool FormatExceptions(App.ExceptionData[] exceptions, StringBuil

public async Task ShowMessageIfUpdatesAvailableAsync(ILSpySettings spySettings, bool forceCheck = false)
{
// TODO: Properly replace old code-paths, this is for UI testing of Netsparkle only
var info = await _sparkle.CheckForUpdatesAtUserRequest();

string downloadUrl;
if (forceCheck)
{
Expand Down