Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions .github/workflows/pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ on:
- Debug
- Release

permissions:
contents: read

jobs:
build:
name: 🛠️ Build
Expand Down Expand Up @@ -130,13 +133,18 @@ jobs:
name: call-codeql
needs: [build,test]
uses: codebeltnet/jobs-codeql/.github/workflows/default.yml@v1
permissions:
security-events: write

deploy:
if: github.event_name != 'pull_request'
name: 🚀 Deploy v${{ needs.build.outputs.version }}
runs-on: ubuntu-24.04
needs: [build, pack, test, sonarcloud, codecov, codeql]
environment: Production
permissions:
contents: write
packages: write
steps:
- uses: codebeltnet/nuget-push@v1
with:
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ 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.

## [4.0.0] - TBD
## [4.0.0] - 2025-04-12

This major release revisits and refines some of the earlier design decisions to offer a more consistent and flexible API. It also brings forward improvements to reliability and maintainability.

Expand Down
3 changes: 3 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly Condition="('$(CI)' == 'true' OR '$(IsMainAuthor)' == 'true') AND '$(SkipSignAssembly)' == 'false'">true</SignAssembly>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)bootstrapper.snk</AssemblyOriginatorKeyFile>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<AnalysisLevel>latest</AnalysisLevel>
<AnalysisMode>Recommended</AnalysisMode>
</PropertyGroup>

<ItemGroup Condition="'$(NuGetPackageRoot)' != ''">
Expand Down
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Codebelt.Extensions.Swashbuckle.AspNetCore" Version="9.0.1" />
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="10.0.0-preview.3" />
<PackageVersion Include="Codebelt.Extensions.Xunit.App" Version="10.0.0" />
<PackageVersion Include="Cuemon.Core" Version="9.0.4" />
<PackageVersion Include="Cuemon.Extensions.Hosting" Version="9.0.4" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"profiles": {
"Codebelt.Bootstrapper.WebApp.App": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "LocalDevelopment",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
}
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "https://localhost:7173;http://localhost:5249",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "LocalDevelopment",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"profiles": {
"Codebelt.Bootstrapper.WebMvc.App": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "LocalDevelopment",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
}
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "https://localhost:7173;http://localhost:5249",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "LocalDevelopment",
"ASPNETCORE_HOSTINGSTARTUPASSEMBLIES": "Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation"
}
}
}
}
17 changes: 9 additions & 8 deletions src/Codebelt.Bootstrapper.Console/ConsoleHostedService.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Cuemon;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Codebelt.Bootstrapper.Console
{
Expand Down Expand Up @@ -55,13 +56,13 @@
{
try
{
_logger.LogInformation("RunAsync started.");
Decorator.EncloseToExpose(_logger, false).RunAsyncStarted();
await startup.RunAsync(_provider, cancellationToken).ConfigureAwait(false);
_ranToCompletion = true;
}
catch (Exception e)
{
_logger.LogCritical(e, "Fatal error occurred while activating {TypeFullName}.", typeof(TStartup).FullName);
Decorator.EncloseToExpose(_logger, false).FatalErrorActivating(typeof(TStartup).FullName, e);

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

View check run for this annotation

Codecov / codecov/patch

src/Codebelt.Bootstrapper.Console/ConsoleHostedService.cs#L65

Added line #L65 was not covered by tests
}
}, cancellationToken);

Expand All @@ -70,7 +71,7 @@
}
else
{
_logger.LogWarning("Unable to activate an instance of {TypeFullName}.", typeof(TStartup).FullName);
Decorator.EncloseToExpose(_logger, false).UnableToActivateInstance(typeof(TStartup).FullName);

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

View check run for this annotation

Codecov / codecov/patch

src/Codebelt.Bootstrapper.Console/ConsoleHostedService.cs#L74

Added line #L74 was not covered by tests
}

return Task.CompletedTask;
Expand All @@ -91,11 +92,11 @@
{
if (!_ranToCompletion)
{
_logger?.LogInformation("RunAsync ended prematurely.");
Decorator.EncloseToExpose(_logger, false)?.RunAsyncPrematureEnd();
}
else
{
_logger?.LogInformation("RunAsync completed successfully.");
Decorator.EncloseToExpose(_logger, false)?.RunAsyncCompleted();
}

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

namespace Codebelt.Bootstrapper.Console
{
Expand Down Expand Up @@ -57,18 +58,18 @@
{
if (program != null)
{
_logger.LogInformation("RunAsync started.");
Decorator.EncloseToExpose(_logger, false).RunAsyncStarted();
await program.RunAsync(_provider, cancellationToken).ConfigureAwait(false);
_ranToCompletion = true;
}
else
{
_logger.LogWarning("Unable to activate an instance of {TypeFullName}.", programType.FullName);
Decorator.EncloseToExpose(_logger, false).UnableToActivateInstance(programType.FullName);

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

View check run for this annotation

Codecov / codecov/patch

src/Codebelt.Bootstrapper.Console/MinimalConsoleHostedService.cs#L67

Added line #L67 was not covered by tests
}
}
catch (Exception e)
{
_logger.LogCritical(e, "Fatal error occurred while activating {TypeFullName}.", programType.FullName);
Decorator.EncloseToExpose(_logger, false).FatalErrorActivating(programType.FullName, e);

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

View check run for this annotation

Codecov / codecov/patch

src/Codebelt.Bootstrapper.Console/MinimalConsoleHostedService.cs#L72

Added line #L72 was not covered by tests
}
}, cancellationToken);

