Git workflow, environments & CI/CD pipeline (#1) #6
@@ -14,7 +14,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v4
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '10.0.x'
|
||||||
- name: Restore
|
- name: Restore
|
||||||
run: dotnet restore InboxIntel.sln
|
run: dotnet restore InboxIntel.sln
|
||||||
- name: Build
|
- name: Build
|
||||||
@@ -45,7 +45,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v4
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '10.0.x'
|
||||||
- name: dotnet format (verify only)
|
- name: dotnet format (verify only)
|
||||||
run: dotnet format InboxIntel.sln --verify-no-changes
|
run: dotnet format InboxIntel.sln --verify-no-changes
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v4
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '10.0.x'
|
||||||
- name: Wait for Postgres
|
- name: Wait for Postgres
|
||||||
run: |
|
run: |
|
||||||
for i in $(seq 1 30); do
|
for i in $(seq 1 30); do
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-dotnet@v4
|
- uses: actions/setup-dotnet@v4
|
||||||
with:
|
with:
|
||||||
dotnet-version: '8.0.x'
|
dotnet-version: '10.0.x'
|
||||||
- name: Restore
|
- name: Restore
|
||||||
run: dotnet restore InboxIntel.sln
|
run: dotnet restore InboxIntel.sln
|
||||||
- name: .NET vulnerable packages (fail on any)
|
- name: .NET vulnerable packages (fail on any)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Multi-stage build for the ASP.NET Core API.
|
# Multi-stage build for the ASP.NET Core API.
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
# Copy solution + project files first for layer-cached restore.
|
# Copy solution + project files first for layer-cached restore.
|
||||||
@@ -13,7 +13,7 @@ RUN dotnet restore src/InboxIntel.Api/InboxIntel.Api.csproj
|
|||||||
COPY src/ src/
|
COPY src/ src/
|
||||||
RUN dotnet publish src/InboxIntel.Api/InboxIntel.Api.csproj -c Release -o /app/publish /p:UseAppHost=false
|
RUN dotnet publish src/InboxIntel.Api/InboxIntel.Api.csproj -c Release -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /app/publish .
|
COPY --from=build /app/publish .
|
||||||
|
|
||||||
|
|||||||
@@ -5,10 +5,10 @@
|
|||||||
<UserSecretsId>210c6d96-c7e4-4ee9-8982-8b91424979b8</UserSecretsId>
|
<UserSecretsId>210c6d96-c7e4-4ee9-8982-8b91424979b8</UserSecretsId>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="8.0.7" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.7" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.9" />
|
||||||
<!-- Required on the startup project for `dotnet ef migrations` to work. -->
|
<!-- Required on the startup project for `dotnet ef migrations` to work. -->
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ var dp = builder.Services.AddDataProtection()
|
|||||||
var dpCertPath = builder.Configuration["DataProtection:CertificatePath"];
|
var dpCertPath = builder.Configuration["DataProtection:CertificatePath"];
|
||||||
if (!string.IsNullOrWhiteSpace(dpCertPath))
|
if (!string.IsNullOrWhiteSpace(dpCertPath))
|
||||||
{
|
{
|
||||||
dp.ProtectKeysWithCertificate(new System.Security.Cryptography.X509Certificates.X509Certificate2(
|
dp.ProtectKeysWithCertificate(System.Security.Cryptography.X509Certificates.X509CertificateLoader
|
||||||
dpCertPath, builder.Configuration["DataProtection:CertificatePassword"]));
|
.LoadPkcs12FromFile(dpCertPath, builder.Configuration["DataProtection:CertificatePassword"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Services.AddApplication();
|
builder.Services.AddApplication();
|
||||||
@@ -201,15 +201,14 @@ var forwardedOptions = new ForwardedHeadersOptions
|
|||||||
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,
|
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,
|
||||||
ForwardLimit = app.Configuration.GetValue<int?>("ForwardedHeaders:ForwardLimit") ?? 1
|
ForwardLimit = app.Configuration.GetValue<int?>("ForwardedHeaders:ForwardLimit") ?? 1
|
||||||
};
|
};
|
||||||
forwardedOptions.KnownNetworks.Clear();
|
forwardedOptions.KnownIPNetworks.Clear();
|
||||||
forwardedOptions.KnownProxies.Clear();
|
forwardedOptions.KnownProxies.Clear();
|
||||||
var trustedNetworks = app.Configuration.GetSection("ForwardedHeaders:KnownNetworks").Get<string[]>()
|
var trustedNetworks = app.Configuration.GetSection("ForwardedHeaders:KnownNetworks").Get<string[]>()
|
||||||
?? new[] { "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8", "::1/128" };
|
?? new[] { "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "127.0.0.0/8", "::1/128" };
|
||||||
foreach (var cidr in trustedNetworks)
|
foreach (var cidr in trustedNetworks)
|
||||||
{
|
{
|
||||||
var parts = cidr.Split('/');
|
if (System.Net.IPNetwork.TryParse(cidr, out var network))
|
||||||
if (parts.Length == 2 && System.Net.IPAddress.TryParse(parts[0], out var prefix) && int.TryParse(parts[1], out var len))
|
forwardedOptions.KnownIPNetworks.Add(network);
|
||||||
forwardedOptions.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(prefix, len));
|
|
||||||
}
|
}
|
||||||
app.UseForwardedHeaders(forwardedOptions);
|
app.UseForwardedHeaders(forwardedOptions);
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="FluentValidation" Version="11.9.2" />
|
<PackageReference Include="FluentValidation" Version="11.9.2" />
|
||||||
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
|
<PackageReference Include="FluentValidation.DependencyInjectionExtensions" Version="11.9.2" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.9" />
|
||||||
<!-- DbSet<> is exposed on IAppDbContext so the Application layer can query.
|
<!-- DbSet<> is exposed on IAppDbContext so the Application layer can query.
|
||||||
Pinned to 8.0.4 to match the Npgsql provider's Relational dependency. -->
|
Pinned to 8.0.4 to match the Npgsql provider's Relational dependency. -->
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.9" />
|
||||||
<!-- Transitive security pins: patch known .NET 8.0.0 advisories pulled in by EF Core. -->
|
<!-- Transitive security pins: patch known .NET 8.0.0 advisories pulled in by EF Core. -->
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.6" />
|
<PackageReference Include="System.Text.Json" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\InboxIntel.Domain\InboxIntel.Domain.csproj" />
|
<ProjectReference Include="..\InboxIntel.Domain\InboxIntel.Domain.csproj" />
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<!-- NpgsqlTypes.NpgsqlTsVector (FTS) and Pgvector.Vector (semantic search) are used as
|
<!-- NpgsqlTypes.NpgsqlTsVector (FTS) and Pgvector.Vector (semantic search) are used as
|
||||||
column types on the Email entity — same pragmatic precedent for both. -->
|
column types on the Email entity — same pragmatic precedent for both. -->
|
||||||
<PackageReference Include="Npgsql" Version="8.0.3" />
|
<PackageReference Include="Npgsql" Version="10.0.2" />
|
||||||
<PackageReference Include="Pgvector" Version="0.2.0" />
|
<PackageReference Include="Pgvector" Version="0.3.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -4,18 +4,18 @@
|
|||||||
<AssemblyName>InboxIntel.Infrastructure</AssemblyName>
|
<AssemblyName>InboxIntel.Infrastructure</AssemblyName>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.9" />
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.4">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.9">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Google.Apis.Gmail.v1" Version="1.68.0.3427" />
|
<PackageReference Include="Google.Apis.Gmail.v1" Version="1.68.0.3427" />
|
||||||
<PackageReference Include="Google.Apis.Auth" Version="1.68.0" />
|
<PackageReference Include="Google.Apis.Auth" Version="1.68.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="8.0.7" />
|
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.9" />
|
||||||
<PackageReference Include="Pgvector.EntityFrameworkCore" Version="0.2.0" />
|
<PackageReference Include="Pgvector.EntityFrameworkCore" Version="0.3.0" />
|
||||||
<PackageReference Include="Polly" Version="8.4.1" />
|
<PackageReference Include="Polly" Version="8.4.1" />
|
||||||
<PackageReference Include="QuestPDF" Version="2024.7.0" />
|
<PackageReference Include="QuestPDF" Version="2024.7.0" />
|
||||||
<PackageReference Include="CsvHelper" Version="33.0.1" />
|
<PackageReference Include="CsvHelper" Version="33.0.1" />
|
||||||
@@ -24,9 +24,9 @@
|
|||||||
<!-- Transitive security pins: patch known .NET 8.0.0 advisories pulled in by
|
<!-- Transitive security pins: patch known .NET 8.0.0 advisories pulled in by
|
||||||
EF Core / ASP.NET / DataProtection. Remove once the parent packages ship
|
EF Core / ASP.NET / DataProtection. Remove once the parent packages ship
|
||||||
these versions transitively. -->
|
these versions transitively. -->
|
||||||
<PackageReference Include="System.Text.Json" Version="8.0.6" />
|
<PackageReference Include="System.Text.Json" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="10.0.9" />
|
||||||
<PackageReference Include="System.Security.Cryptography.Xml" Version="8.0.3" />
|
<PackageReference Include="System.Security.Cryptography.Xml" Version="10.0.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\InboxIntel.Application\InboxIntel.Application.csproj" />
|
<ProjectReference Include="..\InboxIntel.Application\InboxIntel.Application.csproj" />
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace InboxIntel.IntegrationTests;
|
|||||||
/// endpoints (model validation, per-user rate limits) without a real Google login.</summary>
|
/// endpoints (model validation, per-user rate limits) without a real Google login.</summary>
|
||||||
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
|
||||||
{
|
{
|
||||||
public const string Scheme = "Test";
|
public new const string Scheme = "Test";
|
||||||
// Stable across requests so per-user rate-limit partitions accumulate correctly.
|
// Stable across requests so per-user rate-limit partitions accumulate correctly.
|
||||||
public static readonly string Uid = Guid.NewGuid().ToString();
|
public static readonly string Uid = Guid.NewGuid().ToString();
|
||||||
|
|
||||||
@@ -52,6 +52,9 @@ public class AuditTestAppFactory : WebApplicationFactory<Program>
|
|||||||
builder.ConfigureHostConfiguration(cfg => cfg.AddInMemoryCollection(new Dictionary<string, string?>
|
builder.ConfigureHostConfiguration(cfg => cfg.AddInMemoryCollection(new Dictionary<string, string?>
|
||||||
{
|
{
|
||||||
["Database:AutoMigrate"] = "false",
|
["Database:AutoMigrate"] = "false",
|
||||||
|
// Npgsql 10 eagerly validates the connection string when the DbContext is
|
||||||
|
// resolved (8.x was lazy); these tests never connect, but the string must parse.
|
||||||
|
["ConnectionStrings:Postgres"] = "Host=localhost;Database=test;Username=test;Password=test",
|
||||||
["GoogleOAuth:ClientId"] = "test-client-id",
|
["GoogleOAuth:ClientId"] = "test-client-id",
|
||||||
["GoogleOAuth:ClientSecret"] = "test-client-secret",
|
["GoogleOAuth:ClientSecret"] = "test-client-secret",
|
||||||
// H-2: make the auth policy trip on the 3rd request within the window.
|
// H-2: make the auth policy trip on the 3rd request within the window.
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ public class TestAppFactory : WebApplicationFactory<Program>
|
|||||||
builder.ConfigureHostConfiguration(cfg => cfg.AddInMemoryCollection(new Dictionary<string, string?>
|
builder.ConfigureHostConfiguration(cfg => cfg.AddInMemoryCollection(new Dictionary<string, string?>
|
||||||
{
|
{
|
||||||
["Database:AutoMigrate"] = "false",
|
["Database:AutoMigrate"] = "false",
|
||||||
|
// Npgsql 10 eagerly validates the connection string when the DbContext is
|
||||||
|
// resolved (8.x was lazy); these tests never connect, but the string must parse.
|
||||||
|
["ConnectionStrings:Postgres"] = "Host=localhost;Database=test;Username=test;Password=test",
|
||||||
// Dummy OAuth creds so the Google challenge produces a real 302 redirect
|
// Dummy OAuth creds so the Google challenge produces a real 302 redirect
|
||||||
// (an empty ClientId can make the handler throw instead of redirecting).
|
// (an empty ClientId can make the handler throw instead of redirecting).
|
||||||
["GoogleOAuth:ClientId"] = "test-client-id",
|
["GoogleOAuth:ClientId"] = "test-client-id",
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
<PackageReference Include="xunit" Version="2.9.0" />
|
<PackageReference Include="xunit" Version="2.9.0" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.7" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.9" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.4" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.9" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\InboxIntel.Api\InboxIntel.Api.csproj" />
|
<ProjectReference Include="..\..\src\InboxIntel.Api\InboxIntel.Api.csproj" />
|
||||||
|
|||||||
Reference in New Issue
Block a user