sqlinsight-pro/src/SQLInsight.Rules/Lifecycle/SupportLifecycleCatalog.cs
Claude 3121b598f9
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
Fase 3: signed updates of rule packs and the application
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
2026-09-29 09:19:41 +00:00

60 lines
2.3 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
using SQLInsight.Core.Instances;
namespace SQLInsight.Rules.Lifecycle;
public sealed record SupportLifecycle(SqlServerRelease Release, DateOnly? MainstreamEnd, DateOnly? ExtendedEnd);
/// <summary>
/// Microsoft's support dates per SQL Server release. Shipped as data so it can be updated with the rule pack.
/// </summary>
public sealed class SupportLifecycleCatalog
{
private const string ResourceName = "SQLInsight.Rules.support-lifecycle.json";
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true,
Converters = { new JsonStringEnumConverter() },
};
private readonly Dictionary<SqlServerRelease, SupportLifecycle> _entries;
public SupportLifecycleCatalog(IEnumerable<SupportLifecycle> entries)
{
ArgumentNullException.ThrowIfNull(entries);
_entries = entries.ToDictionary(e => e.Release);
}
public static SupportLifecycleCatalog LoadEmbedded()
{
using var stream = typeof(SupportLifecycleCatalog).Assembly.GetManifestResourceStream(ResourceName)
?? throw new InvalidOperationException($"Embedded resource '{ResourceName}' is missing.");
return From(JsonSerializer.Deserialize<CatalogDocument>(stream, JsonOptions));
}
/// <summary>Reads the lifecycle section of a rule pack. Throws <see cref="InvalidDataException"/> if it is malformed.</summary>
public static SupportLifecycleCatalog FromJson(JsonElement element) => From(element.Deserialize<CatalogDocument>(JsonOptions));
private static SupportLifecycleCatalog From(CatalogDocument? document)
{
if (document?.Releases is not { Count: > 0 } releases)
{
throw new InvalidDataException("The support lifecycle catalog is empty.");
}
try
{
return new SupportLifecycleCatalog(releases);
}
catch (ArgumentException ex)
{
throw new InvalidDataException("The support lifecycle catalog is invalid: " + ex.Message, ex);
}
}
public SupportLifecycle? Find(SqlServerRelease release) => _entries.GetValueOrDefault(release);
private sealed record CatalogDocument(IReadOnlyList<SupportLifecycle> Releases);
}