Skip to content
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
23 changes: 21 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)"
Expand All @@ -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
53 changes: 52 additions & 1 deletion src/PlanViewer.App/AboutWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using Avalonia.Interactivity;
using PlanViewer.App.Mcp;
using PlanViewer.App.Services;
using Velopack;

namespace PlanViewer.App;

Expand Down Expand Up @@ -74,13 +75,41 @@ 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)
{
CheckUpdateButton.IsEnabled = false;
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
}
}
Comment on lines 81 to +110
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Clear cached update state before each check.

If a prior Windows check populated _velopackUpdate and a later Velopack check throws before Line 96 assigns a new value, the fallback branch can show a browser update while UpdateLink_Click still applies the stale Velopack release.

🛠️ Proposed fix
 private async void CheckUpdate_Click(object? sender, RoutedEventArgs e)
 {
     CheckUpdateButton.IsEnabled = false;
     UpdateStatusText.Text = "Checking...";
     UpdateLink.IsVisible = false;
+    _updateUrl = null;
+    _velopackMgr = null;
+    _velopackUpdate = null;
 
     // Try Velopack first (Windows only, supports download + apply)
     if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
     {
         try
         {
-            _velopackMgr = new UpdateManager(
+            var velopackMgr = new UpdateManager(
                 new Velopack.Sources.GithubSource(
                     "https://github.com/erikdarlingdata/PerformanceStudio", null, false));
 
-            _velopackUpdate = await _velopackMgr.CheckForUpdatesAsync();
-            if (_velopackUpdate != null)
+            var velopackUpdate = await velopackMgr.CheckForUpdatesAsync();
+            if (velopackUpdate != null)
             {
+                _velopackMgr = velopackMgr;
+                _velopackUpdate = velopackUpdate;
                 UpdateStatusText.Text = "Update available:";
-                UpdateLink.Text = $"v{_velopackUpdate.TargetFullRelease.Version} — click to download and install";
+                UpdateLink.Text = $"v{velopackUpdate.TargetFullRelease.Version} — click to download and install";
                 UpdateLink.IsVisible = true;
                 CheckUpdateButton.IsEnabled = true;
                 return;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private async void CheckUpdate_Click(object? sender, RoutedEventArgs e)
{
CheckUpdateButton.IsEnabled = false;
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
}
}
private async void CheckUpdate_Click(object? sender, RoutedEventArgs e)
{
CheckUpdateButton.IsEnabled = false;
UpdateStatusText.Text = "Checking...";
UpdateLink.IsVisible = false;
_updateUrl = null;
_velopackMgr = null;
_velopackUpdate = null;
// Try Velopack first (Windows only, supports download + apply)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
try
{
var velopackMgr = new UpdateManager(
new Velopack.Sources.GithubSource(
"https://github.com/erikdarlingdata/PerformanceStudio", null, false));
var velopackUpdate = await velopackMgr.CheckForUpdatesAsync();
if (velopackUpdate != null)
{
_velopackMgr = velopackMgr;
_velopackUpdate = velopackUpdate;
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
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/PlanViewer.App/AboutWindow.axaml.cs` around lines 81 - 110,
CheckUpdate_Click can reuse stale _velopackUpdate/_velopackMgr from a prior run
if the new Velopack check throws; reset the cached state at the start of the
method (set _velopackUpdate and _velopackMgr to null) and also ensure the
exception path does not leave a prior value in place, and add a defensive check
in UpdateLink_Click to verify _velopackUpdate is non-null before attempting to
apply a Velopack release (and show a friendly error if it is null).


// Fallback: GitHub API check (opens browser)
var currentVersion = Assembly.GetExecutingAssembly().GetName().Version ?? new Version(0, 0, 0);
var result = await UpdateChecker.CheckAsync(currentVersion);

Expand All @@ -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);
}

Expand Down
3 changes: 2 additions & 1 deletion src/PlanViewer.App/PlanViewer.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>EDD.ico</ApplicationIcon>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
<Version>1.2.0</Version>
<Version>1.2.1</Version>
<Authors>Erik Darling</Authors>
<Company>Darling Data LLC</Company>
<Product>Performance Studio</Product>
Expand All @@ -32,6 +32,7 @@
<PackageReference Include="ModelContextProtocol" Version="0.7.0-preview.1" />
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.7.0-preview.1" />
<PackageReference Include="TextMateSharp.Grammars" Version="2.0.3" />
<PackageReference Include="Velopack" Version="0.0.1298" />
</ItemGroup>

<ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions src/PlanViewer.App/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.IO.Pipes;
using System.Threading;
using Velopack;

namespace PlanViewer.App;

Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/PlanViewer.Cli/PlanViewer.Cli.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Nullable>enable</Nullable>
<RootNamespace>PlanViewer.Cli</RootNamespace>
<AssemblyName>planview</AssemblyName>
<Version>1.2.0</Version>
<Version>1.2.1</Version>
<Authors>Erik Darling</Authors>
<Company>Darling Data LLC</Company>
<Product>Performance Studio</Product>
Expand Down
2 changes: 1 addition & 1 deletion src/PlanViewer.Core/PlanViewer.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>PlanViewer.Core</RootNamespace>
<Version>1.2.0</Version>
<Version>1.2.1</Version>
<Authors>Erik Darling</Authors>
<Company>Darling Data LLC</Company>
<Product>SQL Performance Studio</Product>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<AssemblyName>InstallSsmsExtension</AssemblyName>
<RootNamespace>PlanViewer.Ssms.Installer</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Version>1.2.0</Version>
<Version>1.2.1</Version>
<Authors>Erik Darling</Authors>
<Company>Darling Data LLC</Company>
<Product>SQL Performance Studio</Product>
Expand Down
Loading