feat: add Migrator console project

- Dedicated HrynCo.NotificationService.Migrator console app
- Reads App:ConnectionString from config/env
- Calls db.Database.MigrateAsync() with clear log output
- Own Dockerfile (runtime:10.0 base, no SDK overhead)
- Replaces SDK volume-mount approach in docker-compose
- Added to solution with /docker/Migrator/ folder

Ref: IT-628

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Anatolii Grynchuk
2026-05-02 02:24:45 +03:00
parent 40b4071eb5
commit a8bc26fe38
6 changed files with 113 additions and 8 deletions
@@ -0,0 +1,17 @@
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY ["HrynCo.NotificationService.Migrator/HrynCo.NotificationService.Migrator.csproj", "HrynCo.NotificationService.Migrator/"]
COPY ["HrynCo.NotificationService.DAL.EF/HrynCo.NotificationService.DAL.EF.csproj", "HrynCo.NotificationService.DAL.EF/"]
COPY ["HrynCo.NotificationService.DAL.Abstract/HrynCo.NotificationService.DAL.Abstract.csproj", "HrynCo.NotificationService.DAL.Abstract/"]
COPY ["Directory.Packages.props", "./"]
COPY ["Directory.Build.props", "./"]
RUN dotnet restore "HrynCo.NotificationService.Migrator/HrynCo.NotificationService.Migrator.csproj"
COPY . .
RUN dotnet publish "HrynCo.NotificationService.Migrator/HrynCo.NotificationService.Migrator.csproj" -c Release -o /app/publish --no-restore
FROM mcr.microsoft.com/dotnet/runtime:10.0
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "HrynCo.NotificationService.Migrator.dll"]
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Serilog.Extensions.Hosting" />
<PackageReference Include="Serilog.Settings.Configuration" />
<PackageReference Include="Serilog.Sinks.Console" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HrynCo.NotificationService.DAL.EF\HrynCo.NotificationService.DAL.EF.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,46 @@
using HrynCo.NotificationService.DAL.EF;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Serilog;
Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateBootstrapLogger();
try
{
Log.Information("🚀 Notification Service Migrator starting...");
var host = Host.CreateDefaultBuilder(args)
.UseSerilog((ctx, cfg) => cfg
.ReadFrom.Configuration(ctx.Configuration)
.WriteTo.Console())
.ConfigureServices((ctx, services) =>
{
var connectionString = ctx.Configuration["App:ConnectionString"]
?? throw new InvalidOperationException("App:ConnectionString is not configured.");
services.AddDbContext<NotificationDbContext>(options =>
options.UseNpgsql(connectionString));
})
.Build();
using var scope = host.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<NotificationDbContext>();
Log.Information("Applying migrations...");
await db.Database.MigrateAsync();
Log.Information("Migrations applied successfully.");
}
catch (Exception ex)
{
Log.Fatal(ex, "Migration failed.");
return 1;
}
finally
{
await Log.CloseAndFlushAsync();
}
return 0;
@@ -0,0 +1,18 @@
{
"App": {
"ConnectionString": ""
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.EntityFrameworkCore": "Information"
}
},
"WriteTo": [
{ "Name": "Console" }
],
"Enrich": [ "FromLogContext" ]
}
}
+4
View File
@@ -8,7 +8,11 @@
<Folder Name="/docker/Worker/"> <Folder Name="/docker/Worker/">
<File Path="HrynCo.NotificationService.Worker/Dockerfile" /> <File Path="HrynCo.NotificationService.Worker/Dockerfile" />
</Folder> </Folder>
<Folder Name="/docker/Migrator/">
<File Path="HrynCo.NotificationService.Migrator/Dockerfile" />
</Folder> </Folder>
</Folder>
<Project Path="HrynCo.NotificationService.Migrator/HrynCo.NotificationService.Migrator.csproj" />
<Project Path="HrynCo.NotificationService.Web.IntegrationTests/HrynCo.NotificationService.Web.IntegrationTests.csproj" /> <Project Path="HrynCo.NotificationService.Web.IntegrationTests/HrynCo.NotificationService.Web.IntegrationTests.csproj" />
<Project Path="HrynCo.NotificationService.Web/HrynCo.NotificationService.Web.csproj" /> <Project Path="HrynCo.NotificationService.Web/HrynCo.NotificationService.Web.csproj" />
<Project Path="HrynCo.NotificationService.DAL.Abstract/HrynCo.NotificationService.DAL.Abstract.csproj" /> <Project Path="HrynCo.NotificationService.DAL.Abstract/HrynCo.NotificationService.DAL.Abstract.csproj" />
+3 -8
View File
@@ -1,18 +1,13 @@
services: services:
migrator: migrator:
image: mcr.microsoft.com/dotnet/sdk:10.0 build:
working_dir: /src context: ../..
volumes: dockerfile: HrynCo.NotificationService.Migrator/Dockerfile
- ../..:/src
environment: environment:
- App__ConnectionString=${CONNECTION_STRING} - App__ConnectionString=${CONNECTION_STRING}
depends_on: depends_on:
db: db:
condition: service_started condition: service_started
command:
- /bin/sh
- -c
- dotnet tool install --tool-path /tmp/dotnet-tools dotnet-ef --version "9.*" && until /tmp/dotnet-tools/dotnet-ef database update --project /src/HrynCo.NotificationService.DAL.EF --startup-project /src/HrynCo.NotificationService.Web; do echo "Migration failed, retrying in 3s..."; sleep 3; done
restart: "no" restart: "no"
api: api: