Skip to content

Commit

Permalink
Significantly simplify UpdaterPlugin (#973)
Browse files Browse the repository at this point in the history
The updater **no longer downloads or installs updates**. It only notifies the user of an update being available. This was done to avoid having to show a changelog[^1].

Other changes:

- When the user has explicitly checked for an update, the updater will notify the user whether an update is available or not.
- Whenever a release is skipped by the user, this is now saved immediately. Previously, this was only saved when the user closed Whim.
- Whim was previously unaware of its current version. This was because it was inspecting the incorrect attribute from the assembly. This has been fixed.

[^1]: WinUI 3 does not have native Markdown support in WinUI 3, nor is there a CommunityToolkit control. The prior implementation used a WebView2 to display the changelog. This was removed as the containing window would sometimes hide the non-WebView2 controls.
  • Loading branch information
dalyIsaac authored Aug 14, 2024
1 parent 4fc49a8 commit 0487484
Show file tree
Hide file tree
Showing 26 changed files with 517 additions and 1,158 deletions.
1 change: 0 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="DotNext" Version="5.11.0" />
<PackageVersion Include="Markdig" Version="0.37.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Scripting" Version="4.10.0" />
<PackageVersion Include="Microsoft.Win32.SystemEvents" Version="8.0.0" />
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.3.106" />
Expand Down
7 changes: 5 additions & 2 deletions docs/docs/plugins/updater.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Updater Plugin

The <xref:Whim.Updater.UpdaterPlugin> plugin is in `alpha` (especially as Whim hasn't started releasing non-`alpha` builds). If the updater fails, you can manually update Whim by downloading the latest release from the [releases page](https://github.com/dalyIsaac/Whim/releases).

The updater will show a notification when a new version is available. Clicking on the notification will show the changelog for the delta between the current version and the latest version.
When an update is available, the notification will provide options to:

- Open the `Releases` page on GitHub in the default browser.
- Skip the version (i.e., do not show the notification for this version again).
- Defer the update (i.e., show the notification again when the check is run next).

The <xref:Whim.Updater.UpdaterConfig> supports specifying the <xref:Whim.Updater.ReleaseChannel> and <xref:Whim.Updater.UpdateFrequency>.

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Whim is a hackable, pluggable and scriptable dynamic window manager for Windows

## Installation

Alpha builds are available on the [releases page](https://github.com/dalyIsaac/Whim/releases) on GitHub. Whim has an [updater plugin](docs/plugins/updater.md) to keep you up to date.
Alpha builds are available on the [releases page](https://github.com/dalyIsaac/Whim/releases) on GitHub. Whim has an [updater plugin](docs/plugins/updater.md) to notify you of new releases.

Installation via package managers is coming in [dalyIsaac/Whim#792](https://github.com/dalyIsaac/Whim/issues/792).

Expand Down
18 changes: 18 additions & 0 deletions src/Whim.Tests/Store/Root/SavedStateTransformTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Whim.Tests;

public class SavedStateTransformTests
{
[Theory, AutoSubstituteData<StoreCustomization>]
internal void Execute(IContext ctx, IInternalContext internalCtx, MutableRootSector rootSector)
{
// Given
SaveStateTransform sut = new();

// When
Result<Unit> result = sut.Execute(ctx, internalCtx, rootSector);

// Then
Assert.True(result.IsSuccessful);
ctx.PluginManager.Received(1).SaveState();
}
}
File renamed without changes.
168 changes: 168 additions & 0 deletions src/Whim.Updater.Tests/ReleaseManagerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using System.Diagnostics.CodeAnalysis;
using FluentAssertions;
using Microsoft.UI.Dispatching;
using NSubstitute;
using Octokit;
using Whim.TestUtils;
using Xunit;

namespace Whim.Updater.Tests;

[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope")]
public class ReleaseManagerTests
{
#region CheckForUpdates
[Theory, AutoSubstituteData<UpdaterPluginCustomization>]
public async Task CheckForUpdates_WhenNoReleases(IContext ctx, IGitHubClient client)
{
// Given
UpdaterPlugin plugin = new(ctx, new UpdaterConfig());
ReleaseManager sut = new(ctx, plugin) { GitHubClient = client };

// When
await sut.CheckForUpdates(false);

// Then
ctx.NativeManager.DidNotReceive().TryEnqueue(Arg.Any<DispatcherQueueHandler>());
ctx.Store.Received(1).Dispatch(Arg.Any<SaveStateTransform>());
Assert.NotNull(plugin.LastCheckedForUpdates);
}

[Theory, AutoSubstituteData<UpdaterPluginCustomization>]
public async Task CheckForUpdates_ReleaseIsSkipped(IContext ctx, IGitHubClient client)
{
// Given
Release release = Data.CreateRelease242(tagName: "v0.1.265-alpha+bc5c56c4");
client.Repository.Release.GetAll("dalyIsaac", "Whim", Arg.Any<ApiOptions>()).Returns([release]);

UpdaterPlugin plugin = new(ctx, new UpdaterConfig() { ReleaseChannel = ReleaseChannel.Alpha });
plugin.SkipRelease(release.TagName);
ctx.Store.ClearReceivedCalls();

ReleaseManager sut = new(ctx, plugin) { GitHubClient = client };

// When
await sut.CheckForUpdates(false);

// Then
ctx.NativeManager.DidNotReceive().TryEnqueue(Arg.Any<DispatcherQueueHandler>());
ctx.Store.Received(1).Dispatch(Arg.Any<SaveStateTransform>());
Assert.NotNull(plugin.LastCheckedForUpdates);
}

[Theory, AutoSubstituteData<UpdaterPluginCustomization>]
public async Task CheckForUpdates_NewRelease(IContext ctx, IGitHubClient client)
{
// Given
Release release = Data.CreateRelease242(tagName: "v0.1.265-alpha+bc5c56c4");
client.Repository.Release.GetAll("dalyIsaac", "Whim", Arg.Any<ApiOptions>()).Returns([release]);

UpdaterPlugin plugin = new(ctx, new UpdaterConfig() { ReleaseChannel = ReleaseChannel.Alpha });
ReleaseManager sut = new(ctx, plugin) { GitHubClient = client };

// When
await sut.CheckForUpdates(false);

// Then
ctx.NativeManager.Received(1).TryEnqueue(Arg.Any<DispatcherQueueHandler>());
ctx.Store.Received(1).Dispatch(Arg.Any<SaveStateTransform>());
Assert.NotNull(plugin.LastCheckedForUpdates);
}
#endregion

#region GetNotInstalledReleases
[Theory, AutoSubstituteData<UpdaterPluginCustomization>]
public async Task GetNotInstalledReleases_WhenNoReleases(IContext ctx, IGitHubClient client)
{
// Given
UpdaterPlugin plugin = new(ctx, new UpdaterConfig());
ReleaseManager sut = new(ctx, plugin) { GitHubClient = client };

// When
IEnumerable<ReleaseInfo> releases = await sut.GetNotInstalledReleases();

// Then
Assert.Empty(releases);
}

[Theory, AutoSubstituteData<UpdaterPluginCustomization>]
public async Task GetNotInstalledReleases_InvalidVersion(IContext ctx, IGitHubClient client)
{
// Given
UpdaterPlugin plugin = new(ctx, new UpdaterConfig());
ReleaseManager sut = new(ctx, plugin) { GitHubClient = client };
client
.Repository.Release.GetAll("dalyIsaac", "Whim", Arg.Any<ApiOptions>())
.Returns([Data.CreateRelease242(tagName: "welp")]);

// When
IEnumerable<ReleaseInfo> releases = await sut.GetNotInstalledReleases();

// Then
Assert.Empty(releases);
}

[Theory, AutoSubstituteData<UpdaterPluginCustomization>]
public async Task GetNotInstalledReleases_DifferentChannel(IContext ctx, IGitHubClient client)
{
// Given
UpdaterPlugin plugin = new(ctx, new UpdaterConfig());
ReleaseManager sut = new(ctx, plugin) { GitHubClient = client };
client
.Repository.Release.GetAll("dalyIsaac", "Whim", Arg.Any<ApiOptions>())
.Returns([Data.CreateRelease242(tagName: "v0.1.263-beta+bc5c56c4")]);

// When
IEnumerable<ReleaseInfo> releases = await sut.GetNotInstalledReleases();

// Then
Assert.Empty(releases);
}

[Theory, AutoSubstituteData<UpdaterPluginCustomization>]
public async Task GetNotInstalledReleases_OlderVersion(IContext ctx, IGitHubClient client)
{
// Given
UpdaterPlugin plugin = new(ctx, new UpdaterConfig());
ReleaseManager sut = new(ctx, plugin) { GitHubClient = client };
client
.Repository.Release.GetAll("dalyIsaac", "Whim", Arg.Any<ApiOptions>())
.Returns([Data.CreateRelease242(tagName: "v0.1.261-alpha+bc5c56c4")]);

// When
IEnumerable<ReleaseInfo> releases = await sut.GetNotInstalledReleases();

// Then
Assert.Empty(releases);
}

[Theory, AutoSubstituteData<UpdaterPluginCustomization>]
public async Task GetNotInstalledReleases_Ordered(IContext ctx, IGitHubClient client)
{
// Given
UpdaterPlugin plugin = new(ctx, new UpdaterConfig() { ReleaseChannel = ReleaseChannel.Alpha });
ReleaseManager sut = new(ctx, plugin) { GitHubClient = client };

string[] orderedReleases =
[
"v0.1.261-alpha+bc5c56c4",
"v0.1.262-beta+bc5c56c4",
"v0.1.263-stable+bc5c56c4",
"v0.1.264-alpha+bc5c56c4",
"v0.2.265-alpha+bc5c56c4",
"v1.1.266-alpha+bc5c56c4"
];
string[] expectedReleases = ["v0.1.264-alpha+bc5c56c4", "v0.2.265-alpha+bc5c56c4", "v1.1.266-alpha+bc5c56c4"];

client
.Repository.Release.GetAll("dalyIsaac", "Whim", Arg.Any<ApiOptions>())
.Returns(orderedReleases.Select(t => Data.CreateRelease242(tagName: t)).ToArray());

// When
IEnumerable<ReleaseInfo> releases = await sut.GetNotInstalledReleases();

// Then
releases.Select(r => r.Release.TagName).Should().BeEquivalentTo(expectedReleases);
}
#endregion
}
Loading

0 comments on commit 0487484

Please sign in to comment.