Some checks are pending
CI / Build og unit tests (push) Waiting to run
CI / Integration (mcr.microsoft.com/mssql/server:2017-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2019-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2022-latest) (push) Blocked by required conditions
CI / Integration (mcr.microsoft.com/mssql/server:2025-latest) (push) Blocked by required conditions
Rule packs (support dates and newest CU per release) are published in a signed feed and hot-loaded without a restart; the verified pack is reloaded at startup. New versions are announced (Notify) or downloaded and staged by the unprivileged agent (Automatic), and installed by a privileged apply-update task that re-verifies signature and hash in an admin-only folder, keeps appsettings/web.config, health-checks the services and rolls back on failure. Stable and preview channels, manifest expiry and rollback protection, https or folder/share feeds for offline sites. Vendor tool gains rulepack and publish; the Hub can serve the feed; the web UI gets a System page with update, rule pack, license and exactly what is sent to the Hub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E3EExwndy8Z22EK2Qpt6BK
52 lines
3 KiB
C#
52 lines
3 KiB
C#
using Dapper;
|
|
|
|
namespace SQLInsight.Repository;
|
|
|
|
public sealed record UpdateStatusRow
|
|
{
|
|
public string? Feed { get; init; }
|
|
public string Channel { get; init; } = "stable";
|
|
public string Mode { get; init; } = "Notify";
|
|
public bool RulePacks { get; init; }
|
|
public string InstalledVersion { get; init; } = string.Empty;
|
|
public string RulePackVersion { get; init; } = string.Empty;
|
|
public string RulePackSource { get; init; } = string.Empty;
|
|
public string? AvailableVersion { get; init; }
|
|
public bool AvailableSecurity { get; init; }
|
|
public string? AvailableNotesUrl { get; init; }
|
|
public string? StagedVersion { get; init; }
|
|
public DateTime? LastCheckAt { get; init; }
|
|
public string? LastError { get; init; }
|
|
public string? LastResultOutcome { get; init; }
|
|
public string? LastResultMessage { get; init; }
|
|
public DateTime? LastResultAt { get; init; }
|
|
public DateTime UpdatedAt { get; init; }
|
|
}
|
|
|
|
public sealed partial class InsightRepository
|
|
{
|
|
public async Task SaveUpdateStatusAsync(UpdateStatusRow status, CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(status);
|
|
await using var connection = await OpenAsync(cancellationToken).ConfigureAwait(false);
|
|
await connection.ExecuteAsync("""
|
|
MERGE insight.UpdateStatus AS t
|
|
USING (SELECT 1 AS Id) AS s ON t.Id = s.Id
|
|
WHEN MATCHED THEN UPDATE SET Feed = @Feed, Channel = @Channel, Mode = @Mode, RulePacks = @RulePacks, InstalledVersion = @InstalledVersion,
|
|
RulePackVersion = @RulePackVersion, RulePackSource = @RulePackSource, AvailableVersion = @AvailableVersion,
|
|
AvailableSecurity = @AvailableSecurity, AvailableNotesUrl = @AvailableNotesUrl, StagedVersion = @StagedVersion,
|
|
LastCheckAt = @LastCheckAt, LastError = @LastError, LastResultOutcome = @LastResultOutcome,
|
|
LastResultMessage = @LastResultMessage, LastResultAt = @LastResultAt, UpdatedAt = @UpdatedAt
|
|
WHEN NOT MATCHED THEN INSERT (Id, Feed, Channel, Mode, RulePacks, InstalledVersion, RulePackVersion, RulePackSource, AvailableVersion,
|
|
AvailableSecurity, AvailableNotesUrl, StagedVersion, LastCheckAt, LastError, LastResultOutcome, LastResultMessage, LastResultAt, UpdatedAt)
|
|
VALUES (1, @Feed, @Channel, @Mode, @RulePacks, @InstalledVersion, @RulePackVersion, @RulePackSource, @AvailableVersion,
|
|
@AvailableSecurity, @AvailableNotesUrl, @StagedVersion, @LastCheckAt, @LastError, @LastResultOutcome, @LastResultMessage, @LastResultAt, @UpdatedAt);
|
|
""", status).ConfigureAwait(false);
|
|
}
|
|
|
|
public async Task<UpdateStatusRow?> GetUpdateStatusAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
await using var connection = await OpenAsync(cancellationToken).ConfigureAwait(false);
|
|
return await connection.QuerySingleOrDefaultAsync<UpdateStatusRow>("SELECT * FROM insight.UpdateStatus WHERE Id = 1;").ConfigureAwait(false);
|
|
}
|
|
}
|