diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 15b5e6876..a27b0612d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -57,13 +57,12 @@ jobs:
uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
global-json-file: global.json
- cache: true
- cache-dependency-path: |
- **/Directory.Packages.props
- **/*.csproj
- **/*.fsproj
- **/packages.lock.json
- **/nuget.config
+ # No Actions NuGet cache on the self-hosted runner: ~/.nuget/packages already persists on
+ # the runner's local disk between jobs, so the cache is redundant. Worse, any change to a
+ # dependency file (e.g. a Directory.Packages.props version bump) misses the key and forces a
+ # post-job tar+upload of the accumulated multi-GB global packages folder (~17 min at ~5 MB/s),
+ # which serializes the whole PR behind one cache-save step. Restore stays fast from local disk.
+ cache: false
- name: Setup Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
diff --git a/Directory.Packages.props b/Directory.Packages.props
index f9cb6ce66..ff9e10220 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -74,7 +74,7 @@
the contrib donation goes through its ~6-month review (see [[dual-target-otel-shipping]]).
When #4424 merges these PackageReference rows swap to the upstream id — type identities
preserved, so consumer code keeps compiling. -->
-
+
diff --git a/internal/qyl.instrumentation.generators/GeneratorPipelineHelpers.cs b/internal/qyl.instrumentation.generators/GeneratorPipelineHelpers.cs
index f2ce0c709..688f82750 100644
--- a/internal/qyl.instrumentation.generators/GeneratorPipelineHelpers.cs
+++ b/internal/qyl.instrumentation.generators/GeneratorPipelineHelpers.cs
@@ -9,10 +9,18 @@ internal static class GeneratorPipelineHelpers
public const string QylServiceDefaultsTypeName = "Qyl.Instrumentation.QylServiceDefaults";
public const string WebApplicationBuilderTypeName = "Microsoft.AspNetCore.Builder.WebApplicationBuilder";
public const string WebApplicationTypeName = "Microsoft.AspNetCore.Builder.WebApplication";
+ public const string QylInterceptedAspNetCoreTypeName = "Qyl.OpenTelemetry.AutoInstrumentation.QylInterceptedAspNetCore";
public static bool IsQylRuntimeReferenced(Compilation compilation, CancellationToken _) =>
compilation.GetTypeByMetadataName(QylServiceDefaultsTypeName) is not null;
+ // When Qyl.OpenTelemetry.AutoInstrumentation is referenced, our Build() interceptor routes the
+ // build through its QylInterceptedAspNetCore.Build wrapper (which adds the OTel middleware) so a
+ // single interceptor owns the call site. The consumer sets the package's opt-out property so the
+ // OTel generator yields Build() to us — otherwise the two interceptors would collide (CS9153).
+ public static bool IsOtelAutoInstrumentationReferenced(Compilation compilation, CancellationToken _) =>
+ compilation.GetTypeByMetadataName(QylInterceptedAspNetCoreTypeName) is not null;
+
public static bool IsPipelineEnabled(AnalyzerConfigOptionsProvider options, string propertyName) =>
!options.GlobalOptions.TryGetValue($"build_property.{propertyName}", out var value)
|| !string.Equals(value, "false", StringComparison.OrdinalIgnoreCase);
diff --git a/internal/qyl.instrumentation.generators/ServiceDefaultsSourceGenerator.cs b/internal/qyl.instrumentation.generators/ServiceDefaultsSourceGenerator.cs
index b98b11e63..577eab655 100644
--- a/internal/qyl.instrumentation.generators/ServiceDefaultsSourceGenerator.cs
+++ b/internal/qyl.instrumentation.generators/ServiceDefaultsSourceGenerator.cs
@@ -46,6 +46,10 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
.Select(GeneratorPipelineHelpers.IsQylRuntimeReferenced)
.WithTrackingName(PipelineStage.QylRuntimeCheck);
+ var otelAutoInstrumentationAvailable = context.CompilationProvider
+ .Select(GeneratorPipelineHelpers.IsOtelAutoInstrumentationReferenced)
+ .WithTrackingName(PipelineStage.OtelAutoInstrumentationCheck);
+
var builderCallSites = context.SyntaxProvider
.CreateSyntaxProvider(CouldBeBuildInvocation, ExtractBuilderCallSite)
.WhereNotNull()
@@ -53,7 +57,9 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
var builderInput = builderCallSites.CollectAsEquatableArray()
.Combine(runtimeAvailable)
- .Select(static (input, _) => new BuilderInterceptorInput(input.Left, input.Right));
+ .Combine(otelAutoInstrumentationAvailable)
+ .Select(static (input, _) =>
+ new BuilderInterceptorInput(input.Left.Left, input.Left.Right, input.Right));
context.RegisterSourceOutput(builderInput, EmitBuilderInterceptors);
}
@@ -96,11 +102,15 @@ private static void EmitBuilderInterceptors(SourceProductionContext context, Bui
if (!input.QylRuntimeAvailable)
return;
- var source = GenerateBuilderInterceptorSource(input.CallSites.AsImmutableArray());
+ var source = GenerateBuilderInterceptorSource(
+ input.CallSites.AsImmutableArray(),
+ input.OtelAutoInstrumentationAvailable);
context.AddSource(BuilderInterceptorsFile, SourceText.From(source, Encoding.UTF8));
}
- private static string GenerateBuilderInterceptorSource(ImmutableArray callSites)
+ private static string GenerateBuilderInterceptorSource(
+ ImmutableArray callSites,
+ bool otelAutoInstrumentationAvailable)
{
var sb = new StringBuilder();
@@ -114,7 +124,7 @@ private static string GenerateBuilderInterceptorSource(ImmutableArray c.SortKey, StringComparer.Ordinal))
{
if (callSite.Kind is BuilderCallKind.Build)
- AppendBuildInterceptorMethod(sb, callSite, index);
+ AppendBuildInterceptorMethod(sb, callSite, index, otelAutoInstrumentationAvailable);
index++;
}
@@ -122,11 +132,23 @@ private static string GenerateBuilderInterceptorSource(ImmutableArray CallSites,
- bool QylRuntimeAvailable);
+ bool QylRuntimeAvailable,
+ bool OtelAutoInstrumentationAvailable);
}
diff --git a/services/qyl.collector/qyl.collector.csproj b/services/qyl.collector/qyl.collector.csproj
index 9c7e8333c..6e3e55ddc 100644
--- a/services/qyl.collector/qyl.collector.csproj
+++ b/services/qyl.collector/qyl.collector.csproj
@@ -8,6 +8,11 @@
false
false
$(InterceptorsNamespaces);Qyl.Instrumentation.Generators
+
+ false
false