Skip to content

Commit 4f074ed

Browse files
authored
Add publish test for AOT compilation of Elastic.Transport (#169)
To ensure there are no analysis warnings when the assembly is used not just compiled.
1 parent 75b910f commit 4f074ed

File tree

15 files changed

+146
-146
lines changed

15 files changed

+146
-146
lines changed

.github/workflows/ci.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,34 @@ on:
1515
- "*.*.*"
1616

1717
jobs:
18+
aot-validate:
19+
runs-on: ${{ matrix.os.runner }}
20+
strategy:
21+
fail-fast: false
22+
matrix:
23+
os:
24+
- runner: ubuntu-latest
25+
folder: linux-x64
26+
binary: transport-aot-example
27+
- runner: macos-latest
28+
folder: osx-arm64
29+
binary: transport-aot-example
30+
- runner: windows-latest
31+
folder: win-x64
32+
binary: transport-aot-example.exe
33+
steps:
34+
- uses: actions/checkout@v4
35+
36+
- uses: actions/setup-dotnet@v4
37+
with:
38+
global-json-file: ./global.json
39+
40+
- name: Publish AOT
41+
run: dotnet publish examples/transport-aot-example
42+
43+
- name: Invoke AOT
44+
run: ./examples/transport-aot-example/bin/Release/net8.0/${{ matrix.os.folder }}/${{ matrix.os.binary }}
45+
1846
build:
1947
runs-on: ubuntu-latest
2048
steps:

Elastic.Transport.sln

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elastic.Elasticsearch.Integ
4646
EndProject
4747
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elastic.Transport.Tests.Shared", "tests\Elastic.Transport.Tests.Shared\Elastic.Transport.Tests.Shared.csproj", "{13A2597D-F50C-4D7F-ADA9-716991C8E9DE}"
4848
EndProject
49+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{54C8CB5B-11E4-48CB-8055-D8ACE8EFF0AB}"
50+
EndProject
51+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "transport-aot-example", "examples\transport-aot-example\transport-aot-example.csproj", "{29D5F68D-BABF-4738-9A9E-F2FE2DFFBEA8}"
52+
EndProject
4953
Global
5054
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5155
Debug|Any CPU = Debug|Any CPU
@@ -92,6 +96,10 @@ Global
9296
{13A2597D-F50C-4D7F-ADA9-716991C8E9DE}.Debug|Any CPU.Build.0 = Debug|Any CPU
9397
{13A2597D-F50C-4D7F-ADA9-716991C8E9DE}.Release|Any CPU.ActiveCfg = Release|Any CPU
9498
{13A2597D-F50C-4D7F-ADA9-716991C8E9DE}.Release|Any CPU.Build.0 = Release|Any CPU
99+
{29D5F68D-BABF-4738-9A9E-F2FE2DFFBEA8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
100+
{29D5F68D-BABF-4738-9A9E-F2FE2DFFBEA8}.Debug|Any CPU.Build.0 = Debug|Any CPU
101+
{29D5F68D-BABF-4738-9A9E-F2FE2DFFBEA8}.Release|Any CPU.ActiveCfg = Release|Any CPU
102+
{29D5F68D-BABF-4738-9A9E-F2FE2DFFBEA8}.Release|Any CPU.Build.0 = Release|Any CPU
95103
EndGlobalSection
96104
GlobalSection(SolutionProperties) = preSolution
97105
HideSolutionNode = FALSE
@@ -107,6 +115,7 @@ Global
107115
{5EE4DC72-B337-448B-802A-6158F4D90667} = {7610B796-BB3E-4CB2-8296-79BBFF6D23FC}
108116
{317C118F-FA1E-499A-B7F2-DC932DE66CB8} = {3582B07D-C2B0-49CC-B676-EAF806EB010E}
109117
{13A2597D-F50C-4D7F-ADA9-716991C8E9DE} = {3582B07D-C2B0-49CC-B676-EAF806EB010E}
118+
{29D5F68D-BABF-4738-9A9E-F2FE2DFFBEA8} = {54C8CB5B-11E4-48CB-8055-D8ACE8EFF0AB}
110119
EndGlobalSection
111120
GlobalSection(ExtensibilityGlobals) = postSolution
112121
SolutionGuid = {7F60C4BB-6216-4E50-B1E4-9C38EB484843}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using System.Text.Json.Serialization;
6+
using Elastic.Transport;
7+
using Elastic.Transport.Products.Elasticsearch;
8+
9+
var apiKey = Environment.GetEnvironmentVariable("ELASTIC_API_KEY");
10+
var url = Environment.GetEnvironmentVariable("ELASTIC_URL");
11+
12+
var configuration = apiKey is not null && url is not null
13+
? new ElasticsearchConfiguration(new Uri(url), new ApiKey(apiKey))
14+
: new ElasticsearchConfiguration { DebugMode = true };
15+
16+
var transport = new DistributedTransport(configuration);
17+
18+
var rootResponse = transport.Get<DynamicResponse>("/");
19+
if (rootResponse.ApiCallDetails.HasSuccessfulStatusCode)
20+
Console.WriteLine(rootResponse.Get<string>("tagline"));
21+
else
22+
Console.WriteLine(rootResponse);
23+
24+
public class MyDocument
25+
{
26+
[JsonPropertyName("message")]
27+
public string Message { init; get; } = null!;
28+
}
29+
30+
[JsonSerializable(typeof(MyDocument))]
31+
internal partial class ExampleJsonSerializerContext : JsonSerializerContext;
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<RootNamespace>transport_aot_example</RootNamespace>
7+
<ImplicitUsings>enable</ImplicitUsings>
8+
<Nullable>enable</Nullable>
9+
<PublishAot>true</PublishAot>
10+
<TrimmerSingleWarn>false</TrimmerSingleWarn>
11+
</PropertyGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\..\src\Elastic.Transport\Elastic.Transport.csproj"/>
15+
</ItemGroup>
16+
17+
</Project>

src/Elastic.Transport/Components/Serialization/Converters/DynamicDictionaryConverter.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using System;
66
using System.Collections.Generic;
7+
using System.Diagnostics.CodeAnalysis;
78
using System.Globalization;
89
using System.Text.Json;
910
using System.Text.Json.Serialization;
@@ -31,6 +32,8 @@ public override DynamicDictionary Read(ref Utf8JsonReader reader, Type typeToCon
3132
return DynamicDictionary.Create(dict);
3233
}
3334

35+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")]
36+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")]
3437
public override void Write(Utf8JsonWriter writer, DynamicDictionary dictionary, JsonSerializerOptions options)
3538
{
3639
writer.WriteStartObject();
@@ -42,9 +45,7 @@ public override void Write(Utf8JsonWriter writer, DynamicDictionary dictionary,
4245
writer.WritePropertyName(kvp.Key);
4346

4447
// TODO: Test! We have to make sure all possible "Value" types are registered in the `ErrorSerializationContext`
45-
#pragma warning disable IL2026, IL3050 // ErrorSerializerContext is registered.
4648
JsonSerializer.Serialize(writer, kvp.Value.Value, kvp.Value.GetType(), options);
47-
#pragma warning restore IL2026, IL3050
4849
}
4950

5051
writer.WriteEndObject();

src/Elastic.Transport/Components/Serialization/Converters/ErrorCauseConverter.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Collections.ObjectModel;
8+
using System.Diagnostics.CodeAnalysis;
89
using System.Text.Json;
910
using System.Text.Json.Serialization;
1011
using Elastic.Transport.Extensions;
@@ -19,12 +20,12 @@ public class ErrorCauseConverter : ErrorCauseConverter<ErrorCause> { }
1920
public class ErrorConverter : ErrorCauseConverter<Error>
2021
{
2122
/// <inheritdoc cref="ErrorCauseConverter{T}.ReadMore"/>
23+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")]
24+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")]
2225
protected override bool ReadMore(ref Utf8JsonReader reader, JsonSerializerOptions options, string propertyName, Error errorCause)
2326
{
2427
void ReadAssign<T>(ref Utf8JsonReader r, Action<Error, T> set) =>
25-
#pragma warning disable IL2026, IL3050 // ErrorSerializerContext is registered.
2628
set(errorCause, JsonSerializer.Deserialize<T>(ref r, options));
27-
#pragma warning restore IL2026, IL3050
2829
switch (propertyName)
2930
{
3031
case "headers":
@@ -45,6 +46,8 @@ void ReadAssign<T>(ref Utf8JsonReader r, Action<Error, T> set) =>
4546
public abstract class ErrorCauseConverter<TErrorCause> : JsonConverter<TErrorCause> where TErrorCause : ErrorCause, new()
4647
{
4748
/// <inheritdoc cref="JsonConverter{T}.Read"/>
49+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")]
50+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")]
4851
public override TErrorCause Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
4952
{
5053
if (reader.TokenType != JsonTokenType.StartObject)
@@ -57,14 +60,10 @@ public override TErrorCause Read(ref Utf8JsonReader reader, Type typeToConvert,
5760
errorCause.AdditionalProperties = additionalProperties;
5861

5962
void ReadAssign<T>(ref Utf8JsonReader r, Action<ErrorCause, T> set) =>
60-
#pragma warning disable IL2026, IL3050 // ErrorSerializerContext is registered.
6163
set(errorCause, JsonSerializer.Deserialize<T>(ref r, options));
62-
#pragma warning restore IL2026, IL3050
6364

6465
void ReadAny(ref Utf8JsonReader r, string property, Action<ErrorCause, string, object> set) =>
65-
#pragma warning disable IL2026, IL3050 // ErrorSerializerContext is registered.
6666
set(errorCause, property, JsonSerializer.Deserialize<JsonElement>(ref r, options));
67-
#pragma warning restore IL2026, IL3050
6867

6968
while (reader.Read())
7069
{
@@ -151,6 +150,8 @@ void ReadAny(ref Utf8JsonReader r, string property, Action<ErrorCause, string, o
151150
protected virtual bool ReadMore(ref Utf8JsonReader reader, JsonSerializerOptions options, string propertyName, TErrorCause errorCause) => false;
152151

153152
/// <inheritdoc cref="JsonConverter{T}.Read"/>
153+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")]
154+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")]
154155
public override void Write(Utf8JsonWriter writer, TErrorCause value, JsonSerializerOptions options)
155156
{
156157
writer.WriteStartObject();
@@ -160,19 +161,15 @@ static void Serialize<T>(Utf8JsonWriter writer, JsonSerializerOptions options, s
160161
if (value is null) return;
161162

162163
writer.WritePropertyName(name);
163-
#pragma warning disable IL2026, IL3050 // ErrorSerializerContext is registered.
164164
JsonSerializer.Serialize(writer, value, options);
165-
#pragma warning restore IL2026, IL3050
166165
}
167166

168167
static void SerializeDynamic(Utf8JsonWriter writer, JsonSerializerOptions options, string name, object? value, Type inputType)
169168
{
170169
if (value is null) return;
171170

172171
writer.WritePropertyName(name);
173-
#pragma warning disable IL2026, IL3050 // ErrorSerializerContext is registered.
174172
JsonSerializer.Serialize(writer, value, inputType, options);
175-
#pragma warning restore IL2026, IL3050
176173
}
177174

178175
//Serialize(writer, options, "bytes_limit", value.BytesLimit);

src/Elastic.Transport/Components/Serialization/LowLevelRequestResponseSerializer.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using System.Collections.Generic;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.Text.Json;
78
using System.Text.Json.Serialization;
89
using System.Text.Json.Serialization.Metadata;
@@ -28,6 +29,11 @@ public LowLevelRequestResponseSerializer() : this(null) { }
2829
/// <inheritdoc cref="LowLevelRequestResponseSerializer"/>>
2930
/// </summary>
3031
/// <param name="converters">Add more default converters onto <see cref="JsonSerializerOptions"/> being used</param>
32+
//[RequiresUnreferencedCode(JsonSerializer.SerializationUnreferencedCodeMessage)]
33+
//[RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
34+
35+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")]
36+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")]
3137
public LowLevelRequestResponseSerializer(IReadOnlyCollection<JsonConverter>? converters)
3238
: base(new TransportSerializerOptionsProvider([
3339
new ExceptionConverter(),
@@ -37,9 +43,6 @@ public LowLevelRequestResponseSerializer(IReadOnlyCollection<JsonConverter>? con
3743
], converters, options =>
3844
{
3945
options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
40-
#pragma warning disable IL2026, IL3050
4146
options.TypeInfoResolver = JsonTypeInfoResolver.Combine(new DefaultJsonTypeInfoResolver(), ElasticsearchTransportSerializerContext.Default);
42-
#pragma warning restore IL2026, IL3050
4347
})) { }
44-
4548
}

src/Elastic.Transport/Components/Serialization/SystemTextJsonSerializer.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using System;
6+
using System.Diagnostics.CodeAnalysis;
67
using System.IO;
78
using System.Text.Json;
89
using System.Threading;
@@ -38,6 +39,8 @@ protected SystemTextJsonSerializer(IJsonSerializerOptionsProvider? provider = nu
3839
#region Serializer
3940

4041
/// <inheritdoc />
42+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")]
43+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")]
4144
public override T Deserialize<T>(Stream stream)
4245
{
4346
if (TryReturnDefault(stream, out T deserialize))
@@ -56,6 +59,8 @@ public override T Deserialize<T>(Stream stream)
5659
}
5760

5861
/// <inheritdoc />
62+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "We always provide a static JsonTypeInfoResolver")]
63+
[UnconditionalSuppressMessage("AotAnalysis", "IL3050:RequiresDynamicCode", Justification = "We always provide a static JsonTypeInfoResolver")]
5964
public override ValueTask<T> DeserializeAsync<T>(Stream stream, CancellationToken cancellationToken = default)
6065
{
6166
if (TryReturnDefault(stream, out T deserialize))

src/Elastic.Transport/Elastic.Transport.csproj

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,16 @@
3737
</ItemGroup>
3838

3939
<ItemGroup>
40-
<PackageReference Include="PolySharp" Version="1.15.0" PrivateAssets="all"
41-
IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive"/>
40+
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
4241
</ItemGroup>
43-
4442
<ItemGroup Condition="'$(TargetFramework)' != 'net8.0'">
45-
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
4643
<PackageReference Include="System.Text.Json" Version="8.0.5" />
4744
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="8.0.0" />
4845
</ItemGroup>
4946

5047
<ItemGroup>
5148
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="8.0.0" />
49+
<PackageReference Include="PolySharp" Version="1.15.0" PrivateAssets="all"
50+
IncludeAssets="runtime; build; native; contentfiles; analyzers; buildtransitive"/>
5251
</ItemGroup>
5352
</Project>

src/Elastic.Transport/Requests/MetaData/ReflectionVersionInfo.cs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,7 @@ private static SemVersion DetermineVersionFromType(Type type)
5757
// Try to read the full version in 'major.minor.patch[.build][-prerelease][+build]' format. This format is semver2 compliant
5858
// except for the optional [.build] version number.
5959

60-
var version = type.Assembly?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
61-
62-
#pragma warning disable IL3000
63-
if (string.IsNullOrEmpty(version) && !string.IsNullOrEmpty(type.Assembly?.Location))
64-
{
65-
var location = type.Assembly?.Location;
66-
version = FileVersionInfo.GetVersionInfo(location)?.ProductVersion;
67-
}
68-
#pragma warning restore IL3000
60+
var version = type.Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
6961

7062
if (!string.IsNullOrEmpty(version))
7163
{
@@ -76,10 +68,10 @@ private static SemVersion DetermineVersionFromType(Type type)
7668
var prefix = GetVersionPrefixPart(version);
7769

7870
// Version prefix is not in a valid 'major.minor[.build[.revision]]' form
79-
if (!System.Version.TryParse(prefix, out var prefixVersion))
71+
if (!Version.TryParse(prefix, out var prefixVersion))
8072
return Empty;
8173

82-
// Version prefix '[.revision]' part is not present, but initial semver parsing failed anyways.
74+
// Version prefix '[.revision]' part is not present, but initial semver parsing failed anyway.
8375
// Nothing we can do here...
8476
if (prefixVersion.Revision < 0)
8577
return Empty;
@@ -111,10 +103,10 @@ private static SemVersion DetermineVersionFromType(Type type)
111103
{
112104
// Try to read the assembly version in 'major.minor[.build[.revision]]' format.
113105

114-
var version = type.Assembly?.GetCustomAttribute<AssemblyVersionAttribute>()?.Version;
106+
var version = type.Assembly.GetCustomAttribute<AssemblyVersionAttribute>()?.Version;
115107

116108
if (string.IsNullOrEmpty(version))
117-
version = type.Assembly?.GetName()?.Version?.ToString();
109+
version = type.Assembly.GetName().Version?.ToString();
118110

119111
if (!string.IsNullOrEmpty(version))
120112
{

0 commit comments

Comments
 (0)