Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .docfx/Dockerfile.docfx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
FROM --platform=$BUILDPLATFORM nginx:${NGINX_VERSION} AS base
RUN rm -rf /usr/share/nginx/html/*

FROM --platform=$BUILDPLATFORM codebeltnet/docfx:2.78.3 AS build
FROM --platform=$BUILDPLATFORM codebeltnet/docfx:2.78.4 AS build

ADD [".", "docfx"]

Expand Down
583 changes: 583 additions & 0 deletions .github/copilot-instructions.md

Large diffs are not rendered by default.

File renamed without changes.
12 changes: 11 additions & 1 deletion .nuget/Codebelt.Bootstrapper.Console/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
Version 5.0.0
Version 5.0.1
Availability: .NET 10 and .NET 9

# ALM
- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs)

# Bug Fixes
- FIXED ConsoleHostedService class in the Codebelt.Bootstrapper.Console namespace to properly handle suppression of host lifetime status messages when configured to do so
- FIXED MinimalConsoleHostedService class in the Codebelt.Bootstrapper.Console namespace to properly handle suppression of host lifetime status messages when configured to do so

Version 5.0.0
Availability: .NET 10 and .NET 9

# ALM
Expand Down
8 changes: 7 additions & 1 deletion .nuget/Codebelt.Bootstrapper.Web/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
Version 5.0.0
Version 5.0.1
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.0.0
Availability: .NET 10 and .NET 9

# ALM
Expand Down
8 changes: 7 additions & 1 deletion .nuget/Codebelt.Bootstrapper.Worker/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
Version 5.0.0
Version 5.0.1
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.0.0
Availability: .NET 10 and .NET 9

# ALM
Expand Down
8 changes: 7 additions & 1 deletion .nuget/Codebelt.Bootstrapper/PackageReleaseNotes.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
Version 5.0.0
Version 5.0.1
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.0.0
Availability: .NET 10 and .NET 9

# ALM
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ 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.0.1] - 2025-12-07

This is a service update that primarily focuses on fixing issues related to host lifetime status message suppression.

### Fixed

- ConsoleHostedService class in the Codebelt.Bootstrapper.Console namespace to properly handle suppression of host lifetime status messages when configured to do so
- MinimalConsoleHostedService class in the Codebelt.Bootstrapper.Console namespace to properly handle suppression of host lifetime status messages when configured to do so

## [5.0.0] - 2025-11-14

This is a major release that focuses on adapting the latest `.NET 10` release (LTS) in exchange for current `.NET 8` (LTS).
Expand Down
10 changes: 5 additions & 5 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Codebelt.Extensions.Swashbuckle.AspNetCore" Version="10.0.0" />
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="11.0.0" />
<PackageVersion Include="Cuemon.Core" Version="10.0.0" />
<PackageVersion Include="Cuemon.Extensions.Hosting" Version="10.0.0" />
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="11.0.2" />
<PackageVersion Include="Cuemon.Core" Version="10.1.0" />
<PackageVersion Include="Cuemon.Extensions.Hosting" Version="10.1.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.4" />
<PackageVersion Include="xunit.v3" Version="3.1.0" />
<PackageVersion Include="xunit.v3.runner.console" Version="3.2.0" />
<PackageVersion Include="xunit.v3" Version="3.2.1" />
<PackageVersion Include="xunit.v3.runner.console" Version="3.2.1" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
<ItemGroup Condition="$(TargetFramework.StartsWith('net9'))">
Expand Down
13 changes: 8 additions & 5 deletions src/Codebelt.Bootstrapper.Console/ConsoleHostedService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Options;

namespace Codebelt.Bootstrapper.Console
{
Expand All @@ -22,6 +23,7 @@
private bool _ranToCompletion;
private Task _runAsyncTask;
private readonly IHostLifetimeEvents _events;
private readonly bool _suppressStatusMessages;

/// <summary>
/// Initializes a new instance of the <see cref="ConsoleHostedService{TStartup}" /> class.
Expand All @@ -36,6 +38,7 @@
_applicationLifetime = applicationLifetime;
_provider = provider;
_events = events;
_suppressStatusMessages = provider.GetService<IOptions<ConsoleLifetimeOptions>>()?.Value.SuppressStatusMessages ?? false;
}

/// <summary>
Expand All @@ -43,7 +46,7 @@
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="Task" /> that represents the asynchronous operation.</returns>
public Task StartAsync(CancellationToken cancellationToken)

Check warning on line 49 in src/Codebelt.Bootstrapper.Console/ConsoleHostedService.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 17 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check failure on line 49 in src/Codebelt.Bootstrapper.Console/ConsoleHostedService.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 17 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=bootstrapper&issues=AZr5bbte3o0ylQCwyjCg&open=AZr5bbte3o0ylQCwyjCg&pullRequest=20
{
_logger = _provider.GetRequiredService<ILogger<TStartup>>();
var startup = _factory.Instance;
Expand All @@ -56,13 +59,13 @@
{
try
{
Decorator.EncloseToExpose(_logger, false).RunAsyncStarted();
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false).RunAsyncStarted(); }
await startup.RunAsync(_provider, cancellationToken).ConfigureAwait(false);
_ranToCompletion = true;
}
catch (Exception e)
{
Decorator.EncloseToExpose(_logger, false).FatalErrorActivating(typeof(TStartup).FullName, e);
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false).FatalErrorActivating(typeof(TStartup).FullName, e); }
}
}, cancellationToken);

Expand All @@ -71,7 +74,7 @@
}
else
{
Decorator.EncloseToExpose(_logger, false).UnableToActivateInstance(typeof(TStartup).FullName);
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false).UnableToActivateInstance(typeof(TStartup).FullName); }
}

return Task.CompletedTask;
Expand All @@ -92,11 +95,11 @@
{
if (!_ranToCompletion)
{
Decorator.EncloseToExpose(_logger, false)?.RunAsyncPrematureEnd();
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false)?.RunAsyncPrematureEnd(); }
}
else
{
Decorator.EncloseToExpose(_logger, false)?.RunAsyncCompleted();
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false)?.RunAsyncCompleted(); }
}

return Task.CompletedTask;
Expand Down
13 changes: 8 additions & 5 deletions src/Codebelt.Bootstrapper.Console/MinimalConsoleHostedService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Threading;
using System.Threading.Tasks;
using Cuemon;
using Microsoft.Extensions.Options;

namespace Codebelt.Bootstrapper.Console
{
Expand All @@ -21,6 +22,7 @@
private bool _ranToCompletion;
private Task _runAsyncTask;
private readonly IHostLifetimeEvents _events;
private readonly bool _suppressStatusMessages;

/// <summary>
/// Initializes a new instance of the <see cref="MinimalConsoleHostedService"/> class.
Expand All @@ -35,6 +37,7 @@
_applicationLifetime = applicationLifetime;
_provider = provider;
_events = events;
_suppressStatusMessages = provider.GetService<IOptions<ConsoleLifetimeOptions>>()?.Value.SuppressStatusMessages ?? false;
}

/// <summary>
Expand All @@ -42,7 +45,7 @@
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
/// <returns>A <see cref="Task" /> that represents the asynchronous operation.</returns>
public Task StartAsync(CancellationToken cancellationToken)

Check warning on line 48 in src/Codebelt.Bootstrapper.Console/MinimalConsoleHostedService.cs

View workflow job for this annotation

GitHub Actions / call-sonarcloud / 🔬 Code Quality Analysis

Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed. (https://rules.sonarsource.com/csharp/RSPEC-3776)

Check failure on line 48 in src/Codebelt.Bootstrapper.Console/MinimalConsoleHostedService.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this method to reduce its Cognitive Complexity from 19 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=bootstrapper&issues=AZr5bbre3o0ylQCwyjCf&open=AZr5bbre3o0ylQCwyjCf&pullRequest=20
{
_events.OnApplicationStartedCallback += () =>
{
Expand All @@ -58,18 +61,18 @@
{
if (program != null)
{
Decorator.EncloseToExpose(_logger, false).RunAsyncStarted();
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false).RunAsyncStarted(); }
await program.RunAsync(_provider, cancellationToken).ConfigureAwait(false);
_ranToCompletion = true;
}
else
{
Decorator.EncloseToExpose(_logger, false).UnableToActivateInstance(programType.FullName);
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false).UnableToActivateInstance(programType.FullName); }
}
}
catch (Exception e)
{
Decorator.EncloseToExpose(_logger, false).FatalErrorActivating(programType.FullName, e);
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false).FatalErrorActivating(programType.FullName, e); }
}
}, cancellationToken);

Expand All @@ -94,11 +97,11 @@
{
if (!_ranToCompletion)
{
Decorator.EncloseToExpose(_logger, false)?.RunAsyncPrematureEnd();
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false)?.RunAsyncPrematureEnd(); }
}
else
{
Decorator.EncloseToExpose(_logger, false)?.RunAsyncCompleted();
if (!_suppressStatusMessages) { Decorator.EncloseToExpose(_logger, false)?.RunAsyncCompleted(); }
}

return Task.CompletedTask;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace Codebelt.Bootstrapper.Console.Assets
{
/// <summary>
/// A test implementation of <see cref="IProgramFactory"/> that returns null to simulate program activation failure.
/// </summary>
public class NullProgramFactory : IProgramFactory
{
/// <summary>
/// Gets the instance of the program, which is always null for testing purposes.
/// </summary>
/// <value>Always returns <c>null</c>.</value>
public MinimalConsoleProgram Instance => null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Codebelt.Bootstrapper.Console.Assets
{
/// <summary>
/// Test console startup class that simulates an exception scenario during RunAsync execution.
/// </summary>
public class TestConsoleStartupWithException : ConsoleStartup
{
/// <summary>
/// Initializes a new instance of the <see cref="TestConsoleStartupWithException"/> class.
/// </summary>
/// <param name="configuration">The dependency injected <see cref="IConfiguration" />.</param>
/// <param name="environment">The dependency injected <see cref="IHostEnvironment" />.</param>
public TestConsoleStartupWithException(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment)
{
}

/// <summary>
/// Configures services for the console application.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
public override void ConfigureServices(IServiceCollection services)
{
}

/// <summary>
/// Executes the console application logic and throws an exception to simulate an error scenario.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to retrieve services from.</param>
/// <param name="cancellationToken">Indicates that the run has been aborted.</param>
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">Thrown intentionally to simulate an error during execution.</exception>
public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
{
var logger = serviceProvider.GetRequiredService<ILogger<TestConsoleStartupWithException>>();
logger.LogTrace($"Inside {nameof(RunAsync)} of {nameof(TestConsoleStartupWithException)}.");

// Throw exception to simulate fatal error during execution
throw new InvalidOperationException("Simulated exception in RunAsync for testing purposes.");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Codebelt.Bootstrapper.Console.Assets
{
/// <summary>
/// Test console startup class that simulates a premature end scenario by stopping the application before RunAsync completes.
/// </summary>
public class TestConsoleStartupWithPrematureEnd : ConsoleStartup
{
/// <summary>
/// Initializes a new instance of the <see cref="TestConsoleStartupWithPrematureEnd"/> class.
/// </summary>
/// <param name="configuration">The dependency injected <see cref="IConfiguration" />.</param>
/// <param name="environment">The dependency injected <see cref="IHostEnvironment" />.</param>
public TestConsoleStartupWithPrematureEnd(IConfiguration configuration, IHostEnvironment environment) : base(configuration, environment)
{
}

/// <summary>
/// Configures services for the console application.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
public override void ConfigureServices(IServiceCollection services)
{
}

/// <summary>
/// Executes the console application logic with a premature end by requesting application stop.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to retrieve services from.</param>
/// <param name="cancellationToken">Indicates that the run has been aborted.</param>
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
public override async Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
{
var logger = serviceProvider.GetRequiredService<ILogger<TestConsoleStartupWithPrematureEnd>>();
var lifetime = serviceProvider.GetRequiredService<IHostApplicationLifetime>();

logger.LogTrace($"Inside {nameof(RunAsync)} of {nameof(TestConsoleStartupWithPrematureEnd)}.");

// Simulate premature end by stopping the application immediately
lifetime.StopApplication();

// This delay will never complete because the application stops
await Task.Delay(Timeout.Infinite, cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace Codebelt.Bootstrapper.Console.Assets
{
/// <summary>
/// Test minimal console program that simulates an exception scenario during RunAsync execution.
/// </summary>
public class TestMinimalConsoleProgramWithException : MinimalConsoleProgram
{
/// <summary>
/// Executes the console application logic and throws an exception to simulate an error scenario.
/// </summary>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to retrieve services from.</param>
/// <param name="cancellationToken">Indicates that the run has been aborted.</param>
/// <returns>A <see cref="Task"/> that represents the asynchronous operation.</returns>
/// <exception cref="InvalidOperationException">Thrown intentionally to simulate an error during execution.</exception>
public override Task RunAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken)
{
var logger = serviceProvider.GetRequiredService<ILogger<TestMinimalConsoleProgramWithException>>();
logger.LogTrace($"Inside {nameof(RunAsync)} of {nameof(TestMinimalConsoleProgramWithException)}.");

// Throw exception to simulate fatal error during execution
throw new InvalidOperationException("Simulated exception in RunAsync for testing purposes.");
}
}
}
Loading
Loading