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