Skip to content
Merged
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
96 changes: 81 additions & 15 deletions Installer/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,9 @@ static async Task<int> Main(string[] args)
Console.WriteLine("Licensed under the MIT License");
Console.WriteLine("https://github.com/erikdarlingdata/PerformanceMonitor");
Console.WriteLine("================================================================================");
Console.WriteLine();

await CheckForInstallerUpdateAsync(version);


/*
Determine if running in automated mode (command-line arguments provided)
Expand Down Expand Up @@ -422,7 +424,7 @@ Test connection and get SQL Server version
using (var connection = new SqlConnection(builder.ConnectionString))
{
await connection.OpenAsync().ConfigureAwait(false);
Console.WriteLine("Connection successful!");
WriteSuccess("Connection successful!");

/*Capture SQL Server version for summary report*/
using (var versionCmd = new SqlCommand(@"
Expand Down Expand Up @@ -468,7 +470,7 @@ Azure MI (EngineEdition 8) is always current, skip the check.*/
}
catch (Exception ex)
{
Console.WriteLine($"Connection failed: {ex.Message}");
WriteError($"Connection failed: {ex.Message}");
Console.WriteLine($"Exception type: {ex.GetType().Name}");
if (ex.InnerException != null)
{
Expand Down Expand Up @@ -655,7 +657,7 @@ Traces are server-level and persist after database drops
}
}

Console.WriteLine("✓ Clean install completed (jobs and database removed)");
WriteSuccess("Clean install completed (jobs and database removed)");
}
catch (Exception ex)
{
Expand Down Expand Up @@ -736,7 +738,7 @@ Traces are server-level and persist after database drops
{
Console.WriteLine();
Console.WriteLine("================================================================================");
Console.WriteLine("Installation aborted: upgrade scripts must succeed before installation can proceed.");
WriteError("Installation aborted: upgrade scripts must succeed before installation can proceed.");
Console.WriteLine("Fix the errors above and re-run the installer.");
Console.WriteLine("================================================================================");
if (!automatedMode)
Expand Down Expand Up @@ -854,12 +856,12 @@ Match GO only when it's a whole word on its own line
}
}

Console.WriteLine("✓ Success");
WriteSuccess("Success");
installSuccessCount++;
}
catch (Exception ex)
{
Console.WriteLine($"✗ FAILED");
WriteError("FAILED");
Console.WriteLine($" Error: {ex.Message}");
installFailureCount++;
installationErrors.Add((fileName, ex.Message));
Expand Down Expand Up @@ -938,7 +940,7 @@ Use SYSDATETIME() (local) because collection_time is stored in server local time
command.CommandTimeout = LongTimeoutSeconds;
await command.ExecuteNonQueryAsync().ConfigureAwait(false);
}
Console.WriteLine("✓ Success");
WriteSuccess("Success");

/*
Verify data was collected — only from this validation run, not historical errors
Expand Down Expand Up @@ -1089,7 +1091,7 @@ WHERE t.name LIKE 'query_snapshots_%'
}
}

Console.WriteLine("✓ Success");
WriteSuccess("Success");
installFailureCount = 0; /* Reset failure count */
}
}
Expand Down Expand Up @@ -1159,7 +1161,7 @@ await LogInstallationHistory(

if (installationSuccessful)
{
Console.WriteLine("Installation completed successfully!");
WriteSuccess("Installation completed successfully!");
Console.WriteLine();
Console.WriteLine("WHAT WAS INSTALLED:");
Console.WriteLine("✓ PerformanceMonitor database and all collection tables");
Expand All @@ -1179,7 +1181,7 @@ await LogInstallationHistory(
}
else
{
Console.WriteLine($"Installation completed with {totalFailureCount} error(s).");
WriteWarning($"Installation completed with {totalFailureCount} error(s).");
Console.WriteLine("Review errors above and check PerformanceMonitor.config.collection_log for details.");
}

Expand Down Expand Up @@ -1307,7 +1309,7 @@ private static async Task<int> PerformUninstallAsync(string connectionString, bo
await command.ExecuteNonQueryAsync().ConfigureAwait(false);

Console.WriteLine();
Console.WriteLine("✓ Uninstall completed successfully");
WriteSuccess("Uninstall completed successfully");
Console.WriteLine();
Console.WriteLine("Note: blocked process threshold (s) was NOT reset.");
}
Expand Down Expand Up @@ -1550,12 +1552,12 @@ Execute an upgrade folder
}
}

Console.WriteLine("✓ Success");
WriteSuccess("Success");
successCount++;
}
catch (Exception ex)
{
Console.WriteLine($"✗ FAILED");
WriteError("FAILED");
Console.WriteLine($" Error: {ex.Message}");
failureCount++;
}
Expand Down Expand Up @@ -1899,7 +1901,7 @@ private static async Task InstallDependenciesAsync(string connectionString)
await command.ExecuteNonQueryAsync().ConfigureAwait(false);
}

Console.WriteLine("✓ Success");
WriteSuccess("Success");
Console.WriteLine($" {description}");
successCount++;
}
Expand Down Expand Up @@ -2050,6 +2052,70 @@ Write file
return reportPath;
}

private static void WriteSuccess(string message)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.Write("√ ");
Console.ResetColor();
Console.WriteLine(message);
}

private static void WriteError(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("✗ ");
Console.ResetColor();
Console.WriteLine(message);
}

private static void WriteWarning(string message)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("! ");
Console.ResetColor();
Console.WriteLine(message);
}

private static async Task CheckForInstallerUpdateAsync(string currentVersion)
{
try
{
using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) };
client.DefaultRequestHeaders.Add("User-Agent", "PerformanceMonitor");
client.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");

var response = await client.GetAsync(
"https://api.github.com/repos/erikdarlingdata/PerformanceMonitor/releases/latest")
.ConfigureAwait(false);

if (!response.IsSuccessStatusCode) return;

var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
using var doc = System.Text.Json.JsonDocument.Parse(json);
var tagName = doc.RootElement.GetProperty("tag_name").GetString() ?? "";
var versionString = tagName.TrimStart('v', 'V');

if (!Version.TryParse(versionString, out var latest)) return;
if (!Version.TryParse(currentVersion, out var current)) return;

if (latest > current)
{
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("╔══════════════════════════════════════════════════════════════════════╗");
Console.WriteLine($"║ A newer version ({tagName}) is available! ");
Console.WriteLine("║ https://github.com/erikdarlingdata/PerformanceMonitor/releases ");
Console.WriteLine("╚══════════════════════════════════════════════════════════════════════╝");
Console.ResetColor();
Console.WriteLine();
}
}
catch
{
/* Best effort — don't block installation if GitHub is unreachable */
}
}

[GeneratedRegex(@"^\s*GO\s*(?:--[^\r\n]*)?\s*$", RegexOptions.IgnoreCase | RegexOptions.Multiline)]
private static partial Regex GoBatchRegExp();
}
Expand Down
Loading