Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
129bc52
add debugging to release-it in CI
neoscie Jul 11, 2025
2eae61b
debug
neoscie Jul 11, 2025
7c07402
debug
neoscie Jul 11, 2025
643e03b
debug
neoscie Jul 11, 2025
3fd4446
debug
neoscie Jul 11, 2025
b074519
debug
neoscie Jul 11, 2025
eecd20d
debug
neoscie Jul 11, 2025
e221e82
Fix missing newline in workflow script
neoscie Jul 11, 2025
a422d1e
Try older release-it version and remove DEBUG/prettier hook
neoscie Jul 11, 2025
08890ab
Test with basic release-it only (no plugins)
neoscie Jul 11, 2025
81512be
Try release-it@16.10.1 with explicit GitHub release config
neoscie Jul 11, 2025
6a12c33
Fix version to use existing release-it@16.3.0
neoscie Jul 11, 2025
557e5b6
Add keep-a-changelog plugin with conditional behavior for pre-release…
neoscie Jul 11, 2025
c598037
Fix releaseNotes configuration - remove auto directive
neoscie Jul 11, 2025
24ddfe1
Improves fallback logging configuration
neoscie Jul 11, 2025
a3ef2ed
re-adding pre-release hook
neoscie Jul 11, 2025
a2392a8
Initializes logger within builder
neoscie Jul 11, 2025
e6ba151
Separates logger setup from retrieval.
neoscie Jul 11, 2025
00dd40c
Log Info message if check-deps ran successfully
neoscie Jul 14, 2025
c347f8e
Adds integration tests
neoscie Jul 14, 2025
61ac89b
Refactors integration tests for better reliability
neoscie Jul 14, 2025
6a23de4
Splits tests into unit and integration tests
neoscie Jul 14, 2025
77095ec
Fixed loading of `appsettings.Development.json` during `check-deps` runs
neoscie Jul 16, 2025
299d8fa
Makes Initialize method public for external access
neoscie Feb 13, 2026
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
7 changes: 5 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,8 @@ jobs:
- name: Run check-deps in Demo Application
run: dotnet run --project Neolution.DotNet.Console.Demo --no-build --configuration '${{ env.BUILD_CONFIGURATION }}' -- check-deps

- name: Test
run: dotnet test --no-build --verbosity normal --configuration '${{ env.BUILD_CONFIGURATION }}'
- name: Unit Tests
run: dotnet test --no-build --verbosity normal --configuration '${{ env.BUILD_CONFIGURATION }}' --filter FullyQualifiedName~UnitTests

- name: Integration Tests
run: dotnet test --no-build --verbosity normal --configuration '${{ env.BUILD_CONFIGURATION }}' --filter FullyQualifiedName~IntegrationTests
42 changes: 39 additions & 3 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,39 @@ jobs:
git config user.email release-bot@neolution.ch

- name: install release-it with plugins
run: npm install -g release-it @release-it/keep-a-changelog
run: npm install -g release-it@16.3.0 @release-it/keep-a-changelog@2.1.0

- name: Check Node.js version
run: |
node --version
npm --version
echo "=== PACKAGE.JSON DEBUG ==="
if [ -f package.json ]; then
echo "package.json exists:"
cat package.json
else
echo "No package.json found"
fi
echo "=== RELEASE-IT CONFIG DEBUG ==="
if [ -f .release-it.json ]; then
echo ".release-it.json contents:"
cat .release-it.json
else
echo "No .release-it.json found"
fi
echo "================================"

- name: run release-it
run: |
echo "=== WORKFLOW INPUTS DEBUG ==="
echo "bump_version_number: '${{ github.event.inputs.bump_version_number }}'"
echo "versioning_phase: '${{ github.event.inputs.versioning_phase }}'"
echo "is_dry_run: '${{ github.event.inputs.is_dry_run }}'"
echo "GITHUB_TOKEN (first 10 chars): ${GITHUB_TOKEN:0:10}..."
echo "Node.js version: $(node --version)"
echo "release-it version: $(release-it --version)"
echo "================================"

