diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 4b9a5ee..bd2cea0 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -64,6 +64,22 @@ jobs:
dotnet publish src/PlanViewer.App/PlanViewer.App.csproj -c Release -r osx-x64 --self-contained -o publish/osx-x64
dotnet publish src/PlanViewer.App/PlanViewer.App.csproj -c Release -r osx-arm64 --self-contained -o publish/osx-arm64
+ - name: Create Velopack release (Windows)
+ if: steps.check.outputs.EXISTS == 'false'
+ shell: pwsh
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ VERSION: ${{ steps.version.outputs.VERSION }}
+ run: |
+ dotnet tool install -g vpk
+ New-Item -ItemType Directory -Force -Path releases/velopack
+
+ # Download previous release for delta generation
+ vpk download github --repoUrl https://github.com/${{ github.repository }} --channel win -o releases/velopack
+
+ # Pack Windows release
+ vpk pack -u PerformanceStudio -v $env:VERSION -p publish/win-x64 -e PlanViewer.App.exe -o releases/velopack --channel win
+
- name: Package and upload
if: steps.check.outputs.EXISTS == 'false'
shell: pwsh
@@ -116,7 +132,7 @@ jobs:
Compress-Archive -Path "$wrapperDir/*" -DestinationPath "releases/PerformanceStudio-$rid.zip" -Force
}
- # Checksums
+ # Checksums (zips only, Velopack has its own checksums)
$checksums = Get-ChildItem releases/*.zip | ForEach-Object {
$hash = (Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLower()
"$hash $($_.Name)"
@@ -125,5 +141,8 @@ jobs:
Write-Host "Checksums:"
$checksums | ForEach-Object { Write-Host $_ }
- # Upload
+ # Upload zips + checksums
gh release upload "v$env:VERSION" releases/*.zip releases/SHA256SUMS.txt --clobber
+
+ # Upload Velopack artifacts
+ vpk upload github --repoUrl https://github.com/${{ github.repository }} --channel win -o releases/velopack --releaseName "v$env:VERSION" --tag "v$env:VERSION" --merge
diff --git a/src/PlanViewer.App/AboutWindow.axaml.cs b/src/PlanViewer.App/AboutWindow.axaml.cs
index 9ed0f3f..934d8d8 100644
--- a/src/PlanViewer.App/AboutWindow.axaml.cs
+++ b/src/PlanViewer.App/AboutWindow.axaml.cs
@@ -16,6 +16,7 @@
using Avalonia.Interactivity;
using PlanViewer.App.Mcp;
using PlanViewer.App.Services;
+using Velopack;
namespace PlanViewer.App;
@@ -74,6 +75,8 @@ private async void CopyMcpCommand_Click(object? sender, RoutedEventArgs e)
}
private string? _updateUrl;
+ private UpdateManager? _velopackMgr;
+ private UpdateInfo? _velopackUpdate;
private async void CheckUpdate_Click(object? sender, RoutedEventArgs e)
{
@@ -81,6 +84,32 @@ private async void CheckUpdate_Click(object? sender, RoutedEventArgs e)
UpdateStatusText.Text = "Checking...";
UpdateLink.IsVisible = false;
+ // Try Velopack first (Windows only, supports download + apply)
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ try
+ {
+ _velopackMgr = new UpdateManager(
+ new Velopack.Sources.GithubSource(
+ "https://github.com/erikdarlingdata/PerformanceStudio", null, false));
+
+ _velopackUpdate = await _velopackMgr.CheckForUpdatesAsync();
+ if (_velopackUpdate != null)
+ {
+ UpdateStatusText.Text = "Update available:";
+ UpdateLink.Text = $"v{_velopackUpdate.TargetFullRelease.Version} — click to download and install";
+ UpdateLink.IsVisible = true;
+ CheckUpdateButton.IsEnabled = true;
+ return;
+ }
+ }
+ catch
+ {
+ // Velopack packages may not exist yet — fall through
+ }
+ }
+
+ // Fallback: GitHub API check (opens browser)
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0);
var result = await UpdateChecker.CheckAsync(currentVersion);
@@ -103,8 +132,30 @@ private async void CheckUpdate_Click(object? sender, RoutedEventArgs e)
CheckUpdateButton.IsEnabled = true;
}
- private void UpdateLink_Click(object? sender, PointerPressedEventArgs e)
+ private async void UpdateLink_Click(object? sender, PointerPressedEventArgs e)
{
+ // Velopack download + apply
+ if (_velopackMgr != null && _velopackUpdate != null)
+ {
+ try
+ {
+ UpdateLink.IsVisible = false;
+ UpdateStatusText.Text = "Downloading update...";
+
+ await _velopackMgr.DownloadUpdatesAsync(_velopackUpdate);
+
+ UpdateStatusText.Text = "Update downloaded — restarting...";
+ _velopackMgr.ApplyUpdatesAndRestart(_velopackUpdate.TargetFullRelease);
+ }
+ catch (Exception ex)
+ {
+ UpdateStatusText.Text = $"Update failed: {ex.Message}";
+ UpdateLink.IsVisible = false;
+ }
+ return;
+ }
+
+ // Fallback: open browser
if (_updateUrl != null) OpenUrl(_updateUrl);
}
diff --git a/src/PlanViewer.App/PlanViewer.App.csproj b/src/PlanViewer.App/PlanViewer.App.csproj
index 2a1bc48..9220722 100644
--- a/src/PlanViewer.App/PlanViewer.App.csproj
+++ b/src/PlanViewer.App/PlanViewer.App.csproj
@@ -6,7 +6,7 @@
app.manifest
EDD.ico
true
- 1.2.0
+ 1.2.1
Erik Darling
Darling Data LLC
Performance Studio
@@ -32,6 +32,7 @@
+
diff --git a/src/PlanViewer.App/Program.cs b/src/PlanViewer.App/Program.cs
index 5e569b4..a7d2132 100644
--- a/src/PlanViewer.App/Program.cs
+++ b/src/PlanViewer.App/Program.cs
@@ -3,6 +3,7 @@
using System.IO;
using System.IO.Pipes;
using System.Threading;
+using Velopack;
namespace PlanViewer.App;
@@ -14,6 +15,8 @@ class Program
[STAThread]
public static void Main(string[] args)
{
+ VelopackApp.Build().Run();
+
// If another instance is running, send the file path to it and exit
if (args.Length > 0 && TrySendToRunningInstance(args[0]))
return;
diff --git a/src/PlanViewer.Cli/PlanViewer.Cli.csproj b/src/PlanViewer.Cli/PlanViewer.Cli.csproj
index d38fd5d..15e7897 100644
--- a/src/PlanViewer.Cli/PlanViewer.Cli.csproj
+++ b/src/PlanViewer.Cli/PlanViewer.Cli.csproj
@@ -11,7 +11,7 @@
enable
PlanViewer.Cli
planview
- 1.2.0
+ 1.2.1
Erik Darling
Darling Data LLC
Performance Studio
diff --git a/src/PlanViewer.Core/PlanViewer.Core.csproj b/src/PlanViewer.Core/PlanViewer.Core.csproj
index 3eab7bb..44835c5 100644
--- a/src/PlanViewer.Core/PlanViewer.Core.csproj
+++ b/src/PlanViewer.Core/PlanViewer.Core.csproj
@@ -5,7 +5,7 @@
enable
enable
PlanViewer.Core
- 1.2.0
+ 1.2.1
Erik Darling
Darling Data LLC
SQL Performance Studio
diff --git a/src/PlanViewer.Ssms.Installer/PlanViewer.Ssms.Installer.csproj b/src/PlanViewer.Ssms.Installer/PlanViewer.Ssms.Installer.csproj
index 8d607d5..7809dac 100644
--- a/src/PlanViewer.Ssms.Installer/PlanViewer.Ssms.Installer.csproj
+++ b/src/PlanViewer.Ssms.Installer/PlanViewer.Ssms.Installer.csproj
@@ -6,7 +6,7 @@
InstallSsmsExtension
PlanViewer.Ssms.Installer
app.manifest
- 1.2.0
+ 1.2.1
Erik Darling
Darling Data LLC
SQL Performance Studio