Skip to content

Improve/debug release-it configuration #26

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 23 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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 [email protected]

- name: install release-it with plugins
run: npm install -g release-it @release-it/keep-a-changelog
run: npm install -g [email protected] @release-it/[email protected]

- 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