params=()

if [[ ${{ github.event.inputs.bump_version_number }} != "consecutive" ]]; then
Expand All @@ -62,17 +91,24 @@ jobs:

if [[ ${{ github.event.inputs.versioning_phase }} != "stable" ]]; then
params+=(--preRelease=${{ github.event.inputs.versioning_phase }})
params+=(--plugins.@release-it/keep-a-changelog.keepUnreleased)
params+=(--no-plugins.@release-it/keep-a-changelog.strictLatest)
fi

if [[ ${{ github.event.inputs.is_dry_run }} == "true" ]]; then
params+=(--dry-run)
fi

params+=(--ci)
params+=(--verbose)

echo "=== RELEASE-IT EXECUTION ==="
echo "command: release-it ${params[@]}"
echo "Working directory: $(pwd)"
echo "Git status:"
git status --porcelain
echo "Git tags (last 5):"
git tag --sort=-version:refname | head -5
echo "================================"

release-it "${params[@]}"
env:
GITHUB_TOKEN: ${{ steps.generate-token.outputs.token }}
8 changes: 5 additions & 3 deletions .release-it.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@
"skipChecks": true
},
"github": {
"release": true
"release": true,
"releaseName": "Release ${version}"
},
"plugins": {
"@release-it/keep-a-changelog": {
"filename": "CHANGELOG.md",
"addVersionUrl": true,
"strictLatest": false,
"addUnreleased": true,
"strictLatest": false
"head": "Unreleased",
"keepUnreleased": "${preRelease}"
}
},
"hooks": {
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Fixed loading of `appsettings.Development.json` during `check-deps` runs by forcing the Development environment for dependency validation.
- Updated Scrutor to v6.1.0.
- Updated Microsoft.Extensions packages to latest patch versions.

Expand Down
2 changes: 0 additions & 2 deletions Neolution.DotNet.Console.Demo/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ public static async Task Main(string[] args)
try
{
var builder = DotNetConsole.CreateDefaultBuilder(args);
DotNetConsoleLogger.Initialize(builder.Configuration);

var startup = new Startup(builder.Environment, builder.Configuration);
startup.ConfigureServices(builder.Services);
var console = builder.Build();
Expand Down
84 changes: 84 additions & 0 deletions Neolution.DotNet.Console.IntegrationTests/CheckDepsConsoleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
namespace Neolution.DotNet.Console.IntegrationTests
{
using System.Diagnostics;
using System.Threading.Tasks;
using Shouldly;
using Xunit;

/// <summary>
/// Integration tests for the CheckDepsConsole scenario.
/// </summary>
public class CheckDepsConsoleTests : IClassFixture<SolutionDirectoryFixture>
{
/// <summary>
/// The fixture that provides solution and project paths.
/// </summary>
private readonly SolutionDirectoryFixture fixture;

/// <summary>
/// Initializes a new instance of the <see cref="CheckDepsConsoleTests"/> class.
/// </summary>
/// <param name="fixture">The solution directory fixture.</param>
public CheckDepsConsoleTests(SolutionDirectoryFixture fixture)
{
this.fixture = fixture;
}

/// <summary>
/// Given the Demo app, when run with 'check-deps', then it prints the expected DI validation message.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
[Fact]
public async Task GivenDemoApp_WhenRunWithCheckDeps_ThenPrintsDependencyInjectionValidationSucceeded()
{
// Arrange
var restorePsi = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"restore \"{this.fixture.DemoProjectPath}\"",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};

using (var restoreProcess = Process.Start(restorePsi))
{
if (restoreProcess is null)
{
throw new System.InvalidOperationException("Failed to start dotnet restore for Demo app.");
}

await restoreProcess.StandardOutput.ReadToEndAsync();
await restoreProcess.StandardError.ReadToEndAsync();
await restoreProcess.WaitForExitAsync();
restoreProcess.ExitCode.ShouldBe(0, "dotnet restore failed for Demo app");
}

var psi = new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"run --project \"{this.fixture.DemoProjectPath}\" check-deps",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
};

// Act
using var process = Process.Start(psi);
if (process is null)
{
throw new System.InvalidOperationException("Failed to start process for Demo app.");
}

var output = await process.StandardOutput.ReadToEndAsync();
var error = await process.StandardError.ReadToEndAsync();
await process.WaitForExitAsync();

// Assert
output.ShouldContain("Dependency injection validation succeeded. All registered services can be constructed and no DI issues were found.");
process.ExitCode.ShouldBe(0, $"Process exited with code {process.ExitCode}. Error: {error}");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="Neolution.CodeAnalysis.TestsRuleset" Version="3.3.0-beta.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Shouldly" Version="4.3.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
namespace Neolution.DotNet.Console.IntegrationTests
{
using System.IO;

/// <summary>
/// Provides the solution directory and Demo project path for integration tests.
/// </summary>
public class SolutionDirectoryFixture
{
/// <summary>
/// Initializes a new instance of the <see cref="SolutionDirectoryFixture"/> class.
/// </summary>
public SolutionDirectoryFixture()
{
var dir = Directory.GetCurrentDirectory();
while (dir != null && !File.Exists(Path.Combine(dir, "Neolution.DotNet.Console.sln")))
{
dir = Path.GetDirectoryName(dir);
}

// Set the solution directory to the one containing the solution file
this.SolutionDirectory = dir ?? throw new DirectoryNotFoundException("Could not find solution directory.");

// Set the Demo project path relative to the solution directory
this.DemoProjectPath = Path.Combine(this.SolutionDirectory, "Neolution.DotNet.Console.Demo", "Neolution.DotNet.Console.Demo.csproj");
if (!File.Exists(this.DemoProjectPath))
{
throw new FileNotFoundException($"Demo project file not found: {this.DemoProjectPath}");
}
}

/// <summary>
/// Gets the solution directory path.
/// </summary>
public string SolutionDirectory { get; }

/// <summary>
/// Gets the Demo project file path.
/// </summary>
public string DemoProjectPath { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class DotNetConsoleBuilderTests
/// <summary>
/// The argument string for the internal check-deps command
/// </summary>
private const string CheckDependenciesArgumentString = "check-deps";
private const string CheckDependenciesArgumentString = DotNetConsoleDefaults.CheckDependenciesCommand;

/// <summary>
/// Given a mistyped verb, when a default verb is defined, then should throw on console building.
Expand Down
6 changes: 6 additions & 0 deletions Neolution.DotNet.Console.sln
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Neolution.DotNet.Console.Un
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neolution.DotNet.Console.Demo", "Neolution.DotNet.Console.Demo\Neolution.DotNet.Console.Demo.csproj", "{3F17699A-2864-0EEC-AC50-93648D6E5BDE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Neolution.DotNet.Console.IntegrationTests", "Neolution.DotNet.Console.IntegrationTests\Neolution.DotNet.Console.IntegrationTests.csproj", "{8C6D9105-CEF3-66FD-11A3-73BFF67273F6}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -40,6 +42,10 @@ Global
{3F17699A-2864-0EEC-AC50-93648D6E5BDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3F17699A-2864-0EEC-AC50-93648D6E5BDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3F17699A-2864-0EEC-AC50-93648D6E5BDE}.Release|Any CPU.Build.0 = Release|Any CPU
{8C6D9105-CEF3-66FD-11A3-73BFF67273F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8C6D9105-CEF3-66FD-11A3-73BFF67273F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8C6D9105-CEF3-66FD-11A3-73BFF67273F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8C6D9105-CEF3-66FD-11A3-73BFF67273F6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
23 changes: 16 additions & 7 deletions Neolution.DotNet.Console/DotNetConsoleBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,14 @@ public IDotNetConsole Build()

if (this.checkDependencies)
{
// Use development environment before building because that's where ValidateScopes and ValidateOnBuild are enabled.
// Ensure development environment is used for dependency checking
// Note: The environment should already be set to Development during CreateConsoleEnvironment
// but we explicitly set it here as well to ensure ValidateScopes and ValidateOnBuild are enabled.
this.hostBuilder.UseEnvironment("Development");
this.hostBuilder.Build();

// If build was successful and did not throw an exception, return a console that does nothing and then terminates.
return new NoOperationConsole();
// If build was successful and did not throw an exception, return a console that logs a success message and then terminates.
return new CheckDepsConsole();
}

var host = this.hostBuilder.Build();
Expand All @@ -113,13 +115,19 @@ internal static DotNetConsoleBuilder CreateBuilderInternal(Assembly assembly, Ty
var environment = DotNetConsoleDefaults.CreateConsoleEnvironment(args);
var configuration = DotNetConsoleDefaults.CreateConsoleConfiguration(assembly, args, environment);

// Create a HostBuilder
// Initialize NLog logger from configuration with fallback; any config errors are handled in Initialize
DotNetConsoleLogger.Initialize(configuration);

// Create a HostBuilder and configure logging and services
var builder = Host.CreateDefaultBuilder(args)
.UseContentRoot(environment.ContentRootPath)
.ConfigureLogging((context, logging) =>
.ConfigureLogging((_, logging) =>
{
// Remove default providers and add core providers for debug and event sources
AdjustDefaultBuilderLoggingProviders(logging);
logging.AddNLog(context.Configuration);

// Add NLog provider using existing LogManager configuration
logging.AddNLog();
})
.ConfigureServices((_, services) =>
{
Expand All @@ -137,7 +145,8 @@ internal static DotNetConsoleBuilder CreateBuilderInternal(Assembly assembly, Ty
var parsedArguments = Parser.Default.ParseArguments(args, verbTypes);
var consoleBuilder = new DotNetConsoleBuilder(builder, parsedArguments, environment, configuration);

if (args.Length == 1 && string.Equals(args[0], "check-deps", StringComparison.OrdinalIgnoreCase))
// Determine if this is a check-deps run: only DI validation should run
if (DotNetConsoleDefaults.IsCheckDependenciesRun(args))
{
consoleBuilder.checkDependencies = true;
return consoleBuilder;
Expand Down
25 changes: 24 additions & 1 deletion Neolution.DotNet.Console/DotNetConsoleDefaults.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@
/// </summary>
internal static class DotNetConsoleDefaults
{
/// <summary>
/// The command argument used to trigger dependency validation.
/// </summary>
internal const string CheckDependenciesCommand = "check-deps";

/// <summary>
/// Determines if the given arguments represent a check-deps run.
/// </summary>
/// <param name="args">The command line arguments.</param>
/// <returns>True if this is a check-deps run, false otherwise.</returns>
public static bool IsCheckDependenciesRun(string[] args)
{
return args.Length == 1 && string.Equals(args[0], CheckDependenciesCommand, StringComparison.OrdinalIgnoreCase);
}

/// <summary>
/// Creates the console environment.
/// </summary>
Expand All @@ -27,9 +42,17 @@ internal static DotNetConsoleEnvironment CreateConsoleEnvironment(string[] args)
// The apps root directory is where the appsettings.json are located
var appRootDirectory = AppContext.BaseDirectory;

// Check if this is a check-deps run - if so, always use Development environment
var isCheckDepsRun = IsCheckDependenciesRun(args);

// Default to Production for normal runs, matching ASP.NET Core behavior
// For check-deps, always use Development to ensure appsettings.Development.json is loaded
// Environment can be overridden via DOTNET_ENVIRONMENT or command line arguments
var defaultEnvironment = isCheckDepsRun ? Environments.Development : Environments.Production;

return new DotNetConsoleEnvironment
{
EnvironmentName = configuration[HostDefaults.EnvironmentKey] ?? Environments.Production,
EnvironmentName = isCheckDepsRun ? Environments.Development : (configuration[HostDefaults.EnvironmentKey] ?? defaultEnvironment),
ApplicationName = AppDomain.CurrentDomain.FriendlyName,
ContentRootPath = appRootDirectory,
ContentRootFileProvider = new PhysicalFileProvider(appRootDirectory),
Expand Down
Loading