diff --git a/.docfx/Dockerfile.docfx b/.docfx/Dockerfile.docfx index 1719a33..2229c73 100644 --- a/.docfx/Dockerfile.docfx +++ b/.docfx/Dockerfile.docfx @@ -1,4 +1,4 @@ -ARG NGINX_VERSION=1.31.0-alpine +ARG NGINX_VERSION=1.31.2-alpine FROM --platform=$BUILDPLATFORM nginx:${NGINX_VERSION} AS base RUN rm -rf /usr/share/nginx/html/* diff --git a/.docfx/api/namespaces/Codebelt.Bootstrapper.Console.md b/.docfx/api/namespaces/Codebelt.Bootstrapper.Console.md index 289c038..c654c2d 100644 --- a/.docfx/api/namespaces/Codebelt.Bootstrapper.Console.md +++ b/.docfx/api/namespaces/Codebelt.Bootstrapper.Console.md @@ -2,13 +2,17 @@ uid: Codebelt.Bootstrapper.Console summary: *content --- -The `Codebelt.Bootstrapper.Console` namespace offers an implementation optimized for console applications that is managed by its host. +The `Codebelt.Bootstrapper.Console` namespace delivers the console-specific implementation of the Codebelt bootstrapper, including `ConsoleProgram`/`ConsoleStartup` for the conventional Program/Startup pair and `MinimalConsoleProgram` for the minimal-host model. + +Use it when the host is a console application — a long-running daemon, a cron-style job, or a CLI process — and you want the `BootstrapperLifetime` callbacks (`OnApplicationStartedCallback`, `OnApplicationStoppingCallback`, `OnApplicationStoppedCallback`) to surface in your `Startup` so you can log and tear down cleanly. The minimal variant reflects over the entry assembly to discover a concrete `MinimalConsoleProgram` type, so the project keeps a clean `Program.cs` while still hosting a real application model. + +If you are starting a new console application, start with `MinimalConsoleProgram` and its `CreateHostBuilder` helper — it composes `UseBootstrapperLifetime`, `UseBootstrapperEnvironmentDefaults`, `UseBootstrapperProgram`, and `UseMinimalConsoleProgram` in one call. If you are keeping the conventional `Program.cs`/`Startup.cs` split, start with `ConsoleProgram` and pair it with `ConsoleStartup` to register services and override `RunAsync`. [!INCLUDE [availability-modern](../../includes/availability-modern.md)] -### Extension Methods +### Extension Members |Type|Ext|Methods| |--:|:-:|---| |HostApplicationBuilder|⬇️|`UseBootstrapperProgram`, `UseMinimalConsoleProgram`| -|IHostBuilder|⬇️|`UseConsoleStartup`| +|IHostBuilder|⬇️|`UseConsoleStartup`| diff --git a/.docfx/api/namespaces/Codebelt.Bootstrapper.Web.md b/.docfx/api/namespaces/Codebelt.Bootstrapper.Web.md index 9086a3e..1310dea 100644 --- a/.docfx/api/namespaces/Codebelt.Bootstrapper.Web.md +++ b/.docfx/api/namespaces/Codebelt.Bootstrapper.Web.md @@ -2,12 +2,17 @@ uid: Codebelt.Bootstrapper.Web summary: *content --- -The `Codebelt.Bootstrapper.Web` namespace offers an implementation optimized for web applications that is managed by its host. +The `Codebelt.Bootstrapper.Web` namespace is the web-specific implementation of the Codebelt bootstrapper. It adds `WebProgram`/`WebStartup` for the conventional ASP.NET Core Program/Startup pair and `MinimalWebProgram` for the minimal-host model. + +Use it when the host is an ASP.NET Core web application — WebAPI, webapp, or MVC — and you want the same `BootstrapperLifetime` callbacks that console and worker applications use, applied to the web request pipeline. `WebStartup.ConfigurePipeline` is the entry point for the conventional pair and is wired up automatically by `WebHostBuilderExtensions.UseStartup` through the trivial `Configure` shim. + +If you are starting a new web application, start with `MinimalWebProgram` and its `CreateHostBuilder` helper, then call `hb.UseBootstrapperLifetime()` to keep the lifetime consistent with the rest of the Codebelt family. If you are keeping the conventional Program/Startup split, start with `WebProgram` and pair it with `WebStartup` to define your service registration and request pipeline. [!INCLUDE [availability-modern](../../includes/availability-modern.md)] -### Extension Methods +### Extension Members |Type|Ext|Methods| |--:|:-:|---| -|WebApplicationBuilder|⬇️|`UseBootstrapperLifetime`| +|WebApplicationBuilder|⬇️|`UseBootstrapperLifetime`, `UseBootstrapperEnvironmentDefaults`| +|IHostBuilder|⬇️|`UseBootstrapperStartup`, `UseBootstrapperEnvironmentDefaults`| diff --git a/.docfx/api/namespaces/Codebelt.Bootstrapper.Worker.md b/.docfx/api/namespaces/Codebelt.Bootstrapper.Worker.md index f6eb785..763bca9 100644 --- a/.docfx/api/namespaces/Codebelt.Bootstrapper.Worker.md +++ b/.docfx/api/namespaces/Codebelt.Bootstrapper.Worker.md @@ -2,6 +2,10 @@ uid: Codebelt.Bootstrapper.Worker summary: *content --- -The `Codebelt.Bootstrapper.Worker` namespace offers an implementation optimized for worker applications that is managed by its host. +If you build a .NET worker service and want the same lifetime callbacks and the same conventional Program.cs/Startup.cs split that the rest of the Codebelt family uses, the `Codebelt.Bootstrapper.Worker` namespace is your starting point. It provides `WorkerProgram` and `WorkerStartup` for the conventional Program/Startup pair, and `MinimalWorkerProgram` for the minimal-host model, both wired against `BootstrapperLifetime` and the shared `UseBootstrapperEnvironmentDefaults` for local-development user secrets. + +Choose this namespace when the host is a `BackgroundService`, a long-running queue consumer, or any other `IHostedService` host that should react to startup and shutdown callbacks through `IHostLifetimeEvents`. The worker flavor is intentionally lightweight because worker hosts are almost always `Host.CreateApplicationBuilder` or `Host.CreateDefaultBuilder` based. + +If you are starting a new worker service, start with `MinimalWorkerProgram` and its `CreateHostBuilder` helper — it composes `UseBootstrapperLifetime` and `UseBootstrapperEnvironmentDefaults` so a hosted service can react to lifetime events. If you are keeping the conventional Program/Startup split, start with `WorkerProgram` and pair it with `WorkerStartup` to register the worker's services. [!INCLUDE [availability-modern](../../includes/availability-modern.md)] diff --git a/.docfx/api/namespaces/Codebelt.Bootstrapper.md b/.docfx/api/namespaces/Codebelt.Bootstrapper.md index 0ee5251..0190073 100644 --- a/.docfx/api/namespaces/Codebelt.Bootstrapper.md +++ b/.docfx/api/namespaces/Codebelt.Bootstrapper.md @@ -2,14 +2,17 @@ uid: Codebelt.Bootstrapper summary: *content --- -The `Codebelt.Bootstrapper` namespace offers core types of this lightweight boostrapper framework optimized for console applicationss, providing a uniform and consistent implementation of console-, worker-, web-, mvc-, webapp- and webapi-project types. +If you want a single .NET host that listens to SIGTERM/Ctrl+C, exposes a conventional Program.cs/Startup.cs split, and applies the same lifetime and environment defaults across console, web, and worker projects, the `Codebelt.Bootstrapper` namespace is the cross-cutting layer to start from. The namespace defines `BootstrapperLifetime` (which complements the default `Microsoft.Extensions.Hosting.Internal.ConsoleLifetime`), a `StartupRoot` partner for the conventional Program/Startup pair, and the shared `UseBootstrapperLifetime` / `UseBootstrapperStartup` / `UseBootstrapperEnvironmentDefaults` extension methods that the rest of the family (`Codebelt.Bootstrapper.Console`, `Codebelt.Bootstrapper.Web`, and `Codebelt.Bootstrapper.Worker`) compose on top of. -[!INCLUDE [availability-modern](../../includes/availability-modern.md)] +Choose this surface when you need a uniform bootstrap regardless of project type, or when a console/worker/web project should share the same lifetime callbacks and the same conventional startup shape. If you are on the minimal-host model, start with `UseBootstrapperLifetime` on `IHostApplicationBuilder` to install `BootstrapperLifetime` together with `IHostLifetimeEvents` so a hosted service can react to startup and shutdown. If you are on the conventional Program/Startup pair, start with `UseBootstrapperStartup` on `IHostBuilder` to wire a `StartupRoot` partner into the service collection. For local-development user secrets, reach for `UseBootstrapperEnvironmentDefaults` (or its `TStartup` overload) so secrets are added to configuration automatically. -### Extension Methods +[!INCLUDE [availability-default](../../includes/availability-default.md)] + +### Extension Members |Type|Ext|Methods| |--:|:-:|---| -|HostApplicationBuilder|⬇️|`UseBootstrapperLifetime`| -|IHostBuilder|⬇️|`UseBootstrapperLifetime`, `UseBootstrapperStartup`| +|HostApplicationBuilder|⬇️|`UseBootstrapperLifetime`, `UseBootstrapperEnvironmentDefaults`| +|IHostBuilder|⬇️|`UseBootstrapperLifetime`, `UseBootstrapperStartup`, `UseBootstrapperEnvironmentDefaults`| |IHostedService|⬇️|`WaitForApplicationStartedAnnouncementAsync`| +|IDecorator|⬇️|`RunAsyncStarted`, `RunAsyncPrematureEnd`, `RunAsyncCompleted`, `UnableToActivateInstance`, `FatalErrorActivating`| diff --git a/.docfx/api/types/Codebelt.Bootstrapper.BootstrapperLifetime.md b/.docfx/api/types/Codebelt.Bootstrapper.BootstrapperLifetime.md new file mode 100644 index 0000000..66ab4b2 --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.BootstrapperLifetime.md @@ -0,0 +1,31 @@ +--- +uid: Codebelt.Bootstrapper.BootstrapperLifetime +example: +- *content +--- +The following example shows how to resolve `BootstrapperLifetime` from the host's service container, attach a callback to the application-stopped event, and confirm the lifetime is registered after the host starts. The `BootstrapperLifetime` replaces the default `Microsoft.Extensions.Hosting.Internal.ConsoleLifetime` so that consumers can subscribe to the same `IHostLifetimeEvents` surface area used by hosted services. + +```csharp +using System; +using Codebelt.Bootstrapper; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace BootstrapperLifetimeDemo; + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperLifetime(); + var host = builder.Build(); + host.Start(); + + var lifetime = (BootstrapperLifetime)host.Services.GetRequiredService(); + lifetime.OnApplicationStoppedCallback = () => Console.WriteLine("Stopped."); + + host.StopAsync().GetAwaiter().GetResult(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.BootstrapperLogMessages.md b/.docfx/api/types/Codebelt.Bootstrapper.BootstrapperLogMessages.md new file mode 100644 index 0000000..a4695eb --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.BootstrapperLogMessages.md @@ -0,0 +1,44 @@ +--- +uid: Codebelt.Bootstrapper.BootstrapperLogMessages +example: +- *content +--- +The static `BootstrapperLogMessages` class is the host's source of truth for the four lifecycle events that `ConsoleHostedService` and `MinimalConsoleHostedService` log through `Decorator.EncloseToExpose(logger, false)`: `RunAsyncStarted` when the run loop begins, `RunAsyncPrematureEnd` when the host stops before the run finishes, `RunAsyncCompleted` when the run finishes cleanly, and `FatalErrorActivating` when the run loop's `catch` block sees an exception. The companion `UnableToActivateInstance` extension surfaces a warning when the bootstrapper could not resolve a startup or program type. To use them from your own `ConsoleStartup` or `MinimalConsoleProgram`, build an `ILogger` through your normal `ServiceCollection` configuration and wrap it with `Decorator.EncloseToExpose(logger, false)`; the resulting `IDecorator` exposes all five extensions through receiver-style calls. The example below wires a console logger, demonstrates the run-loop happy path, exercises the failure path, and shows the startup-resolution warning so every extension in this class is invoked in a single coherent workflow. + +```csharp +using System; +using Codebelt.Bootstrapper; +using Cuemon; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace BootstrapperLogMessagesDemo; + +public class BootstrapperLogMessagesDemoHost +{ + public void Run() + { + using var services = new ServiceCollection() + .AddLogging(builder => builder.AddConsole()) + .BuildServiceProvider(); + + var logger = services.GetRequiredService>(); + var decorated = Decorator.EncloseToExpose(logger, false); + + decorated.RunAsyncStarted(); + + try + { + throw new InvalidOperationException("Boom"); + } + catch (Exception ex) + { + decorated.FatalErrorActivating(typeof(BootstrapperLogMessagesDemoHost).FullName!, ex); + } + + decorated.UnableToActivateInstance(typeof(BootstrapperLogMessagesDemoHost).FullName!); + decorated.RunAsyncPrematureEnd(); + decorated.RunAsyncCompleted(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.Console.ConsoleHostedService`1.md b/.docfx/api/types/Codebelt.Bootstrapper.Console.ConsoleHostedService`1.md new file mode 100644 index 0000000..4389811 --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.Console.ConsoleHostedService`1.md @@ -0,0 +1,48 @@ +--- +uid: Codebelt.Bootstrapper.Console.ConsoleHostedService`1 +example: +- *content +--- +The following example shows how `ConsoleHostedService` is registered by `UseConsoleStartup` on the conventional `IHostBuilder`, and how the hosted service itself can be resolved from the host's service collection. When the host starts, the service logs a `RunAsync started.` message through `BootstrapperLogMessages`, runs the `TStartup.RunAsync` task, and then calls `IHostApplicationLifetime.StopApplication` to begin a graceful shutdown. + +```csharp +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Console; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace ConsoleHostedServiceDemo; + +public class MyStartup : ConsoleStartup +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } + + public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +public static class Program +{ + public static void Main() + { + var host = Host.CreateDefaultBuilder() + .UseConsoleStartup() + .Build(); + + var hosted = host.Services.GetServices(); + var consoleHosted = hosted.OfType>().Single(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.Console.HostApplicationBuilderExtensions.md b/.docfx/api/types/Codebelt.Bootstrapper.Console.HostApplicationBuilderExtensions.md new file mode 100644 index 0000000..fea7f4e --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.Console.HostApplicationBuilderExtensions.md @@ -0,0 +1,115 @@ +--- +uid: Codebelt.Bootstrapper.Console.HostApplicationBuilderExtensions +example: +- *content +--- +The following example shows how to compose `UseBootstrapperProgram` and `UseMinimalConsoleProgram` on a `HostApplicationBuilder` to wire a `MinimalConsoleProgram` implementation into the host. The `ProgramFactory` then discovers the derived `MinimalConsoleProgram` in the entry assembly and the `MinimalConsoleHostedService` runs its `RunAsync` once the host is fully started. + +```csharp +using System; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Console; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace MinimalConsoleHostDemo; + +public class MyProgram : MinimalConsoleProgram +{ + public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperProgram(typeof(MyProgram)); + builder.UseMinimalConsoleProgram(); + + using var host = builder.Build(); + var hosted = host.Services.GetServices(); + } +} +``` + +--- +uid: Codebelt.Bootstrapper.Console.HostApplicationBuilderExtensions.UseBootstrapperProgram +example: +- *content +--- +The following example shows how to call `UseBootstrapperProgram` on a `HostApplicationBuilder` to register a `ProgramFactory` that resolves a `MinimalConsoleProgram`-derived type at runtime. + +```csharp +using System; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Console; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace UseBootstrapperProgramDemo; + +public class MyProgram : MinimalConsoleProgram +{ + public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperProgram(typeof(MyProgram)); + + using var host = builder.Build(); + var factory = host.Services.GetRequiredService(); + } +} +``` + +--- +uid: Codebelt.Bootstrapper.Console.HostApplicationBuilderExtensions.UseMinimalConsoleProgram +example: +- *content +--- +The following example shows how to call `UseMinimalConsoleProgram` on a `HostApplicationBuilder` to register the `MinimalConsoleHostedService` so the run loop is driven from a single hosted service. + +```csharp +using System; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Console; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace UseMinimalConsoleProgramDemo; + +public class MyProgram : MinimalConsoleProgram +{ + public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperProgram(typeof(MyProgram)); + builder.UseMinimalConsoleProgram(); + + using var host = builder.Build(); + var hosted = host.Services.GetServices(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.Console.HostBuilderExtensions.md b/.docfx/api/types/Codebelt.Bootstrapper.Console.HostBuilderExtensions.md new file mode 100644 index 0000000..3d71968 --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.Console.HostBuilderExtensions.md @@ -0,0 +1,93 @@ +--- +uid: Codebelt.Bootstrapper.Console.HostBuilderExtensions +example: +- *content +--- +The following example shows how to call `UseConsoleStartup` on a conventional `IHostBuilder` to register a `ConsoleHostedService` so the host runs the startup's `RunAsync` method from a hosted service. + +```csharp +using System; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Console; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace UseConsoleStartupDemo; + +public class MyStartup : ConsoleStartup +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } + + public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +public static class Program +{ + public static void Main() + { + using var host = Host.CreateDefaultBuilder() + .UseConsoleStartup() + .Build(); + + host.Run(); + } +} +``` + +--- +uid: Codebelt.Bootstrapper.Console.HostBuilderExtensions.UseConsoleStartup +example: +- *content +--- +The following example shows how to call `UseConsoleStartup` on a conventional `IHostBuilder` to register the console-style hosted service that drives `TStartup.RunAsync` from inside the host. + +```csharp +using System; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Console; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace UseConsoleStartupGenericDemo; + +public class MyStartup : ConsoleStartup +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } + + public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +public static class Program +{ + public static void Main() + { + using var host = Host.CreateDefaultBuilder() + .UseConsoleStartup() + .Build(); + + host.Run(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.Console.MinimalConsoleHostedService.md b/.docfx/api/types/Codebelt.Bootstrapper.Console.MinimalConsoleHostedService.md new file mode 100644 index 0000000..dd207d8 --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.Console.MinimalConsoleHostedService.md @@ -0,0 +1,39 @@ +--- +uid: Codebelt.Bootstrapper.Console.MinimalConsoleHostedService +example: +- *content +--- +The following example shows how `MinimalConsoleHostedService` is registered by `UseMinimalConsoleProgram` on a `HostApplicationBuilder`. When the host starts, the service subscribes to `IHostLifetimeEvents.OnApplicationStartedCallback`, runs the `MinimalConsoleProgram.RunAsync` task, and then calls `IHostApplicationLifetime.StopApplication` to begin a graceful shutdown. The example resolves the hosted service from the host's service collection to confirm the registration. + +```csharp +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Console; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace MinimalConsoleHostedServiceDemo; + +public class MyProgram : MinimalConsoleProgram +{ + public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperProgram(typeof(MyProgram)); + builder.UseMinimalConsoleProgram(); + + using var host = builder.Build(); + var hosted = host.Services.GetServices().OfType().Single(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.Console.ProgramFactory.md b/.docfx/api/types/Codebelt.Bootstrapper.Console.ProgramFactory.md new file mode 100644 index 0000000..620464a --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.Console.ProgramFactory.md @@ -0,0 +1,37 @@ +--- +uid: Codebelt.Bootstrapper.Console.ProgramFactory +example: +- *content +--- +The following example shows how `ProgramFactory` activates a derived `MinimalConsoleProgram` from the entry assembly. The factory is what `UseBootstrapperProgram` registers, so consumer code resolves `IProgramFactory.Instance` (and can cast the registered service to `ProgramFactory` to confirm the concrete type). + +```csharp +using System.Threading; +using System.Threading.Tasks; +using Codebelt.Bootstrapper.Console; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace ProgramFactoryDemo; + +public class MyProgram : MinimalConsoleProgram +{ + public override Task RunAsync(System.IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } +} + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperProgram(typeof(MyProgram)); + + using var host = builder.Build(); + var factory = (ProgramFactory)host.Services.GetRequiredService(); + var program = factory.Instance; + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.HostApplicationBuilderExtensions.md b/.docfx/api/types/Codebelt.Bootstrapper.HostApplicationBuilderExtensions.md new file mode 100644 index 0000000..c992e80 --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.HostApplicationBuilderExtensions.md @@ -0,0 +1,82 @@ +--- +uid: Codebelt.Bootstrapper.HostApplicationBuilderExtensions +example: +- *content +--- +The following example shows how to install `BootstrapperLifetime` and apply local-development environment defaults on a minimal-host `HostApplicationBuilder` so that user secrets from the application assembly are added to configuration when the environment is `LocalDevelopment`. + +```csharp +using Codebelt.Bootstrapper; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace HostApplicationBuilderExtensionsDemo; + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperLifetime(); + builder.UseBootstrapperEnvironmentDefaults(); + + var host = builder.Build(); + host.Start(); + + var lifetime = host.Services.GetRequiredService(); + } +} +``` + +--- +uid: Codebelt.Bootstrapper.HostApplicationBuilderExtensions.UseBootstrapperLifetime +example: +- *content +--- +The following example shows how to call `UseBootstrapperLifetime` on a `HostApplicationBuilder` to replace the default `IHostLifetime` with `BootstrapperLifetime`, exposing `IHostLifetimeEvents` so hosted services can subscribe to startup and shutdown callbacks. + +```csharp +using Codebelt.Bootstrapper; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace UseBootstrapperLifetimeDemo; + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperLifetime(); + + var host = builder.Build(); + var lifetime = host.Services.GetRequiredService(); + } +} +``` + +--- +uid: Codebelt.Bootstrapper.HostApplicationBuilderExtensions.UseBootstrapperEnvironmentDefaults +example: +- *content +--- +The following example shows how to call `UseBootstrapperEnvironmentDefaults` on a `HostApplicationBuilder` so that, when the environment is `LocalDevelopment`, the application assembly is resolved from `IHostEnvironment.ApplicationName` and user secrets are added to the configuration builder. + +```csharp +using Codebelt.Bootstrapper; +using Microsoft.Extensions.Hosting; + +namespace UseBootstrapperEnvironmentDefaultsDemo; + +public static class Program +{ + public static void Main() + { + var builder = Host.CreateApplicationBuilder(); + builder.UseBootstrapperEnvironmentDefaults(); + + var host = builder.Build(); + host.Start(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.HostBuilderExtensions.md b/.docfx/api/types/Codebelt.Bootstrapper.HostBuilderExtensions.md new file mode 100644 index 0000000..2527377 --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.HostBuilderExtensions.md @@ -0,0 +1,155 @@ +--- +uid: Codebelt.Bootstrapper.HostBuilderExtensions +example: +- *content +--- +The following example shows how to install `BootstrapperLifetime`, register a conventional `StartupRoot` partner through `UseBootstrapperStartup`, and apply local-development environment defaults on a conventional `IHostBuilder` produced by `Host.CreateDefaultBuilder`. + +```csharp +using Codebelt.Bootstrapper; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace HostBuilderExtensionsDemo; + +public class MyStartup : StartupRoot +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } +} + +public static class Program +{ + public static void Main() + { + var host = Host.CreateDefaultBuilder() + .UseBootstrapperLifetime() + .UseBootstrapperStartup() + .UseBootstrapperEnvironmentDefaults() + .Build(); + + var lifetime = host.Services.GetRequiredService(); + var factory = host.Services.GetRequiredService>(); + host.Run(); + } +} +``` + +--- +uid: Codebelt.Bootstrapper.HostBuilderExtensions.UseBootstrapperLifetime +example: +- *content +--- +The following example shows how to call `UseBootstrapperLifetime` on an `IHostBuilder` to install `BootstrapperLifetime` so the conventional host also exposes `IHostLifetimeEvents` to hosted services. + +```csharp +using Codebelt.Bootstrapper; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace UseBootstrapperLifetimeHostBuilderDemo; + +public static class Program +{ + public static void Main() + { + using var host = Host.CreateDefaultBuilder() + .UseBootstrapperLifetime() + .Build(); + + var lifetime = host.Services.GetRequiredService(); + } +} +``` + +--- +uid: Codebelt.Bootstrapper.HostBuilderExtensions.UseBootstrapperStartup +example: +- *content +--- +The following example shows how to call `UseBootstrapperStartup` on an `IHostBuilder` to register a `StartupRoot` partner. The `IStartupFactory` registered here produces the `TStartup` instance that consumers resolve from the service container. + +```csharp +using Codebelt.Bootstrapper; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace UseBootstrapperStartupDemo; + +public class MyStartup : StartupRoot +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } +} + +public static class Program +{ + public static void Main() + { + using var host = Host.CreateDefaultBuilder() + .UseBootstrapperStartup() + .Build(); + + var factory = host.Services.GetRequiredService>(); + var startup = factory.Instance; + } +} +``` + +--- +uid: Codebelt.Bootstrapper.HostBuilderExtensions.UseBootstrapperEnvironmentDefaults +example: +- *content +--- +The following example shows how to call `UseBootstrapperEnvironmentDefaults` on an `IHostBuilder` so that, when the environment is `LocalDevelopment`, user secrets for the `TStartup` type are added to the application's configuration. The first call sets the environment to `LocalDevelopment` so the addition is observable; the second call leaves the default `Development` environment to demonstrate the no-op path. + +```csharp +using Codebelt.Bootstrapper; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace UseBootstrapperEnvironmentDefaultsHostBuilderDemo; + +public class MyStartup : StartupRoot +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } +} + +public static class Program +{ + public static void Main() + { + using var localHost = Host.CreateDefaultBuilder() + .UseEnvironment("LocalDevelopment") + .UseBootstrapperEnvironmentDefaults() + .Build(); + + using var defaultHost = Host.CreateDefaultBuilder() + .UseEnvironment(Environments.Development) + .UseBootstrapperEnvironmentDefaults() + .Build(); + + localHost.Start(); + defaultHost.Start(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.StartupFactory`1.md b/.docfx/api/types/Codebelt.Bootstrapper.StartupFactory`1.md new file mode 100644 index 0000000..2977123 --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.StartupFactory`1.md @@ -0,0 +1,39 @@ +--- +uid: Codebelt.Bootstrapper.StartupFactory`1 +example: +- *content +--- +The following example shows how `StartupFactory` activates a `StartupRoot` partner from the host's `IServiceCollection`, configuration, and environment, then surfaces it through the `IStartupFactory.Instance` property. The factory is what `UseBootstrapperStartup` registers, so consumer code resolves the singleton instead of constructing the startup directly. + +```csharp +using Codebelt.Bootstrapper; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace StartupFactoryDemo; + +public class MyStartup : StartupRoot +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } +} + +public static class Program +{ + public static void Main() + { + using var host = Host.CreateDefaultBuilder() + .UseBootstrapperStartup() + .Build(); + + var factory = (StartupFactory)host.Services.GetRequiredService>(); + var startup = factory.Instance; + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.Web.WebProgram`1.md b/.docfx/api/types/Codebelt.Bootstrapper.Web.WebProgram`1.md new file mode 100644 index 0000000..eccad07 --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.Web.WebProgram`1.md @@ -0,0 +1,45 @@ +--- +uid: Codebelt.Bootstrapper.Web.WebProgram`1 +example: +- *content +--- +The following example shows how `WebProgram` is used as the base entry point for a conventional ASP.NET Core web application. The `CreateHostBuilder` helper composes `UseBootstrapperLifetime`, `UseBootstrapperEnvironmentDefaults`, and the built-in `ConfigureWebHostDefaults` so the resulting host wires `WebStartup` through the standard ASP.NET Core convention. The example derives a `Program : WebProgram` so the protected `CreateHostBuilder` is reachable, then builds the host and confirms the bootstrapper's lifetime is registered before letting the host run. + +```csharp +using Codebelt.Bootstrapper; +using Codebelt.Bootstrapper.Web; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace WebProgramDemo; + +public class MyStartup : WebStartup +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } + + public override void ConfigurePipeline(IApplicationBuilder app) + { + app.Run(async context => await context.Response.WriteAsync("Hello World!")); + } +} + +public class Program : WebProgram +{ + public static void Main(string[] args) + { + using var host = CreateHostBuilder(args).Build(); + var lifetime = host.Services.GetRequiredService(); + host.Run(); + } +} +``` diff --git a/.docfx/api/types/Codebelt.Bootstrapper.Worker.WorkerProgram`1.md b/.docfx/api/types/Codebelt.Bootstrapper.Worker.WorkerProgram`1.md new file mode 100644 index 0000000..7b0bc7e --- /dev/null +++ b/.docfx/api/types/Codebelt.Bootstrapper.Worker.WorkerProgram`1.md @@ -0,0 +1,38 @@ +--- +uid: Codebelt.Bootstrapper.Worker.WorkerProgram`1 +example: +- *content +--- +The following example shows how `WorkerProgram` is used as the base entry point for a conventional .NET worker service. The `CreateHostBuilder` helper composes `UseBootstrapperLifetime`, `UseBootstrapperEnvironmentDefaults`, and `UseBootstrapperStartup` so the resulting host wires the `WorkerStartup` partner through the standard bootstrapper convention. The example derives a `Program : WorkerProgram` so the protected `CreateHostBuilder` is reachable, then builds the host and confirms the bootstrapper lifetime and the `IStartupFactory` registration before letting the host run. + +```csharp +using Codebelt.Bootstrapper; +using Codebelt.Bootstrapper.Worker; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace WorkerProgramDemo; + +public class MyStartup : WorkerStartup +{ + public MyStartup(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + } +} + +public class Program : WorkerProgram +{ + public static void Main(string[] args) + { + using var host = CreateHostBuilder(args).Build(); + var lifetime = host.Services.GetRequiredService(); + var factory = host.Services.GetRequiredService>(); + host.Run(); + } +} +``` diff --git a/.docfx/docfx.json b/.docfx/docfx.json index cedda6d..3f4e26e 100644 --- a/.docfx/docfx.json +++ b/.docfx/docfx.json @@ -28,12 +28,13 @@ { "files": [ "api/**/*.yml", - "api/**/*.md", "packages/**/*.md", "toc.yml", "*.md" ], "exclude": [ + "api/namespaces/**", + "api/types/**", "bin/**", "obj/**" ] @@ -70,7 +71,8 @@ "overwrite": [ { "files": [ - "api/namespaces/**.md" + "api/namespaces/**/*.md", + "api/types/**/*.md" ], "exclude": [ "obj/**", diff --git a/.github/workflows/ci-pipeline.yml b/.github/workflows/ci-pipeline.yml index 780c9b9..111bea9 100644 --- a/.github/workflows/ci-pipeline.yml +++ b/.github/workflows/ci-pipeline.yml @@ -212,7 +212,8 @@ jobs: security-events: write deploy: - if: github.event_name != 'pull_request' + # Avoid skipped optional jobs (for example disabled macOS matrix runs) from suppressing deployment. + if: ${{ always() && github.event_name != 'pull_request' && needs.build.result == 'success' && needs.pack.result == 'success' && needs.test_qualitygate.result == 'success' && needs.sonarcloud.result == 'success' && needs.codecov.result == 'success' && needs.codeql.result == 'success' }} name: call-nuget needs: [build, pack, test_qualitygate, sonarcloud, codecov, codeql] uses: codebeltnet/jobs-nuget-push/.github/workflows/default.yml@v3 diff --git a/.nuget/Codebelt.Bootstrapper.Console/PackageReleaseNotes.txt b/.nuget/Codebelt.Bootstrapper.Console/PackageReleaseNotes.txt index 2e0ea47..7f18143 100644 --- a/.nuget/Codebelt.Bootstrapper.Console/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Bootstrapper.Console/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 5.1.2 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 5.1.1 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt b/.nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt index a9325a1..cb589f6 100644 --- a/.nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 5.1.2 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 5.1.1 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Bootstrapper.Worker/PackageReleaseNotes.txt b/.nuget/Codebelt.Bootstrapper.Worker/PackageReleaseNotes.txt index 8ddbac4..b9d0178 100644 --- a/.nuget/Codebelt.Bootstrapper.Worker/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Bootstrapper.Worker/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 5.1.2 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 5.1.1 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Bootstrapper/PackageReleaseNotes.txt b/.nuget/Codebelt.Bootstrapper/PackageReleaseNotes.txt index dafacb3..d9b48de 100644 --- a/.nuget/Codebelt.Bootstrapper/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Bootstrapper/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 5.1.2 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 5.1.1 Availability: .NET 10 and .NET 9 diff --git a/AGENTS.md b/AGENTS.md index 92a400c..387f64d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -69,8 +69,62 @@ Agents must never automatically commit code changes or push to remote repositori **Rationale:** Automatic commits can clutter history with incomplete work, temporary debugging code, or unintended changes. Unexpected remote operations risk overwriting or losing commits on shared branches. Always require explicit user approval before performing these actions. -## Official Documentation + +## DocFX Documentation Maintenance -- Public API conventions belong in `.docfx/api/namespaces/` and should be treated as the official documentation source for library behavior and naming vocabulary. -- When adding or renaming public APIs, update the relevant namespace page in `.docfx/api/namespaces/` if the change introduces or clarifies a convention. -- Keep internal reasoning, exploratory notes, and agent discussion out of DocFX pages; summarize only stable public guidance. +When changing public .NET APIs, keep the DocFX documentation current in the same change set. + +Documentation updates must cover public API only. Do not document private or internal types or members. Do not create namespace overview pages for namespaces that contain no public API. + +Public non-abstraction types — including enums, structs, records, plain classes, and static extension containers — are valid documentation targets. Generic public types and generic extension methods are valid documentation targets too. Do not exclude a type solely because it is generic or because reflection reports it as abstract and sealed (that is the IL pattern for a static class). + +For public non-abstraction types, include at least one realistic, copy/paste-ready usage example on the generated type page/overwrite section for that type UID. For example, a public `Class1` requires an example on the `Class1` API page, not only on the namespace page. Prefer deriving examples from existing unit, functional, or integration tests, but convert test code into real-life consumer-oriented usage. + +Missing type examples must be added through per-type DocFX overwrite files under `.docfx/api/types/{TypeUid}.md` in Codebelt repositories. Namespace overview text and `Extension Members` tables are not substitutes for type-page examples. + +Public extension methods must have examples too. Listing an extension method in an `Extension Members` table is required, but it is not enough. + +All added or changed code samples must be deterministic and verified to compile. Do not add pseudo-code, ellipses, hidden test helpers, or examples that rely on unverified behavior. + +Compilation is necessary but not sufficient. Do not present runtime implementation names such as `services.GetType().Name` or `host.GetType().FullName` as the example outcome. Show application behavior, configured state, a resolved domain service, an HTTP response, or another result that explains why a caller uses the API. Application-entry-point examples must not declare an empty local `Program` type merely to compile; show a real entry point or clearly identify the referenced application project. + +Every namespace containing public API must have a DocFX namespace overview page named after the namespace, such as `X.Y.Z.md`, under `.docfx/api/namespaces/`, using DocFX overwrite front matter with the namespace `uid`. + +Namespace pages must identify key entry points from release notes, package documentation, public factories/builders, and strong functional tests, then help readers choose among adjacent workflows. When the package complements a well-known upstream API, compare concrete acquisition, customization, lifecycle, and sharing tradeoffs from current official guidance; do not claim drop-in replacement compatibility without evidence. + +Namespaces exposing public extension methods must document those extension members at namespace level. The namespace page must include an `Extension Members` table listing the extended type, the extension marker, and the public extension methods. Extension members are rendered under the heading `Extension Members`. + +Both namespace overwrite files and type overwrite files are required deliverables in the same run. Generating only namespace pages or only type pages is incomplete. + +`docfx.json` must keep namespace and type overwrite files in separate subdirectories. `build.overwrite` must include both `api/namespaces/**/*.md` (for namespace pages) and `api/types/**/*.md` (for type pages). `build.content` must exclude both `api/namespaces/**` and `api/types/**` to prevent overwrite Markdown from being treated as conceptual content. Do not use `api/**/*.md` under `build.overwrite` or `build.content`. + +Availability must be documented by referencing the appropriate include file when one exists, or by adding explicit availability text when no suitable include exists. Availability must reflect the actual target frameworks, conditional compilation, and project configuration. + +For conditionally compiled APIs, choose the executable test framework from the asset that contains the API. Inspect the preprocessor condition, project TFMs, package `lib/` assets, and resolved consumer asset before changing a sample. For APIs under `NETSTANDARD2_0` or `NETSTANDARD2_0_OR_GREATER`, when modern `lib/netX.0/` assets also exist, use `net48` (or another supported .NET Framework target from `net462` onward) so the consumer selects `lib/netstandard2.0/`. Never use `netstandard*` as an executable target, and never use a modern `netX.0` target when it selects an asset where the API is absent. For other TFM guards, select a runnable consumer TFM that resolves to the containing asset and confirm that selection from restore or build evidence. + +Preserve manual documentation edits. Prefer additive changes, but correct stale or contradictory information so documentation remains accurate. + +Preserve working Markdown links, `Related:` references, and historical URL citations during prose rewrites. Remove or replace a URL only after directly verifying that the current destination returns HTTP 404. Timeouts, 403s, rate limits, DNS failures, and other lookup problems are not removal evidence. + +Interim scratch artifacts do not belong in the repository working tree. Store assessment queues, project manifests, review reports, captured validator output, progress notes, and one-off helper scripts in temp or session storage instead. New working-tree files are only legitimate when they are the managed `AGENTS.md` block, the active `docfx.json`, the deterministic `skip-compile-allowlist.json` waiver file when one is truly required, or DocFX-authored namespace/type Markdown that maps to a real public namespace or type. Everything else is blocking cleanup work, not a documentation deliverable. The validator auto-detects generic-arity type families (such as `MutableTuple`1`..`MutableTuple`N`) and skips redundant sibling examples from the public API surface alone, so no family-skip manifest is ever written into the repository. + +Skip markers are waivers, not fixes. A skip marker only suppresses compilation when it both existed before the current run and matches an entry in `.docfx/skip-compile-allowlist.json`. Each allowlist entry must include `diagnosticCode`, `filePath`, `uid` or `symbol`, `reason`, `approval`, and `lifetime` (`temporary` or `permanent`). Newly introduced or unallowlisted skip markers remain fail-level diagnostics and do not permit a completion claim. + +Do not emit a final report, audit result, completion summary, or handoff while `summary.canClaimCompletion` is false, `summary.remainingWorkItems` is greater than zero, `summary.remainingGates` is non-empty, `summary.fullVerificationRan` is false, fail-level diagnostics remain, `summary.newlyIntroducedSkipMarkers` is non-zero, or `summary.interimArtifacts` is non-zero. Large queues, many changed files, repetitive next steps, long runtimes, context pressure, session length, task size, or a "stable queue" are not valid stop reasons; the next action must be another remediation batch, a validator rerun, a validator/tooling fix, or a true blocker with exact evidence. + +Context pressure is not a completion condition. If the session feels constrained while work remains, continue with a smaller deterministic batch, regenerate deterministic queue state such as `--assessment-queue`, `--project-manifest`, or the active dry-run manifest/review pair, or report a true tooling failure with the exact command, exit code, and output. When naming a queue-state regeneration command, resolve it to a concrete temp/session path instead of leaving `` as a placeholder. Do not stop with phrases like "given context constraints", "best done in a follow-up", "remaining work requires authoring", "this is a massive task", or "I will provide a focused summary". A context-sized handoff while work remains is `FAIL_CONTEXT_HANDOFF_WITH_REMAINING_WORK`; the remediation is to continue with a smaller deterministic batch. + +Before completing documentation work, run the relevant verification commands, normally: + +```bash +dotnet build +dotnet test +dotnet run --file /scripts/docfx.cs -- --repo-root . --build-api-model --validate-samples --verify-docfx-build +``` + +Codebelt repositories are normally strong-name signed with a `.snk` file in the repository root on the main author's codespace. Preserve and copy that root `.snk` file when building a temporary copy. If the repository or temp copy has no root `.snk`, run build and test verification with `-p:SkipSignAssembly=true`, for example `dotnet build -p:SkipSignAssembly=true` and `dotnet test -p:SkipSignAssembly=true`. + +The final DocFX verification must run outside the working tree when possible. The `--verify-docfx-build` option copies the repository to a temp workspace, runs DocFX against the resolved `docfx.json` there, and removes the temp workspace afterward so generated API YAML, manifest files, and site output do not flood git status. Do not call the work complete until the final JSON reports `summary.fullVerificationRan: true`, `summary.canClaimCompletion: true`, `summary.remainingWorkItems: 0`, an empty `summary.remainingGates`, an empty `summary.remainingDiagnosticsByCode`, `summary.newlyIntroducedSkipMarkers: 0`, and `summary.interimArtifacts: 0`. + +If a command cannot be run, report the exact limitation or failure instead of claiming the documentation was verified. + diff --git a/CHANGELOG.md b/CHANGELOG.md index b8c291f..b418722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), For more details, please refer to `PackageReleaseNotes.txt` on a per assembly basis in the `.nuget` folder. +## [5.1.2] - 2026-07-01 + +This is a patch release focused on dependency upgrades for security and stability, comprehensive API documentation enhancements, and CI pipeline robustness improvements. + +### Changed + +- All NuGet dependencies upgraded to latest patch versions: Codebelt.Extensions.Swashbuckle.AspNetCore to 10.2.3, Codebelt.Extensions.Xunit.App to 11.1.1, Cuemon.Core and Cuemon.Extensions.Hosting to 10.5.4, Microsoft.NET.Test.Sdk to 18.7.0, and all Microsoft.AspNetCore and Microsoft.Extensions packages for net9 (9.0.17) and net10 (10.0.9) to latest patch releases, +- DocFX configuration refactored to separate type/namespace overwrite files from conceptual content while maintaining inclusion in build process, +- AGENTS.md expanded with comprehensive DocFX maintenance section covering type/namespace documentation requirements, realistic code examples, TFM-selection patterns, verification workflows, and completion gates, +- DocFX nginx base image bumped to 1.31.2. + +### Fixed + +- CI deployment job conditional refined to use explicit job-result checks (`always() + needs..result == 'success'`) instead of simple skip predicates, ensuring skipped optional jobs (such as disabled macOS matrix) do not suppress deployment while keeping failures in required jobs from proceeding. + ## [5.1.1] - 2026-06-06 This is a service update that focuses on package dependencies. @@ -248,7 +263,9 @@ Highlighted features included in this release: - WorkerProgram class in the Codebelt.Bootstrapper.Worker namespace that is the base entry point of an application responsible for registering its WorkerStartup partner - WorkerStartup interface in the Codebelt.Bootstrapper.Worker namespace that provides the base class of a conventional based Startup class for a console application -[Unreleased]: https://github.com/codebeltnet/bootstrapper/compare/v5.1.0...HEAD +[Unreleased]: https://github.com/codebeltnet/bootstrapper/compare/v5.1.2...HEAD +[5.1.2]: https://github.com/codebeltnet/bootstrapper/compare/v5.1.1...v5.1.2 +[5.1.1]: https://github.com/codebeltnet/bootstrapper/compare/v5.1.0...v5.1.1 [5.1.0]: https://github.com/codebeltnet/bootstrapper/compare/v5.0.7...v5.1.0 [5.0.7]: https://github.com/codebeltnet/bootstrapper/compare/v5.0.6...v5.0.7 [5.0.6]: https://github.com/codebeltnet/bootstrapper/compare/v5.0.5...v5.0.6 diff --git a/Directory.Packages.props b/Directory.Packages.props index 992876c..c2beb68 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -3,11 +3,11 @@ true - - - - - + + + + + @@ -16,17 +16,17 @@ - - - - - + + + + + - - - - - + + + + + \ No newline at end of file