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
20 changes: 14 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ jobs:
- name: Publish Lite
run: dotnet publish Lite/PerformanceMonitorLite.csproj -c Release -o publish/Lite

- name: Publish Lite (self-contained for Velopack)
if: github.event_name == 'release'
run: dotnet publish Lite/PerformanceMonitorLite.csproj -c Release -r win-x64 --self-contained -o publish/Lite-velopack

- name: Publish CLI Installer
run: dotnet publish Installer/PerformanceMonitorInstaller.csproj -c Release

Expand Down Expand Up @@ -168,13 +172,16 @@ jobs:
VERSION: ${{ steps.version.outputs.VERSION }}
run: |
dotnet tool install -g vpk
New-Item -ItemType Directory -Force -Path releases/velopack
New-Item -ItemType Directory -Force -Path releases/velopack-dashboard
New-Item -ItemType Directory -Force -Path releases/velopack-lite

# Download previous release for delta generation
vpk download github --repoUrl https://github.com/${{ github.repository }} --channel win -o releases/velopack --token $env:GH_TOKEN
# Dashboard: download previous + pack
vpk download github --repoUrl https://github.com/${{ github.repository }} --channel dashboard -o releases/velopack-dashboard --token $env:GH_TOKEN
vpk pack -u PerformanceMonitorDashboard -v $env:VERSION -p publish/Dashboard-velopack -e PerformanceMonitorDashboard.exe -o releases/velopack-dashboard --channel dashboard

# Pack Dashboard (use self-contained publish output)
vpk pack -u PerformanceMonitorDashboard -v $env:VERSION -p publish/Dashboard-velopack -e PerformanceMonitorDashboard.exe -o releases/velopack --channel win
# Lite: download previous + pack
vpk download github --repoUrl https://github.com/${{ github.repository }} --channel lite -o releases/velopack-lite --token $env:GH_TOKEN
vpk pack -u PerformanceMonitorLite -v $env:VERSION -p publish/Lite-velopack -e PerformanceMonitorLite.exe -o releases/velopack-lite --channel lite

- name: Generate checksums
if: github.event_name == 'release'
Expand Down Expand Up @@ -202,4 +209,5 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ steps.version.outputs.VERSION }}
run: |
vpk upload github --repoUrl https://github.com/${{ github.repository }} --channel win -o releases/velopack --releaseName "v$env:VERSION" --tag "v$env:VERSION" --merge --token $env:GH_TOKEN
vpk upload github --repoUrl https://github.com/${{ github.repository }} --channel dashboard -o releases/velopack-dashboard --releaseName "v$env:VERSION" --tag "v$env:VERSION" --merge --token $env:GH_TOKEN
vpk upload github --repoUrl https://github.com/${{ github.repository }} --channel lite -o releases/velopack-lite --releaseName "v$env:VERSION" --tag "v$env:VERSION" --merge --token $env:GH_TOKEN
2 changes: 1 addition & 1 deletion Lite/App.xaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Application x:Class="PerformanceMonitorLite.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
Expand Down
27 changes: 16 additions & 11 deletions Lite/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,19 @@ protected override void OnStartup(StartupEventArgs e)

base.OnStartup(e);

// Initialize paths - use executable directory for portability
var exeDirectory = AppDomain.CurrentDomain.BaseDirectory;
DataDirectory = exeDirectory;
ConfigDirectory = Path.Combine(exeDirectory, "config");
DatabasePath = Path.Combine(exeDirectory, "monitor.duckdb");
ArchiveDirectory = Path.Combine(exeDirectory, "archive");
// Initialize paths — store data in %LOCALAPPDATA% so Velopack updates
// can replace the app directory without losing data
var appDataRoot = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"PerformanceMonitorLite");
DataDirectory = appDataRoot;
ConfigDirectory = Path.Combine(appDataRoot, "config");
DatabasePath = Path.Combine(appDataRoot, "monitor.duckdb");
ArchiveDirectory = Path.Combine(appDataRoot, "archive");

// Ensure directories exist
if (!Directory.Exists(ConfigDirectory))
{
Directory.CreateDirectory(ConfigDirectory);
}
Directory.CreateDirectory(ConfigDirectory);
Directory.CreateDirectory(Path.Combine(appDataRoot, "archive"));

// Load settings
LoadDefaultTimeRange();
Expand All @@ -187,7 +188,7 @@ protected override void OnStartup(StartupEventArgs e)
Helpers.ThemeManager.Apply(ColorTheme);

// Initialize logging
var logDirectory = Path.Combine(exeDirectory, "logs");
var logDirectory = Path.Combine(appDataRoot, "logs");
AppLogger.Initialize(logDirectory);
Helpers.MethodProfiler.Initialize(logDirectory);
Helpers.QueryLogger.Initialize(logDirectory);
Expand All @@ -198,6 +199,10 @@ protected override void OnStartup(StartupEventArgs e)
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
DispatcherUnhandledException += OnDispatcherUnhandledException;
TaskScheduler.UnobservedTaskException += OnUnobservedTaskException;

