diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b24cf0..b622f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.3.19] - 2026-04-26 + +### Changed + +- **FluentMigrator / PostgreSQL:** Version metadata table is **`public.version_info`** with snake_case columns **`version`**, **`applied_on`**, **`description`** and unique index **`uc_version`**, configured via **`CertsFluentMigratorVersionTableMetaData`** and **`WithVersionTable`** on the runner (aligned with the rest of the schema naming). + +### Removed + +- **Startup migrations:** Removed legacy compatibility paths: **`VersionInfo` → `version_info` rename**, PascalCase → snake_case column repair on the version table, and the **EF-era baseline** that created or seeded **`version_info`** when **`users`** already existed (and **`RunMigrationsService.BaselineVersion`**). + +### Upgrade notes + +- **Breaking:** **Recreate the Certs engine database** (or use a new empty database). The app no longer upgrades in place from **`VersionInfo`**, mixed column casing, or pre–FluentMigrator-baseline layouts; **`MigrateUp`** expects a clean or fully FluentMigrator-managed schema. + ## [3.3.18] - 2026-04-26 ### Changed diff --git a/src/MaksIT.CertsUI.Engine/Extensions/ServiceCollectionExtensions.cs b/src/MaksIT.CertsUI.Engine/Extensions/ServiceCollectionExtensions.cs index 5c290db..64063fa 100644 --- a/src/MaksIT.CertsUI.Engine/Extensions/ServiceCollectionExtensions.cs +++ b/src/MaksIT.CertsUI.Engine/Extensions/ServiceCollectionExtensions.cs @@ -31,6 +31,7 @@ public static class ServiceCollectionExtensions { .ConfigureRunner(rb => rb .AddPostgres() .WithGlobalConnectionString(certsEngineConfiguration.ConnectionString) + .WithVersionTable(new CertsFluentMigratorVersionTableMetaData()) .ScanIn(typeof(BaselineCertsSchema).Assembly).For.All()) .AddLogging(lb => lb.AddFluentMigratorConsole()); services.AddScoped(); diff --git a/src/MaksIT.CertsUI.Engine/Infrastructure/CertsFluentMigratorVersionTableMetaData.cs b/src/MaksIT.CertsUI.Engine/Infrastructure/CertsFluentMigratorVersionTableMetaData.cs new file mode 100644 index 0000000..46b6c22 --- /dev/null +++ b/src/MaksIT.CertsUI.Engine/Infrastructure/CertsFluentMigratorVersionTableMetaData.cs @@ -0,0 +1,33 @@ +using FluentMigrator.Runner.VersionTableInfo; + +namespace MaksIT.CertsUI.Engine.Infrastructure; + +/// FluentMigrator version table: snake_case public.version_info (table and columns) for PostgreSQL consistency. +public sealed class CertsFluentMigratorVersionTableMetaData : IVersionTableMetaData { + + public const string Table = "version_info"; + + public const string VersionColumn = "version"; + + public const string AppliedOnColumn = "applied_on"; + + public const string DescriptionColumn = "description"; + + public const string UniqueIndex = "uc_version"; + + public bool OwnsSchema => true; + + public string SchemaName => "public"; + + public string TableName => Table; + + public string ColumnName => VersionColumn; + + public string DescriptionColumnName => DescriptionColumn; + + public string UniqueIndexName => UniqueIndex; + + public string AppliedOnColumnName => AppliedOnColumn; + + public bool CreateWithPrimaryKey => false; +} diff --git a/src/MaksIT.CertsUI.Engine/Infrastructure/RunMigrationsService.cs b/src/MaksIT.CertsUI.Engine/Infrastructure/RunMigrationsService.cs index cf32a40..6b12efc 100644 --- a/src/MaksIT.CertsUI.Engine/Infrastructure/RunMigrationsService.cs +++ b/src/MaksIT.CertsUI.Engine/Infrastructure/RunMigrationsService.cs @@ -8,7 +8,7 @@ using Npgsql; namespace MaksIT.CertsUI.Engine.Infrastructure; /// -/// FluentMigrator runner for the Certs database: optionally creates the database, baselines legacy EF-created schemas, migrates up, +/// FluentMigrator runner for the Certs database: optionally creates the database, migrates up, /// then idempotent coordination-table repair. Forward Up() migrations should be additive (new tables/columns); avoid dropping /// renamed or legacy columns in Up() — use expand/contract and ops-driven cleanup. /// @@ -18,8 +18,6 @@ public sealed class RunMigrationsService( ICertsEngineConfiguration config ) : IRunMigrationsService { - public static long BaselineVersion => BaselineCertsSchema.Version; - public async Task RunAsync(CancellationToken cancellationToken = default) { if (string.IsNullOrWhiteSpace(config.ConnectionString)) throw new InvalidOperationException( @@ -36,7 +34,6 @@ public sealed class RunMigrationsService( logger.LogInformation("FluentMigrator discovered {MigrationCount} migration type(s) in {Assembly}.", migrationTypeCount, typeof(BaselineCertsSchema).Assembly.GetName().Name); await EnsureDatabaseExistsAsync(cancellationToken).ConfigureAwait(false); - await BaselineExistingEfDatabaseAsync(cancellationToken).ConfigureAwait(false); await Task.Run(() => migrationRunner.MigrateUp(), cancellationToken).ConfigureAwait(false); await CoordinationTableProvisioner.EnsureAsync(config.ConnectionString, cancellationToken).ConfigureAwait(false); await VerifyCoreSchemaAsync(cancellationToken).ConfigureAwait(false); @@ -54,7 +51,7 @@ public sealed class RunMigrationsService( EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'app_runtime_leases') AND ( EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'users') - OR EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'VersionInfo') + OR EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'version_info') ); """, conn); @@ -64,7 +61,7 @@ public sealed class RunMigrationsService( return; throw new InvalidOperationException( - "After migrations and coordination DDL, schema \"public\" is missing \"app_runtime_leases\" and/or core tables (\"users\" / \"VersionInfo\"). " + + "After migrations and coordination DDL, schema \"public\" is missing \"app_runtime_leases\" and/or core tables (\"users\" / \"version_info\"). " + "Confirm Database= in the connection string, role CREATE privileges, and that FluentMigrator committed (non-empty connection string)."); } @@ -103,42 +100,4 @@ public sealed class RunMigrationsService( database); } } - - /// - /// If the database already has Certs tables from legacy EF Core migrations, mark the FluentMigrator baseline as applied. - /// - private async Task BaselineExistingEfDatabaseAsync(CancellationToken cancellationToken) { - await using var conn = new NpgsqlConnection(config.ConnectionString); - await conn.OpenAsync(cancellationToken).ConfigureAwait(false); - - await using (var cmd = new NpgsqlCommand( - "SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'users' LIMIT 1", - conn)) { - await using var reader = await cmd.ExecuteReaderAsync(cancellationToken).ConfigureAwait(false); - if (!await reader.ReadAsync(cancellationToken).ConfigureAwait(false)) - return; - } - - logger.LogInformation("Existing Certs schema detected; baselining FluentMigrator VersionInfo if needed."); - - await using (var cmd = new NpgsqlCommand(@" - CREATE TABLE IF NOT EXISTS ""VersionInfo"" ( - ""Version"" bigint NOT NULL PRIMARY KEY, - ""AppliedOn"" timestamp NULL, - ""Description"" varchar(1024) NULL - )", conn)) { - await cmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); - } - - await using (var cmd = new NpgsqlCommand( - @"INSERT INTO ""VersionInfo"" (""Version"", ""AppliedOn"", ""Description"") - VALUES (@v, @appliedOn, @desc) - ON CONFLICT (""Version"") DO NOTHING", - conn)) { - cmd.Parameters.AddWithValue("v", BaselineCertsSchema.Version); - cmd.Parameters.AddWithValue("appliedOn", DBNull.Value); - cmd.Parameters.AddWithValue("desc", "BaselineCertsSchema (existing DB from EF Core)"); - await cmd.ExecuteNonQueryAsync(cancellationToken).ConfigureAwait(false); - } - } } diff --git a/src/MaksIT.CertsUI/MaksIT.CertsUI.csproj b/src/MaksIT.CertsUI/MaksIT.CertsUI.csproj index e72d1d9..2f97ea8 100644 --- a/src/MaksIT.CertsUI/MaksIT.CertsUI.csproj +++ b/src/MaksIT.CertsUI/MaksIT.CertsUI.csproj @@ -1,7 +1,7 @@ - 3.3.18 + 3.3.19 net10.0 enable enable