Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
c6c700a
feat: enhance ValueJsonConverter for AOT compatibility with manual JS…
askpt Aug 15, 2025
bdb394b
feat: refactor EnumExtensions to improve AOT compatibility and remove…
askpt Aug 15, 2025
c41cab7
feat: add OpenFeatureJsonSerializerContext for AOT compilation support
askpt Aug 15, 2025
56608c0
feat: add AOT and trimming support for net8.0 and net9.0
askpt Aug 15, 2025
ed39652
feat: add NativeAOT compatibility tests and project configuration
askpt Aug 15, 2025
6f5390a
feat: update project structure for AOT compatibility and add MultiPro…
askpt Aug 15, 2025
5c794e4
fix: remove unnecessary Type attribute project in solution file
askpt Aug 15, 2025
5cd179e
feat: add unit tests for EnumExtensions.GetDescription method
askpt Aug 15, 2025
1c54a7f
fix: remove trimming support properties for net8.0 and net9.0
askpt Aug 15, 2025
f62a88d
feat: add AOT compatibility workflow with cross-platform testing and …
askpt Aug 15, 2025
f8f90fc
fix: simplify AOT compatibility workflow by removing unnecessary prop…
askpt Aug 15, 2025
df63171
fix: update AOT compatibility workflow to include runtime in publish …
askpt Aug 16, 2025
ea053a3
fix: update AOT compatibility workflow to streamline ARM64 handling a…
askpt Aug 16, 2025
4cc832e
fix: standardize shell usage and update publish command syntax in AOT…
askpt Aug 16, 2025
f462b4e
fix: update AOT size comparison report to remove AspNetCore sample co…
askpt Aug 16, 2025
71c2bc4
fix: remove AOT size comparison job and artifact upload steps from wo…
askpt Aug 18, 2025
2895325
fix: update AOT compatibility workflow permissions and enhance docume…
askpt Aug 18, 2025
1086914
fix: streamline AOT compatibility documentation by removing redundant…
askpt Aug 18, 2025
f005f9e
fix: update actions/checkout and actions/cache versions in AOT compat…
askpt Aug 18, 2025
f36d20f
Apply suggestions from code review
askpt Aug 19, 2025
d93dd2d
Update .github/workflows/aot-compatibility.yml
askpt Aug 19, 2025
d05fa4e
fix: remove unnecessary properties from AOT project configuration
askpt Aug 19, 2025
5994f99
docs: update README to clarify NativeAOT compatibility for contrib an…
askpt Aug 19, 2025
14a881e
Apply suggestions from code review
askpt Aug 20, 2025
92339a6
fix: add descriptions to ErrorType enum values for better clarity
askpt Aug 22, 2025
67559c9
fix: remove AOT compatibility references and enhance error handling t…
askpt Aug 22, 2025
2cadd2a
fix: update System.Text.Json package reference in project files
askpt Aug 22, 2025
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
97 changes: 97 additions & 0 deletions .github/workflows/aot-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: AOT Compatibility

on:
push:
branches: [main]
pull_request:
branches: [main]
merge_group:
workflow_dispatch:

jobs:
aot-compatibility:
name: AOT Test (${{ matrix.os }}, ${{ matrix.arch }})
strategy:
fail-fast: false
matrix:
include:
# Linux x64
- os: ubuntu-latest
arch: x64
runtime: linux-x64
# Linux ARM64
- os: ubuntu-24.04-arm
arch: arm64
runtime: linux-arm64
# Windows x64
- os: windows-latest
arch: x64
runtime: win-x64
# Windows ARM64
- os: windows-11-arm
arch: arm64
runtime: win-arm64
# macOS x64
- os: macos-13
arch: x64
runtime: osx-x64
# macOS ARM64 (Apple Silicon)
- os: macos-latest
arch: arm64
runtime: osx-arm64

runs-on: ${{ matrix.os }}

steps:
- name: Checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
with:
fetch-depth: 0
submodules: recursive

- name: Setup .NET SDK
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
env:
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
global-json-file: global.json
source-url: https://nuget.pkg.github.com/open-feature/index.json

- name: Cache NuGet packages
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-${{ matrix.arch }}-nuget-${{ hashFiles('**/*.csproj') }}
restore-keys: |
${{ runner.os }}-${{ matrix.arch }}-nuget-
${{ runner.os }}-nuget-

- name: Restore dependencies
shell: pwsh
run: dotnet restore

- name: Build solution
shell: pwsh
run: dotnet build -c Release --no-restore

- name: Test AOT compatibility project build
shell: pwsh
run: dotnet build test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj -c Release --no-restore