// Create and show main window (StartupUri removed for Velopack custom Main)
var mainWindow = new MainWindow();
mainWindow.Show();
}

protected override void OnExit(ExitEventArgs e)
Expand Down
2 changes: 1 addition & 1 deletion Lite/Controls/ServerTab.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@
}

var tz = ServerTimeHelper.GetTimezoneLabel(ServerTimeHelper.CurrentDisplayMode);
ConnectionStatusText.Text = $"{_server.ServerNameDisplay} - Last refresh: {DateTime.Now:HH:mm:ss} ({tz})";

Check warning on line 648 in Lite/Controls/ServerTab.xaml.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 648 in Lite/Controls/ServerTab.xaml.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 648 in Lite/Controls/ServerTab.xaml.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.

Check warning on line 648 in Lite/Controls/ServerTab.xaml.cs

View workflow job for this annotation

GitHub Actions / build

Dereference of a possibly null reference.
}
catch (Exception ex)
{
Expand Down Expand Up @@ -4242,7 +4242,7 @@

private void OpenLogFile_Click(object sender, RoutedEventArgs e)
{
var logDir = System.IO.Path.Combine(AppContext.BaseDirectory, "logs");
var logDir = System.IO.Path.Combine(App.DataDirectory, "logs");
var logFile = System.IO.Path.Combine(logDir, $"lite_{DateTime.Now:yyyyMMdd}.log");

if (File.Exists(logFile))
Expand Down
37 changes: 26 additions & 11 deletions Lite/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -165,26 +165,41 @@ private async Task CheckForUpdatesOnStartupAsync()
{
try
{
await Task.Delay(5000); // Don't slow down startup

if (!App.CheckForUpdatesOnStartup) return;

var result = await UpdateCheckService.CheckForUpdateAsync();
if (result?.IsUpdateAvailable == true)
// Try Velopack first (supports download + apply)
try
{
var answer = MessageBox.Show(
$"Performance Monitor {result.LatestVersion} is available (you have {result.CurrentVersion}).\n\nWould you like to open the download page?",
"Update Available",
MessageBoxButton.YesNo,
MessageBoxImage.Information);
var mgr = new Velopack.UpdateManager(
new Velopack.Sources.GithubSource(
"https://github.com/erikdarlingdata/PerformanceMonitor", null, false));

if (answer == MessageBoxResult.Yes)
var newVersion = await mgr.CheckForUpdatesAsync();
if (newVersion != null)
{
System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo
Dispatcher.Invoke(() =>
{
FileName = result.ReleaseUrl,
UseShellExecute = true
Title = $"Performance Monitor Lite — Update v{newVersion.TargetFullRelease.Version} available (Help > About)";
});
return;
}
}
catch
{
// Velopack packages may not exist yet — fall through
}

// Fallback: GitHub Releases API check
var result = await UpdateCheckService.CheckForUpdateAsync();
if (result?.IsUpdateAvailable == true)
{
Dispatcher.Invoke(() =>
{
Title = $"Performance Monitor Lite — Update {result.LatestVersion} available (Help > About)";
});
}
}
catch
{
Expand Down
2 changes: 2 additions & 0 deletions Lite/PerformanceMonitorLite.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<TargetFramework>net8.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
<StartupObject>PerformanceMonitorLite.Program</StartupObject>
<AssemblyName>PerformanceMonitorLite</AssemblyName>
<RootNamespace>PerformanceMonitorLite</RootNamespace>
<Product>SQL Server Performance Monitor Lite</Product>
Expand Down Expand Up @@ -63,6 +64,7 @@
<!-- MCP server for LLM tool access -->
<PackageReference Include="ModelContextProtocol" Version="0.7.0-preview.1" />
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.7.0-preview.1" />
<PackageReference Include="Velopack" Version="0.0.1298" />
</ItemGroup>

<ItemGroup>
Expand Down
26 changes: 26 additions & 0 deletions Lite/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2026 Erik Darling, Darling Data LLC
*
* This file is part of the SQL Server Performance Monitor Lite.
*
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
*/

using System;
using Velopack;

namespace PerformanceMonitorLite
{
public static class Program
{
[STAThread]
public static void Main(string[] args)
{
VelopackApp.Build().Run();

var app = new App();
app.InitializeComponent();
app.Run();
}
}
}
2 changes: 1 addition & 1 deletion Lite/Services/AlertStateService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public class AlertStateService

public AlertStateService()
{
_stateFilePath = Path.Combine(AppContext.BaseDirectory, "alert_state.json");
_stateFilePath = Path.Combine(App.DataDirectory, "alert_state.json");
_silencedServers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
_acknowledgedAlerts = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);
Load();
Expand Down
76 changes: 75 additions & 1 deletion Lite/Windows/AboutWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
* Licensed under the MIT License. See LICENSE file in the project root for full license information.
*/

using System;
using System.Diagnostics;
using System.Reflection;
using System.Windows;
using PerformanceMonitorLite.Services;
using Velopack;

namespace PerformanceMonitorLite.Windows;

Expand All @@ -21,6 +23,9 @@ public partial class AboutWindow : Window
private const string DarlingDataUrl = "https://www.erikdarling.com";

private string? _updateReleaseUrl;
private UpdateManager? _velopackMgr;
private Velopack.UpdateInfo? _pendingUpdate;
private bool _updateDownloaded;

public AboutWindow()
{
Expand All @@ -45,6 +50,32 @@ private async void CheckUpdatesLink_Click(object sender, RoutedEventArgs e)
UpdateStatusText.Text = "Checking for updates...";
UpdateStatusText.Visibility = Visibility.Visible;

// Try Velopack first (supports download + apply)
try
{
_velopackMgr = new UpdateManager(
new Velopack.Sources.GithubSource("https://github.com/erikdarlingdata/PerformanceMonitor", null, false));

var updateInfo = await _velopackMgr.CheckForUpdatesAsync();
if (updateInfo != null)
{
_pendingUpdate = updateInfo;
UpdateStatusText.Text = $"Update available: v{updateInfo.TargetFullRelease.Version} — click to install";
UpdateStatusText.Cursor = System.Windows.Input.Cursors.Hand;
UpdateStatusText.MouseLeftButtonUp -= UpdateStatusText_Click;
UpdateStatusText.MouseLeftButtonUp += VelopackDownload_Click;
UpdateStatusText.TextDecorations = System.Windows.TextDecorations.Underline;
UpdateStatusText.Foreground = FindResource("AccentBrush") as System.Windows.Media.Brush
?? System.Windows.Media.Brushes.DodgerBlue;
return;
}
}
catch
{
// Velopack packages may not exist yet — fall through
}

// Fallback: GitHub Releases API check (opens browser)
var result = await UpdateCheckService.CheckForUpdateAsync(bypassCache: true);

if (result == null)
Expand All @@ -54,8 +85,9 @@ private async void CheckUpdatesLink_Click(object sender, RoutedEventArgs e)
else if (result.IsUpdateAvailable)
{
_updateReleaseUrl = result.ReleaseUrl;
UpdateStatusText.Text = $"Update available: {result.LatestVersion} (you have {result.CurrentVersion})";
UpdateStatusText.Text = $"Update available: {result.LatestVersion} (you have {result.CurrentVersion}) — click to open releases";
UpdateStatusText.Cursor = System.Windows.Input.Cursors.Hand;
UpdateStatusText.MouseLeftButtonUp -= VelopackDownload_Click;
UpdateStatusText.MouseLeftButtonUp += UpdateStatusText_Click;
UpdateStatusText.TextDecorations = System.Windows.TextDecorations.Underline;
UpdateStatusText.Foreground = FindResource("AccentBrush") as System.Windows.Media.Brush
Expand All @@ -67,6 +99,48 @@ private async void CheckUpdatesLink_Click(object sender, RoutedEventArgs e)
}
}

private async void VelopackDownload_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (_velopackMgr == null || _pendingUpdate == null) return;

// Step 3: restart with confirmation
if (_updateDownloaded)
{
var result = MessageBox.Show(this,
"The application will close and restart with the new version. Continue?",
"Update Ready",
MessageBoxButton.OKCancel,
MessageBoxImage.Question);

if (result == MessageBoxResult.OK)
{
_velopackMgr.ApplyUpdatesAndRestart(_pendingUpdate.TargetFullRelease);
}
return;
}

// Step 2: download
try
{
UpdateStatusText.MouseLeftButtonUp -= VelopackDownload_Click;
UpdateStatusText.TextDecorations = null;
UpdateStatusText.Cursor = System.Windows.Input.Cursors.Arrow;
UpdateStatusText.Text = "Downloading update...";

await _velopackMgr.DownloadUpdatesAsync(_pendingUpdate);

_updateDownloaded = true;
UpdateStatusText.Text = "Update downloaded.";
UpdateStatusText.Cursor = System.Windows.Input.Cursors.Hand;
UpdateStatusText.TextDecorations = System.Windows.TextDecorations.Underline;
UpdateStatusText.MouseLeftButtonUp += VelopackDownload_Click;
}
catch (Exception ex)
{
UpdateStatusText.Text = $"Download failed: {ex.Message}";
}
}

private void UpdateStatusText_Click(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (!string.IsNullOrEmpty(_updateReleaseUrl))
Expand Down
Loading