From a8bc26fe38b750539032a5b2551b0bfa02968581 Mon Sep 17 00:00:00 2001 From: Anatolii Grynchuk Date: Sat, 2 May 2026 02:24:45 +0300 Subject: [PATCH] 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> --- .../Dockerfile | 17 +++++++ ...HrynCo.NotificationService.Migrator.csproj | 25 ++++++++++ .../Program.cs | 46 +++++++++++++++++++ .../appsettings.json | 18 ++++++++ HrynCo.NotificationService.slnx | 4 ++ docker/environments/docker-compose.yml | 11 ++--- 6 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 HrynCo.NotificationService.Migrator/Dockerfile create mode 100644 HrynCo.NotificationService.Migrator/HrynCo.NotificationService.Migrator.csproj create mode 100644 HrynCo.NotificationService.Migrator/Program.cs create mode 100644 HrynCo.NotificationService.Migrator/appsettings.json diff --git a/HrynCo.NotificationService.Migrator/Dockerfile b/HrynCo.NotificationService.Migrator/Dockerfile new file mode 100644 index 0000000..f34c4b4 --- /dev/null +++ b/HrynCo.NotificationService.Migrator/Dockerfile @@ -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"] diff --git a/HrynCo.NotificationService.Migrator/HrynCo.NotificationService.Migrator.csproj b/HrynCo.NotificationService.Migrator/HrynCo.NotificationService.Migrator.csproj new file mode 100644 index 0000000..080e9b3 --- /dev/null +++ b/HrynCo.NotificationService.Migrator/HrynCo.NotificationService.Migrator.csproj @@ -0,0 +1,25 @@ + + + + Exe + net10.0 + enable + enable + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/HrynCo.NotificationService.Migrator/Program.cs b/HrynCo.NotificationService.Migrator/Program.cs new file mode 100644 index 0000000..d3e979d --- /dev/null +++ b/HrynCo.NotificationService.Migrator/Program.cs @@ -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(options => + options.UseNpgsql(connectionString)); + }) + .Build(); + + using var scope = host.Services.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + + 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; diff --git a/HrynCo.NotificationService.Migrator/appsettings.json b/HrynCo.NotificationService.Migrator/appsettings.json new file mode 100644 index 0000000..530da5d --- /dev/null +++ b/HrynCo.NotificationService.Migrator/appsettings.json @@ -0,0 +1,18 @@ +{ + "App": { + "ConnectionString": "" + }, + "Serilog": { + "MinimumLevel": { + "Default": "Information", + "Override": { + "Microsoft": "Warning", + "Microsoft.EntityFrameworkCore": "Information" + } + }, + "WriteTo": [ + { "Name": "Console" } + ], + "Enrich": [ "FromLogContext" ] + } +} diff --git a/HrynCo.NotificationService.slnx b/HrynCo.NotificationService.slnx index 9becdfd..0bdcbeb 100644 --- a/HrynCo.NotificationService.slnx +++ b/HrynCo.NotificationService.slnx @@ -8,7 +8,11 @@ + + + + diff --git a/docker/environments/docker-compose.yml b/docker/environments/docker-compose.yml index e44dc0c..59e5c91 100644 --- a/docker/environments/docker-compose.yml +++ b/docker/environments/docker-compose.yml @@ -1,18 +1,13 @@ services: migrator: - image: mcr.microsoft.com/dotnet/sdk:10.0 - working_dir: /src - volumes: - - ../..:/src + build: + context: ../.. + dockerfile: HrynCo.NotificationService.Migrator/Dockerfile environment: - App__ConnectionString=${CONNECTION_STRING} depends_on: db: 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" api: