diff --git a/.editorconfig b/.editorconfig
index 20e9dc9..ab32471 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -2,3 +2,7 @@ root = true
[*.cs]
indent_style = tab
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..8ec442b
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,44 @@
+name: .NET Build
+
+on:
+ push:
+ branches: [ "**" ]
+ pull_request:
+ branches: [ "main" ]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+env:
+ SOLUTION_FILE: src/Ninject.Web.AspNetCore.slnx
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ name: Build and Test
+ steps:
+ # Checkout the repository
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ # Setup .NET Core SDK
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Restore dependencies
+ run: dotnet restore $SOLUTION_FILE
+
+ - name: Verify formatting
+ run: dotnet format $SOLUTION_FILE --verify-no-changes
+
+ - name: Build
+ run: dotnet build --no-restore $SOLUTION_FILE
+
+ - name: Test
+ run: dotnet test --no-build $SOLUTION_FILE
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
deleted file mode 100644
index 38612ea..0000000
--- a/.github/workflows/ci.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-name: .NET 10 CI
-on: [push]
-
-jobs:
- build:
- runs-on: ubuntu-latest
- name: Build and Test
- steps:
- - uses: actions/checkout@v5
-
- - uses: actions/setup-dotnet@v5
- name: Setup .NET SDK
- with:
- dotnet-version: '10.0.x'
-
- - name: Build
- run: dotnet build src/Ninject.Web.AspNetCore.sln -c Release
- - name: Test
- run: dotnet test src/Ninject.Web.AspNetCore.sln -c Release
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..35a362f
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,64 @@
+name: NuGet Package Release
+
+on:
+ push:
+ tags:
+ - 'v*.*.*'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: false
+
+permissions:
+ contents: read
+
+env:
+ SOLUTION_FILE: src/Ninject.Web.AspNetCore.slnx
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write # required so the job can request a GitHub OIDC token for NuGet Trusted Publishing
+
+ steps:
+ # Checkout the repository
+ - name: Checkout code
+ uses: actions/checkout@v5
+
+ # Setup .NET Core SDK
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: 10.0.x
+
+ - name: Restore dependencies
+ run: dotnet restore $SOLUTION_FILE
+
+ - name: Verify formatting
+ run: dotnet format $SOLUTION_FILE --verify-no-changes
+
+ - name: Build
+ run: dotnet build --no-restore $SOLUTION_FILE
+
+ - name: Test
+ run: dotnet test --no-build $SOLUTION_FILE
+
+ # Derive the package version from the pushed tag, e.g. refs/tags/v10.0.1 -> 10.0.1
+ - name: Extract version from tag
+ id: version
+ run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
+
+ - name: Pack Ninject.Web.AspNetCore
+ run: dotnet pack $SOLUTION_FILE -c Release --include-symbols -p:SymbolPackageFormat=snupkg -p:Version=${{ steps.version.outputs.version }} --output ./artifacts
+
+ # Exchange the GitHub OIDC token for a short-lived (1 hour) NuGet API key
+ - name: NuGet login (OIDC -> temp API key)
+ uses: NuGet/login@v1
+ id: login
+ with:
+ user: ${{ secrets.NUGET_USER }}
+
+ # Push the packages to NuGet
+ - name: Push to NuGet
+ run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ steps.login.outputs.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
diff --git a/README.md b/README.md
index 0b36fa3..3822074 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[](https://github.com/lord-executor/Ninject.Web.AspNetCore/blob/master/LICENSE) [](https://github.com/lord-executor/Ninject.Web.AspNetCore/actions/workflows/ci.yml)
+[](https://github.com/lord-executor/Ninject.Web.AspNetCore/blob/master/LICENSE) [](https://github.com/lord-executor/Ninject.Web.AspNetCore/actions/workflows/build.yml)
# Overview
diff --git a/doc/publish.ps1 b/doc/publish.ps1
deleted file mode 100644
index a55769c..0000000
--- a/doc/publish.ps1
+++ /dev/null
@@ -1,16 +0,0 @@
-param(
- [Parameter(Mandatory=$true)]
- [string] $apiKey,
- [Parameter(Mandatory=$true)]
- [string] $version = $null
-)
-
-$projects = (
- "Ninject.Web.AspNetCore",
- "Ninject.Web.AspNetCore.IIS",
- "Ninject.Web.AspNetCore.Httpsys"
-)
-
-$projects | ForEach-Object {
- dotnet nuget push "src\$_\bin\Release\$_.$version.nupkg" -k $apiKey -s https://api.nuget.org/v3/index.json
-}
diff --git a/doc/release-process.md b/doc/release-process.md
index f7a163a..524b6d8 100644
--- a/doc/release-process.md
+++ b/doc/release-process.md
@@ -1,18 +1,15 @@
# Release Process
-Build and package the project with the _Release_ configuration.
+Releases are built, packed and published automatically by the [`NuGet Package Release`](../.github/publish.yml) GitHub Actions workflow.
+To ship a release, push a tag matching `v*.*.*` (e.g. `v10.0.1`):
```
-$version = "5.1.x"
-dotnet build .\src\Ninject.Web.AspNetCore.sln -c Release -p:Version="$version"
-dotnet pack .\src\Ninject.Web.AspNetCore.sln -c Release --include-symbols -p:SymbolPackageFormat=snupkg -p:Version="$version"
+git tag v10.0.1
+git push origin v10.0.1
```
-Publish all three packages with the `publish.ps1` script, replacing the API key and versions as needed.
-```
-.\doc\publish.ps1 -apiKey "NuGet APIKey" -version "$version"
-```
+The workflow then:
+1. Restores, builds and tests the solution.
+2. Derives the package version from the tag name (stripping the leading `v`, e.g. `v10.0.1` -> `10.0.1`) and packs all three packages with that version via `-p:Version`.
+3. Exchanges the workflow's GitHub OIDC token for a short-lived NuGet API key ([NuGet Trusted Publishing](https://learn.microsoft.com/en-us/nuget/nuget-org/trusted-publishing)) and pushes the packages to nuget.org.
-With the NuGet API key stored in the Windows credential manager, we can do
-```
-.\doc\publish.ps1 -apiKey $((Read-CredentialsStore -Target "NuGet:Ninject.Web.AspNetCore:APIKey").GetNetworkCredential().Password) -version "$version"
-```
+No manual build, pack or publish steps are required.
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
new file mode 100644
index 0000000..6f7f2fd
--- /dev/null
+++ b/src/Directory.Build.props
@@ -0,0 +1,11 @@
+
+
+
+ true
+ latest
+ Recommended
+ true
+ CA1805,CA1822
+
+
+
diff --git a/src/Ninject.Web.AspNetCore.ComplianceTest/KeyedDependencyInjectionComplianceTests.cs b/src/Ninject.Web.AspNetCore.ComplianceTest/KeyedDependencyInjectionComplianceTests.cs
index 934d3e3..cf9821a 100644
--- a/src/Ninject.Web.AspNetCore.ComplianceTest/KeyedDependencyInjectionComplianceTests.cs
+++ b/src/Ninject.Web.AspNetCore.ComplianceTest/KeyedDependencyInjectionComplianceTests.cs
@@ -1,41 +1,41 @@
-using System;
-using Microsoft.Extensions.DependencyInjection;
-using Ninject.Planning.Bindings.Resolvers;
-using Xunit;
-
-namespace Ninject.Web.AspNetCore.ComplianceTest;
-
-///
-/// See https://github.com/dotnet/runtime/tree/main/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src - the dotnet/runtime
-/// project which contains the dependency injection library code also contains a set of "compliance tests" that can be run against a potential alternative
-/// implementation to check if it is compliant. This class is running the dedicated specification tests for KEYED services.
-///
-public class KeyedDependencyInjectionComplianceTests : Microsoft.Extensions.DependencyInjection.Specification.KeyedDependencyInjectionSpecificationTests
-{
- protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
- {
- var kernel = new AspNetCoreKernel();
- // remove autobinding as CreateServiceWithKeyedParameter e.g. tests that no autobinding happens.
- kernel.Components.Remove();
- var factory = new NinjectServiceProviderFactory(kernel);
-
- return factory.CreateBuilder(serviceCollection).Build();
- }
-
-#pragma warning disable xUnit1024, xUnit1026
-
- [Theory(Skip = "Wrong implementation of the test, should use Assert.Equal and not Assert.Same")]
- [InlineData(true)]
- [InlineData(false)]
- public new void ResolveWithAnyKeyQuery_Constructor(bool anyKeyQueryBeforeSingletonQueries)
- {
- }
-
- [Theory(Skip = "Wrong implementation, should use Assert.Equal and not Assert.Same")]
- [InlineData(true)]
- [InlineData(false)]
- public new void ResolveWithAnyKeyQuery_Constructor_Duplicates(bool anyKeyQueryBeforeSingletonQueries)
- {
- }
-#pragma warning restore xUnit1024, xUnit1026
-}
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Ninject.Planning.Bindings.Resolvers;
+using Xunit;
+
+namespace Ninject.Web.AspNetCore.ComplianceTest;
+
+///
+/// See https://github.com/dotnet/runtime/tree/main/src/libraries/Microsoft.Extensions.DependencyInjection.Specification.Tests/src - the dotnet/runtime
+/// project which contains the dependency injection library code also contains a set of "compliance tests" that can be run against a potential alternative
+/// implementation to check if it is compliant. This class is running the dedicated specification tests for KEYED services.
+///
+public class KeyedDependencyInjectionComplianceTests : Microsoft.Extensions.DependencyInjection.Specification.KeyedDependencyInjectionSpecificationTests
+{
+ protected override IServiceProvider CreateServiceProvider(IServiceCollection collection)
+ {
+ var kernel = new AspNetCoreKernel();
+ // remove autobinding as CreateServiceWithKeyedParameter e.g. tests that no autobinding happens.
+ kernel.Components.Remove();
+ var factory = new NinjectServiceProviderFactory(kernel);
+
+ return factory.CreateBuilder(collection).Build();
+ }
+
+#pragma warning disable xUnit1024, xUnit1026
+
+ [Theory(Skip = "Wrong implementation of the test, should use Assert.Equal and not Assert.Same")]
+ [InlineData(true)]
+ [InlineData(false)]
+ public new void ResolveWithAnyKeyQuery_Constructor(bool anyKeyQueryBeforeSingletonQueries)
+ {
+ }
+
+ [Theory(Skip = "Wrong implementation, should use Assert.Equal and not Assert.Same")]
+ [InlineData(true)]
+ [InlineData(false)]
+ public new void ResolveWithAnyKeyQuery_Constructor_Duplicates(bool anyKeyQueryBeforeSingletonQueries)
+ {
+ }
+#pragma warning restore xUnit1024, xUnit1026
+}
diff --git a/src/Ninject.Web.AspNetCore.ComplianceTest/Ninject.Web.AspNetCore.ComplianceTest.csproj b/src/Ninject.Web.AspNetCore.ComplianceTest/Ninject.Web.AspNetCore.ComplianceTest.csproj
index 61851cf..5d30a1a 100644
--- a/src/Ninject.Web.AspNetCore.ComplianceTest/Ninject.Web.AspNetCore.ComplianceTest.csproj
+++ b/src/Ninject.Web.AspNetCore.ComplianceTest/Ninject.Web.AspNetCore.ComplianceTest.csproj
@@ -3,6 +3,9 @@
net10.0
false
+
+ CA1707,CA1822,CA2263
+
diff --git a/src/Ninject.Web.AspNetCore.Httpsys/Hosting/AspNetCoreHostConfigurationExtensions.cs b/src/Ninject.Web.AspNetCore.Httpsys/Hosting/AspNetCoreHostConfigurationExtensions.cs
index 09624fe..58d3b4f 100644
--- a/src/Ninject.Web.AspNetCore.Httpsys/Hosting/AspNetCoreHostConfigurationExtensions.cs
+++ b/src/Ninject.Web.AspNetCore.Httpsys/Hosting/AspNetCoreHostConfigurationExtensions.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.HttpSys;
using System;
using System.Runtime.Versioning;
diff --git a/src/Ninject.Web.AspNetCore.IIS/Hosting/AspNetCoreHostConfigurationExtensions.cs b/src/Ninject.Web.AspNetCore.IIS/Hosting/AspNetCoreHostConfigurationExtensions.cs
index 1fd0971..c99bd11 100644
--- a/src/Ninject.Web.AspNetCore.IIS/Hosting/AspNetCoreHostConfigurationExtensions.cs
+++ b/src/Ninject.Web.AspNetCore.IIS/Hosting/AspNetCoreHostConfigurationExtensions.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Hosting;
namespace Ninject.Web.AspNetCore.Hosting
{
diff --git a/src/Ninject.Web.AspNetCore.Test/Fakes/FakeActivationCacheAccessor.cs b/src/Ninject.Web.AspNetCore.Test/Fakes/FakeActivationCacheAccessor.cs
index 2b58dad..8774c34 100644
--- a/src/Ninject.Web.AspNetCore.Test/Fakes/FakeActivationCacheAccessor.cs
+++ b/src/Ninject.Web.AspNetCore.Test/Fakes/FakeActivationCacheAccessor.cs
@@ -1,4 +1,4 @@
-using Ninject.Activation.Caching;
+using Ninject.Activation.Caching;
using Ninject.Web.AspNetCore.Components;
using System;
using System.Collections.Generic;
@@ -7,7 +7,7 @@
namespace Ninject.Web.AspNetCore.Test.Fakes
{
- public class FakeActivationCacheAccessor : IActivationCache, IActivationCacheAccessor
+ public sealed class FakeActivationCacheAccessor : IActivationCache, IActivationCacheAccessor
{
private readonly ConditionalWeakTable