- name: Publish AOT compatibility test (cross-platform)
shell: pwsh
run: |
dotnet publish test/OpenFeature.AotCompatibility/OpenFeature.AotCompatibility.csproj `
-c Release `
-r ${{ matrix.runtime }} `
-o ./aot-output

- name: Run AOT compatibility test
shell: pwsh
run: |
if ("${{ runner.os }}" -eq "Windows") {
./aot-output/OpenFeature.AotCompatibility.exe
} else {
chmod +x ./aot-output/OpenFeature.AotCompatibility
./aot-output/OpenFeature.AotCompatibility
}
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
<PackageVersion Include="coverlet.msbuild" Version="6.0.4" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Testing" Version="9.3.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsVersion)" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsVersion)" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="NSubstitute" Version="5.3.0" />
<PackageVersion Include="OpenTelemetry" Version="1.12.0" />
Expand Down
7 changes: 4 additions & 3 deletions OpenFeature.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
<Folder Name="/src/">
<Project Path="src/OpenFeature.DependencyInjection/OpenFeature.DependencyInjection.csproj" />
<Project Path="src/OpenFeature.Hosting/OpenFeature.Hosting.csproj" />
<Project Path="src/OpenFeature.Providers.MultiProvider/OpenFeature.Providers.MultiProvider.csproj" Type="Classic C#" />
<Project Path="src/OpenFeature.Providers.MultiProvider/OpenFeature.Providers.MultiProvider.csproj" />
<Project Path="src/OpenFeature/OpenFeature.csproj" />
<File Path="src/Directory.Build.props" />
<File Path="src/Directory.Build.targets" />
Expand All @@ -64,7 +64,8 @@
<Project Path="test/OpenFeature.E2ETests/OpenFeature.E2ETests.csproj" />
<Project Path="test/OpenFeature.IntegrationTests/OpenFeature.IntegrationTests.csproj" />
<Project Path="test/OpenFeature.Tests/OpenFeature.Tests.csproj" />
<Project Path="test\OpenFeature.Providers.MultiProvider.Tests\OpenFeature.Providers.MultiProvider.Tests.csproj" Type="Classic C#" />
<Project Path="test/OpenFeature.AotCompatibility\OpenFeature.AotCompatibility.csproj" />
<Project Path="test\OpenFeature.Providers.MultiProvider.Tests\OpenFeature.Providers.MultiProvider.Tests.csproj" />
<File Path="test/Directory.Build.props" />
</Folder>
</Solution>
</Solution>
9 changes: 7 additions & 2 deletions build/Common.prod.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<Import Project=".\Common.props" />
<Import Project=".\Common.props"/>

<PropertyGroup>
<ContinuousIntegrationBuild Condition="'$(CI)' == 'true'">true</ContinuousIntegrationBuild>
Expand All @@ -24,8 +24,13 @@
<FileVersion>$(VersionNumber)</FileVersion>
</PropertyGroup>

<!-- AOT and Trimming Support -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net8.0' or '$(TargetFramework)' == 'net9.0'">
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>

<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)openfeature-icon.png" Pack="true" PackagePath="\" />
<None Include="$(MSBuildThisFileDirectory)openfeature-icon.png" Pack="true" PackagePath="\"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<ItemGroup>
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
<InternalsVisibleTo Include="OpenFeature.Providers.MultiProvider.Tests" />
<InternalsVisibleTo Include="OpenFeature.AotCompatibility" />
<None Include="README.md" Pack="true" PackagePath="/" />
</ItemGroup>

Expand Down
18 changes: 8 additions & 10 deletions src/OpenFeature/Constant/ErrorType.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
using System.ComponentModel;

namespace OpenFeature.Constant;

/// <summary>
Expand All @@ -16,40 +14,40 @@ public enum ErrorType
/// <summary>
/// Provider has yet been initialized
/// </summary>
[Description("PROVIDER_NOT_READY")] ProviderNotReady,
ProviderNotReady,

/// <summary>
/// Provider was unable to find the flag
/// </summary>
[Description("FLAG_NOT_FOUND")] FlagNotFound,
FlagNotFound,

/// <summary>
/// Provider failed to parse the flag response
/// </summary>
[Description("PARSE_ERROR")] ParseError,
ParseError,

/// <summary>
/// Request type does not match the expected type
/// </summary>
[Description("TYPE_MISMATCH")] TypeMismatch,
TypeMismatch,

/// <summary>
/// Abnormal execution of the provider
/// </summary>
[Description("GENERAL")] General,
General,

/// <summary>
/// Context does not satisfy provider requirements.
/// </summary>
[Description("INVALID_CONTEXT")] InvalidContext,
InvalidContext,

/// <summary>
/// Context does not contain a targeting key and the provider requires one.
/// </summary>
[Description("TARGETING_KEY_MISSING")] TargetingKeyMissing,
TargetingKeyMissing,

/// <summary>
/// The provider has entered an irrecoverable error state.
/// </summary>
[Description("PROVIDER_FATAL")] ProviderFatal,
ProviderFatal,
}
27 changes: 23 additions & 4 deletions src/OpenFeature/Extension/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,32 @@
using System.ComponentModel;
using OpenFeature.Constant;

namespace OpenFeature.Extension;

internal static class EnumExtensions
{
/// <summary>
/// Gets the description of an enum value without using reflection.
/// This is AOT-compatible and only supports specific known enum types.
/// </summary>
/// <param name="value">The enum value to get the description for</param>
/// <returns>The description string or the enum value as string if no description is available</returns>
public static string GetDescription(this Enum value)
{
var field = value.GetType().GetField(value.ToString());
var attribute = field?.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault() as DescriptionAttribute;
return attribute?.Description ?? value.ToString();
return value switch
{
// ErrorType descriptions
ErrorType.None => "NONE",
ErrorType.ProviderNotReady => "PROVIDER_NOT_READY",
ErrorType.FlagNotFound => "FLAG_NOT_FOUND",
ErrorType.ParseError => "PARSE_ERROR",
ErrorType.TypeMismatch => "TYPE_MISMATCH",
ErrorType.General => "GENERAL",
ErrorType.InvalidContext => "INVALID_CONTEXT",
ErrorType.TargetingKeyMissing => "TARGETING_KEY_MISSING",
ErrorType.ProviderFatal => "PROVIDER_FATAL",

// Fallback for any other enum types
_ => value.ToString()
};
}
}
4 changes: 3 additions & 1 deletion src/OpenFeature/Model/ValueJsonConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace OpenFeature.Model;

/// <summary>
/// A <see cref="JsonConverter{T}"/> for <see cref="Value"/> for Json serialization
/// A <see cref="JsonConverter{T}"/> for <see cref="Value"/> for Json serialization.
/// This converter is AOT-compatible as it uses manual JSON reading/writing
/// instead of reflection-based serialization.
/// </summary>
public sealed class ValueJsonConverter : JsonConverter<Value>
{
Expand Down
3 changes: 2 additions & 1 deletion src/OpenFeature/OpenFeature.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
<InternalsVisibleTo Include="OpenFeature.E2ETests" />
<InternalsVisibleTo Include="OpenFeature.DependencyInjection" />
<InternalsVisibleTo Include="OpenFeature.Providers.MultiProvider.Tests" />
<InternalsVisibleTo Include="OpenFeature.AotCompatibility" />
<None Include="../../README.md" Pack="true" PackagePath="/" />
</ItemGroup>

</Project>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System.Collections.Immutable;
using System.Text.Json.Serialization;
using OpenFeature.Model;

namespace OpenFeature.Serialization;

/// <summary>
/// JSON serializer context for AOT compilation support.
/// This ensures that all necessary types are pre-compiled for JSON serialization
/// when using NativeAOT.
/// </summary>
[JsonSerializable(typeof(Value))]
[JsonSerializable(typeof(Structure))]
[JsonSerializable(typeof(EvaluationContext))]
[JsonSerializable(typeof(Dictionary<string, Value>))]
[JsonSerializable(typeof(ImmutableDictionary<string, Value>))]
[JsonSerializable(typeof(List<Value>))]
[JsonSerializable(typeof(ImmutableList<Value>))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(int))]
[JsonSerializable(typeof(double))]
[JsonSerializable(typeof(DateTime))]
[JsonSourceGenerationOptions(
WriteIndented = false,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
public partial class OpenFeatureJsonSerializerContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>

<!-- NativeAOT Configuration -->
<PublishAot>true</PublishAot>
<IsAotCompatible>true</IsAotCompatible>
<InvariantGlobalization>true</InvariantGlobalization>
<TrimMode>full</TrimMode>
<IlcOptimizationPreference>Size</IlcOptimizationPreference>

<!-- Suppress vulnerable package warnings for this test project -->
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningsNotAsErrors>NU1903</WarningsNotAsErrors>
<RootNamespace>OpenFeature.AotCompatibility</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\OpenFeature\OpenFeature.csproj" />
<ProjectReference Include="..\..\src\OpenFeature.DependencyInjection\OpenFeature.DependencyInjection.csproj" />
<ProjectReference Include="..\..\src\OpenFeature.Providers.MultiProvider\OpenFeature.Providers.MultiProvider.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
</ItemGroup>

</Project>
Loading
Loading