Expand All @@ -93,11 +94,11 @@
{
if (!_ranToCompletion)
{
_logger?.LogInformation("RunAsync ended prematurely.");
Decorator.EncloseToExpose(_logger, false)?.RunAsyncPrematureEnd();
}
else
{
_logger?.LogInformation("RunAsync completed successfully.");
Decorator.EncloseToExpose(_logger, false)?.RunAsyncCompleted();
}

return Task.CompletedTask;
Expand Down
2 changes: 1 addition & 1 deletion src/Codebelt.Bootstrapper/BootstrapperLifetime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ private void OnApplicationStopping()
}

/// <summary>
/// Called when this object is being disposed by either <see cref="M:Cuemon.Disposable.Dispose" /> or <see cref="M:Cuemon.Disposable.Dispose(System.Boolean)" /> having <c>disposing</c> set to <c>true</c> and <see cref="P:Cuemon.Disposable.Disposed" /> is <c>false</c>.
/// Called when this object is being disposed by either <see cref="Disposable.Dispose()" /> or <see cref="Disposable.Dispose(bool)" /> having <c>disposing</c> set to <c>true</c> and <see cref="Disposable.Disposed" /> is <c>false</c>.
/// </summary>
protected override void OnDisposeManagedResources()
{
Expand Down
73 changes: 73 additions & 0 deletions src/Codebelt.Bootstrapper/BootstrapperLogMessages.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using Cuemon;
using Microsoft.Extensions.Logging;

namespace Codebelt.Bootstrapper
{
/// <summary>
/// Provides centralized logging messages for the Bootstrapper SDK using the LoggerMessage pattern.
/// This API supports the product infrastructure and is not intended to be used directly from your code.
/// </summary>
/// <remarks>https://learn.microsoft.com/en-us/dotnet/core/extensions/high-performance-logging</remarks>
public static class BootstrapperLogMessages
{
// Information messages
private static readonly Action<ILogger, Exception> RunAsyncStartedDefinition = LoggerMessage.Define(LogLevel.Information, new EventId(1000, nameof(RunAsyncStarted)), "RunAsync started.");
private static readonly Action<ILogger, Exception> RunAsyncPrematureEndDefinition = LoggerMessage.Define(LogLevel.Information, new EventId(1001, nameof(RunAsyncPrematureEnd)), "RunAsync ended prematurely.");
private static readonly Action<ILogger, Exception> RunAsyncCompletedDefinition = LoggerMessage.Define(LogLevel.Information, new EventId(1002, nameof(RunAsyncCompleted)), "RunAsync completed successfully.");

// Warning messages
private static readonly Action<ILogger, string, Exception> UnableToActivateInstanceDefinition = LoggerMessage.Define<string>(LogLevel.Warning, new EventId(2000, nameof(UnableToActivateInstance)), "Unable to activate an instance of {TypeFullName}.");

// Critical messages
private static readonly Action<ILogger, string, Exception> FatalErrorActivatingDefinition = LoggerMessage.Define<string>(LogLevel.Critical, new EventId(3000, nameof(FatalErrorActivating)), "Fatal error occurred while activating {TypeFullName}.");

/// <summary>
/// Logs that RunAsync has started.
/// </summary>
/// <param name="logger">The logger instance.</param>
public static void RunAsyncStarted(this IDecorator<ILogger> logger)
{
RunAsyncStartedDefinition(logger.Inner, null);
}

/// <summary>
/// Logs that RunAsync ended prematurely.
/// </summary>
/// <param name="logger">The logger instance.</param>
public static void RunAsyncPrematureEnd(this IDecorator<ILogger> logger)
{
RunAsyncPrematureEndDefinition(logger.Inner, null);
}

Check warning on line 41 in src/Codebelt.Bootstrapper/BootstrapperLogMessages.cs

View check run for this annotation

Codecov / codecov/patch

src/Codebelt.Bootstrapper/BootstrapperLogMessages.cs#L39-L41

Added lines #L39 - L41 were not covered by tests

/// <summary>
/// Logs that RunAsync completed successfully.
/// </summary>
/// <param name="logger">The logger instance.</param>
public static void RunAsyncCompleted(this IDecorator<ILogger> logger)
{
RunAsyncCompletedDefinition(logger.Inner, null);
}

/// <summary>
/// Logs an inability to activate the specified type.
/// </summary>
/// <param name="logger">The logger instance.</param>
/// <param name="typeFullName">The full name of the type that couldn't be activated.</param>
public static void UnableToActivateInstance(this IDecorator<ILogger> logger, string typeFullName)
{
UnableToActivateInstanceDefinition(logger.Inner, typeFullName, null);
}

Check warning on line 60 in src/Codebelt.Bootstrapper/BootstrapperLogMessages.cs

View check run for this annotation

Codecov / codecov/patch

src/Codebelt.Bootstrapper/BootstrapperLogMessages.cs#L58-L60

Added lines #L58 - L60 were not covered by tests

/// <summary>
/// Logs a fatal error that occurred while activating a type.
/// </summary>
/// <param name="logger">The logger instance.</param>
/// <param name="typeFullName">The full name of the type being activated.</param>
/// <param name="exception">The exception that occurred.</param>
public static void FatalErrorActivating(this IDecorator<ILogger> logger, string typeFullName, Exception exception)
{
FatalErrorActivatingDefinition(logger.Inner, typeFullName, exception);
}

Check warning on line 71 in src/Codebelt.Bootstrapper/BootstrapperLogMessages.cs

View check run for this annotation

Codecov / codecov/patch

src/Codebelt.Bootstrapper/BootstrapperLogMessages.cs#L69-L71

Added lines #L69 - L71 were not covered by tests
}
}
Loading