From 7c348d1b012f00e7fc3d164cc9670b74208f6b3c Mon Sep 17 00:00:00 2001 From: Matthew Henderson Date: Mon, 23 Jun 2025 09:52:11 -0700 Subject: [PATCH 01/45] Updating extension dependencies (#3098) --- extensions/Worker.Extensions.EventHubs/release_notes.md | 4 ++-- .../src/Worker.Extensions.EventHubs.csproj | 6 +++--- extensions/Worker.Extensions.ServiceBus/release_notes.md | 4 ++-- .../src/Worker.Extensions.ServiceBus.csproj | 8 ++++---- extensions/Worker.Extensions.Tables/release_notes.md | 3 ++- .../src/Worker.Extensions.Tables.csproj | 6 +++--- 6 files changed, 16 insertions(+), 15 deletions(-) diff --git a/extensions/Worker.Extensions.EventHubs/release_notes.md b/extensions/Worker.Extensions.EventHubs/release_notes.md index e621874c1..aa498c751 100644 --- a/extensions/Worker.Extensions.EventHubs/release_notes.md +++ b/extensions/Worker.Extensions.EventHubs/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.EventHubs 6.4.0 +### Microsoft.Azure.Functions.Worker.Extensions.EventHubs 6.5.0 -- Updating `Microsoft.Azure.WebJobs.Extensions.EventHubs` reference to 6.5.1 +- Updating `Microsoft.Azure.WebJobs.Extensions.EventHubs` reference to 6.5.2 (#3098) diff --git a/extensions/Worker.Extensions.EventHubs/src/Worker.Extensions.EventHubs.csproj b/extensions/Worker.Extensions.EventHubs/src/Worker.Extensions.EventHubs.csproj index 860d90d25..711ca3711 100644 --- a/extensions/Worker.Extensions.EventHubs/src/Worker.Extensions.EventHubs.csproj +++ b/extensions/Worker.Extensions.EventHubs/src/Worker.Extensions.EventHubs.csproj @@ -6,7 +6,7 @@ Azure Event Hubs extensions for .NET isolated functions - 6.4.0 + 6.5.0 @@ -17,7 +17,7 @@ - + @@ -27,7 +27,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.ServiceBus/release_notes.md b/extensions/Worker.Extensions.ServiceBus/release_notes.md index c92b7d33c..eb4cbfcb5 100644 --- a/extensions/Worker.Extensions.ServiceBus/release_notes.md +++ b/extensions/Worker.Extensions.ServiceBus/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus +### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus 5.23.0 -- \ No newline at end of file +- Update dependency `Microsoft.Azure.WebJobs.Extensions.ServiceBus` to 5.17.0 (#3098) \ No newline at end of file diff --git a/extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj b/extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj index 69392a826..048f513cc 100644 --- a/extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj +++ b/extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj @@ -6,7 +6,7 @@ Azure Service Bus extensions for .NET isolated functions - 5.22.2 + 5.23.0 false @@ -15,9 +15,9 @@ - + - + @@ -36,7 +36,7 @@ - + diff --git a/extensions/Worker.Extensions.Tables/release_notes.md b/extensions/Worker.Extensions.Tables/release_notes.md index 197b1a6c1..bffbc10ad 100644 --- a/extensions/Worker.Extensions.Tables/release_notes.md +++ b/extensions/Worker.Extensions.Tables/release_notes.md @@ -6,4 +6,5 @@ ### Microsoft.Azure.Functions.Worker.Extensions.Tables 1.5.0 -- Support deferred binding for poco types in Table Input binding (#3003) \ No newline at end of file +- Support deferred binding for poco types in Table Input binding (#3003) +- Update dependency `Microsoft.Azure.WebJobs.Extensions.Tables` to 1.4.0 (#3098) \ No newline at end of file diff --git a/extensions/Worker.Extensions.Tables/src/Worker.Extensions.Tables.csproj b/extensions/Worker.Extensions.Tables/src/Worker.Extensions.Tables.csproj index ab07ea44e..30c5668e8 100644 --- a/extensions/Worker.Extensions.Tables/src/Worker.Extensions.Tables.csproj +++ b/extensions/Worker.Extensions.Tables/src/Worker.Extensions.Tables.csproj @@ -18,8 +18,8 @@ - - + + @@ -29,7 +29,7 @@ - + \ No newline at end of file From 4e9f1379e39820df72c4ce22907c2096d307076e Mon Sep 17 00:00:00 2001 From: Jacob Viau Date: Mon, 23 Jun 2025 10:08:14 -0700 Subject: [PATCH 02/45] Skip writing functions.metadata when using source gen (#2974) * Skip writing functions.metadata when using source gen * Update SDK tests * update release notes * Fix tests, allow for manual override of writing functions.metadata * Update tests for force write metadata * Fix copy condition, refactor tests * FIx IDisposable * Fix restore with rid * Fix bad merge * Restore separately for test --- ...crosoft.Azure.Functions.Worker.Sdk.targets | 10 +- sdk/Sdk/Tasks/GenerateFunctionMetadata.cs | 9 ++ sdk/release_notes.md | 4 +- .../AspNetCore/CancellationEndToEndTests.cs | 8 +- .../E2ETests/Fixtures/FunctionAppFixture.cs | 5 +- test/Sdk.E2ETests/InnerBuildTests.cs | 42 ++++--- test/Sdk.E2ETests/ProcessWrapper.cs | 33 +++--- test/Sdk.E2ETests/ProjectBuilder.cs | 106 +++++++++++++++++ test/Sdk.E2ETests/PublishTests.cs | 109 +++++++++--------- test/Sdk.E2ETests/TempDirectory.cs | 41 +++++++ test/Sdk.E2ETests/TestUtility.cs | 107 ++--------------- test/Sdk.E2ETests/ZipDeployTests.cs | 28 +++-- 12 files changed, 286 insertions(+), 216 deletions(-) create mode 100644 test/Sdk.E2ETests/ProjectBuilder.cs create mode 100644 test/Sdk.E2ETests/TempDirectory.cs diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets index 5deed66cf..b08c2e352 100644 --- a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets +++ b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets @@ -48,7 +48,9 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and false true $(FunctionsEnableWorkerIndexing) - $(FunctionsEnableWorkerIndexing) + $(FunctionsEnableWorkerIndexing) + false + true true true @@ -184,9 +186,10 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and ReferencePaths="@(ReferencePath)" ExtensionsCsProjFilePath="$(ExtensionsCsProj)" AzureFunctionsVersion="$(AzureFunctionsVersion)" + WriteMetadataFile="$(FunctionsWriteMetadataJson)" TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)" TargetFrameworkVersion="$(TargetFrameworkVersion)" - OutputPath="$(IntermediateOutputPath)"/> + OutputPath="$(IntermediateOutputPath)" /> @@ -240,7 +243,8 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and - <_FunctionsAdditionalFile Include="$(_FunctionsMetadataPath);$(_FunctionsWorkerConfigPath);$(_FunctionsIntermediateExtensionUpdatedJsonPath)" /> + <_FunctionsAdditionalFile Include="$(_FunctionsMetadataPath)" Condition="'$(FunctionsWriteMetadataJson)' == 'true'" /> + <_FunctionsAdditionalFile Include="$(_FunctionsWorkerConfigPath);$(_FunctionsIntermediateExtensionUpdatedJsonPath)" /> <_FunctionsAdditionalFile Include="$(_FunctionsMetadataLoaderExtensionFile)" SubPath="$(_FunctionsExtensionsDirectory)/" /> <_NoneWithTargetPath Include="@(_FunctionsAdditionalFile)" TargetPath="%(_FunctionsAdditionalFile.SubPath)%(Filename)%(Extension)" diff --git a/sdk/Sdk/Tasks/GenerateFunctionMetadata.cs b/sdk/Sdk/Tasks/GenerateFunctionMetadata.cs index e54aa995a..58568268d 100644 --- a/sdk/Sdk/Tasks/GenerateFunctionMetadata.cs +++ b/sdk/Sdk/Tasks/GenerateFunctionMetadata.cs @@ -26,6 +26,8 @@ public class GenerateFunctionMetadata : Task public string? ExtensionsCsProjFilePath { get; set; } + public bool WriteMetadataFile { get; set; } = true; + [Required] public ITaskItem[]? ReferencePaths { get; set; } @@ -67,11 +69,18 @@ public override bool Execute() private void WriteMetadataWithRetry(IEnumerable functions) { + if (!WriteMetadataFile) + { + Log.LogMessage("Skipping writing function metadata file."); + return; + } + int attempt = 0; while (attempt < 10) { try { + Log.LogMessage($"Writing function metadata to {OutputPath} directory."); FunctionMetadataJsonWriter.WriteMetadata(functions, OutputPath!); break; } diff --git a/sdk/release_notes.md b/sdk/release_notes.md index 45c1eac83..3d4479236 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -4,9 +4,9 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Sdk 2.0.5 +### Microsoft.Azure.Functions.Worker.Sdk -- Address issue with design time build producing an error when project had not yet been built yet. (#3081) +- Build no longer generates `functions.metadata` if source-generated metadata provider is enabled. (#2974) ### Microsoft.Azure.Functions.Worker.Sdk.Generators diff --git a/test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs b/test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs index 4bfce6034..0fc9dae82 100644 --- a/test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs +++ b/test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs @@ -37,10 +37,10 @@ public async Task HttpTriggerFunctions_WithCancellationToken_BehaveAsExpected(st await TestUtility.RetryAsync(() => { invocationStartLog = _fixture.TestLogs.CoreToolsLogs.Where(p => p.Contains($"Executing 'Functions.{functionName}'")); - return Task.FromResult(invocationStartLog.Count() >= 1); + return Task.FromResult(invocationStartLog.Any()); }); - // The task should be cancelled before it completes, mimicing a client closing the connection. + // The task should be cancelled before it completes, mimicking a client closing the connection. // This should lead to the worker getting an InvocationCancel request from the functions host cts.Cancel(); await Assert.ThrowsAsync(async () => await task); @@ -49,13 +49,13 @@ await TestUtility.RetryAsync(() => await TestUtility.RetryAsync(() => { invocationEndLog = _fixture.TestLogs.CoreToolsLogs.Where(p => p.Contains($"Executed 'Functions.{functionName}'")); - return Task.FromResult(invocationEndLog.Count() >= 1); + return Task.FromResult(invocationEndLog.Any()); }); Assert.Contains(_fixture.TestLogs.CoreToolsLogs, log => log.Contains(expectedMessage, StringComparison.OrdinalIgnoreCase)); // TODO: 2/3 of the test invocations will fail until the host with the ForwarderProxy fix is released - uncomment this line when the fix is released - Assert.NotEqual(null, invocationResult); // just here to 'use' invocationResult to avoid a warning. + Assert.NotNull(invocationResult); // just here to 'use' invocationResult to avoid a warning. // Assert.Contains(_fixture.TestLogs.CoreToolsLogs, log => log.Contains($"'Functions.{functionName}' ({invocationResult}", StringComparison.OrdinalIgnoreCase)); } diff --git a/test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs b/test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs index 2eedeb61f..be132df86 100644 --- a/test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs +++ b/test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs @@ -17,11 +17,11 @@ namespace Microsoft.Azure.Functions.Tests.E2ETests { public class FunctionAppFixture : IAsyncLifetime { + private readonly string _testApp = Constants.TestAppNames.E2EApp; private readonly ILogger _logger; private bool _disposed; private Process _funcProcess; private JobObjectRegistry _jobObjectRegistry; - private string _testApp = Constants.TestAppNames.E2EApp; public FunctionAppFixture(IMessageSink messageSink) { @@ -32,7 +32,8 @@ public FunctionAppFixture(IMessageSink messageSink) _logger = loggerFactory.CreateLogger(); } - internal FunctionAppFixture(IMessageSink messageSink, string testApp) : this(messageSink) + internal FunctionAppFixture(IMessageSink messageSink, string testApp) + : this(messageSink) { _testApp = testApp; } diff --git a/test/Sdk.E2ETests/InnerBuildTests.cs b/test/Sdk.E2ETests/InnerBuildTests.cs index e38822c14..455ec7307 100644 --- a/test/Sdk.E2ETests/InnerBuildTests.cs +++ b/test/Sdk.E2ETests/InnerBuildTests.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System; using System.IO; using System.Threading.Tasks; using Newtonsoft.Json.Linq; @@ -9,25 +10,27 @@ namespace Microsoft.Azure.Functions.Sdk.E2ETests { - public class InnerBuildTests + public sealed class InnerBuildTests(ITestOutputHelper testOutputHelper) : IDisposable { - private readonly ITestOutputHelper _testOutputHelper; + private readonly ProjectBuilder _builder = new( + testOutputHelper, + Path.Combine(TestUtility.TestResourcesProjectsRoot, "FunctionApp01", "FunctionApp01.csproj")); - public InnerBuildTests(ITestOutputHelper testOutputHelper) + [Theory] + [InlineData("", false)] + [InlineData("-p:FunctionsEnableWorkerIndexing=true", false)] + [InlineData("-p:FunctionsEnableWorkerIndexing=true -p:FunctionsWriteMetadataJson=false", false)] + [InlineData("-p:FunctionsEnableWorkerIndexing=true -p:FunctionsWriteMetadataJson=true", true)] + [InlineData("-p:FunctionsEnableWorkerIndexing=false", true)] + [InlineData("-p:FunctionsEnableWorkerIndexing=false -p:FunctionsWriteMetadataJson=false", false)] + [InlineData("-p:FunctionsEnableWorkerIndexing=false -p:FunctionsWriteMetadataJson=true", true)] + public async Task Build_ScansReferences(string parameters, bool metadataGenerated) { - _testOutputHelper = testOutputHelper; - } - - [Fact] - public async Task Build_ScansReferences() - { - string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Build_ScansReferences)); - string projectFileDirectory = Path.Combine(TestUtility.TestResourcesProjectsRoot, "FunctionApp01", "FunctionApp01.csproj"); - - await TestUtility.RestoreAndBuildProjectAsync(projectFileDirectory, outputDir, null, _testOutputHelper); + await _builder.RestoreAsync(); + await _builder.BuildAsync(parameters, restore: false); // Verify extensions.json contents - string extensionsJsonPath = Path.Combine(outputDir, "extensions.json"); + string extensionsJsonPath = Path.Combine(_builder.OutputPath, "extensions.json"); Assert.True(File.Exists(extensionsJsonPath)); JToken extensionsJsonContents = JObject.Parse(File.ReadAllText(extensionsJsonPath)); @@ -54,8 +57,13 @@ public async Task Build_ScansReferences() Assert.True(JToken.DeepEquals(expectedExtensionsJson, extensionsJsonContents)); // Verify functions.metadata contents - string functionsMetadataPath = Path.Combine(outputDir, "functions.metadata"); - Assert.True(File.Exists(functionsMetadataPath)); + string functionsMetadataPath = Path.Combine(_builder.OutputPath, "functions.metadata"); + Assert.Equal(metadataGenerated, File.Exists(functionsMetadataPath)); + + if (!metadataGenerated) + { + return; + } JToken functionsMetadataContents = JArray.Parse(File.ReadAllText(functionsMetadataPath)); JToken expectedFunctionsMetadata = JArray.Parse(@"[ @@ -115,5 +123,7 @@ public async Task Build_ScansReferences() Assert.True(JToken.DeepEquals(expectedFunctionsMetadata, functionsMetadataContents)); } + + public void Dispose() => _builder.Dispose(); } } diff --git a/test/Sdk.E2ETests/ProcessWrapper.cs b/test/Sdk.E2ETests/ProcessWrapper.cs index ae250e4ae..eb04f284b 100644 --- a/test/Sdk.E2ETests/ProcessWrapper.cs +++ b/test/Sdk.E2ETests/ProcessWrapper.cs @@ -6,26 +6,27 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Xunit.Abstractions; namespace Microsoft.Azure.Functions.Sdk.E2ETests { - public class ProcessWrapper + public static class ProcessWrapper { - - public async Task RunProcess(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null) + public static async Task RunProcessAsync( + string fileName, string arguments, string workingDirectory = null, Action log = null) { - return await RunProcessInternal(fileName, arguments, workingDirectory, testOutputHelper); + return await RunProcessInternalAsync(fileName, arguments, workingDirectory, log); } - public async Task> RunProcessForOutput(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null) + public static async Task> RunProcessForOutputAsync( + string fileName, string arguments, string workingDirectory = null, Action log = null) { StringBuilder processOutputStringBuilder = new StringBuilder(); - var exitCode = await RunProcessInternal(fileName, arguments, workingDirectory, testOutputHelper, processOutputStringBuilder); + var exitCode = await RunProcessInternalAsync(fileName, arguments, workingDirectory, log, processOutputStringBuilder); return new Tuple(exitCode, processOutputStringBuilder.ToString()); } - private async Task RunProcessInternal(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null, StringBuilder processOutputBuilder = null) + private static async Task RunProcessInternalAsync( + string fileName, string arguments, string workingDirectory = null, Action log = null, StringBuilder processOutputBuilder = null) { SemaphoreSlim processExitSemaphore = new SemaphoreSlim(0, 1); @@ -53,11 +54,8 @@ public class ProcessWrapper { if (o.Data != null) { - testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] Error: {o.Data}"); - if (processOutputBuilder != null) - { - processOutputBuilder.AppendLine(o.Data); - } + log?.Invoke($"[{DateTime.UtcNow:O}] Error: {o.Data}"); + processOutputBuilder?.AppendLine(o.Data); } }; @@ -65,11 +63,8 @@ public class ProcessWrapper { if (o.Data != null) { - testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] {o.Data}"); - if (processOutputBuilder != null) - { - processOutputBuilder.AppendLine(o.Data); - } + log?.Invoke($"[{DateTime.UtcNow:O}] {o.Data}"); + processOutputBuilder?.AppendLine(o.Data); } }; @@ -81,7 +76,7 @@ public class ProcessWrapper int wait = 3 * 60 * 1000; if (!await processExitSemaphore.WaitAsync(wait)) { - testOutputHelper?.WriteLine($"Process '{testProcess.Id}' did not exit in {wait}ms."); + log?.Invoke($"Process '{testProcess.Id}' did not exit in {wait}ms."); testProcess.Kill(); } diff --git a/test/Sdk.E2ETests/ProjectBuilder.cs b/test/Sdk.E2ETests/ProjectBuilder.cs new file mode 100644 index 000000000..9fff6b893 --- /dev/null +++ b/test/Sdk.E2ETests/ProjectBuilder.cs @@ -0,0 +1,106 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Azure.Functions.Sdk.E2ETests +{ + public sealed class ProjectBuilder(ITestOutputHelper logger, string project) : IDisposable + { +#if DEBUG + public const string Configuration = "Debug"; +#elif RELEASE + public const string Configuration = "Release"; +#endif + public static readonly string LocalPackages = Path.Combine(TestUtility.PathToRepoRoot, "local"); + public static readonly string SrcRoot = Path.Combine(TestUtility.PathToRepoRoot, "src"); + public static readonly string SdkSolutionRoot = Path.Combine(TestUtility.PathToRepoRoot, "sdk"); + public static readonly string SdkProjectRoot = Path.Combine(SdkSolutionRoot, "Sdk"); + public static readonly string DotNetExecutable = "dotnet"; + public static readonly string SdkVersion = "99.99.99-test"; + public static readonly string SdkBuildProj = Path.Combine(TestUtility.PathToRepoRoot, "build", "Sdk.slnf"); + public static readonly string NuGetOrgPackages = "https://api.nuget.org/v3/index.json"; + + private static Task _initialization; + private static object _sync; + + private readonly TempDirectory _tempDirectory = new(); + + public string OutputPath => _tempDirectory.Path; + + public async Task RestoreAsync() + { + await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync); + logger.WriteLine("Restoring..."); + string dotnetArgs = $"restore {project} -s {NuGetOrgPackages} -s {LocalPackages} -p:SdkVersion={SdkVersion}"; + Stopwatch stopwatch = Stopwatch.StartNew(); + int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)"); + } + + public async Task BuildAsync(string additionalParams = null, bool restore = true) + { + await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync); + + Stopwatch stopwatch = Stopwatch.StartNew(); + logger.WriteLine("Building..."); + string dotnetArgs = $"build {project} -c {Configuration} -o {OutputPath} -p:SdkVersion={SdkVersion} {additionalParams}"; + + if (!restore) + { + dotnetArgs += " --no-restore"; + } + + if (Debugger.IsAttached) + { + dotnetArgs += " -bl"; + } + + int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)"); + } + + public async Task PublishAsync(string additionalParams = null, bool restore = true) + { + await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync); + + Stopwatch stopwatch = Stopwatch.StartNew(); + logger.WriteLine($"Publishing..."); + string dotnetArgs = $"publish {project} -c {Configuration} -o {OutputPath} -p:SdkVersion={SdkVersion} {additionalParams}"; + + if (!restore) + { + dotnetArgs += " --no-restore"; + } + + if (Debugger.IsAttached) + { + dotnetArgs += " -bl"; + } + + int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)"); + } + + private async Task InitializeAsync() + { + logger.WriteLine($"Packing {SdkBuildProj} with version {SdkVersion}"); + string arguments = $"pack {SdkBuildProj} -c {Configuration} -o {LocalPackages} -p:Version={SdkVersion}"; + + int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, arguments, SrcRoot, logger.WriteLine); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + } + + public void Dispose() => _tempDirectory.Dispose(); + } +} diff --git a/test/Sdk.E2ETests/PublishTests.cs b/test/Sdk.E2ETests/PublishTests.cs index d707922e2..9f3cb6544 100644 --- a/test/Sdk.E2ETests/PublishTests.cs +++ b/test/Sdk.E2ETests/PublishTests.cs @@ -11,27 +11,20 @@ namespace Microsoft.Azure.Functions.Sdk.E2ETests { - public class PublishTests + public sealed class PublishTests(ITestOutputHelper testOutputHelper) : IDisposable { - private ITestOutputHelper _testOutputHelper; - - public PublishTests(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } - - [Fact] - public async Task Publish() - { - string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Publish)); - await RunPublishTest(outputDir); - } - - [Fact] - public async Task Publish_Rid() + private readonly ProjectBuilder _builder = new( + testOutputHelper, + Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj")); + + [Theory] + [InlineData("", false)] + [InlineData("-r win-x86", false)] + [InlineData("-p:FunctionsEnableWorkerIndexing=false", true)] + [InlineData("-p:FunctionsEnableWorkerIndexing=false -r win-x86", true)] + public async Task Publish(string parameters, bool metadataGenerated) { - string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Publish_Rid)); - await RunPublishTest(outputDir, "-r win-x86"); + await RunPublishTest(parameters, metadataGenerated); } [Fact] @@ -41,45 +34,51 @@ public async Task Publish_Rid() [Trait("Requirement", "Docker")] public async Task Publish_Container() { - string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Publish_Container)); - var repository = nameof(Sdk.E2ETests).ToLower(); + var repository = "sdk." + nameof(E2ETests).ToLower(); var imageTag = nameof(Publish_Container); // setup test environment state in case there is leftover data from previous runs - await TestUtility.RemoveDockerTestImage(repository, imageTag, _testOutputHelper); + await TestUtility.RemoveDockerTestImage(repository, imageTag, testOutputHelper); - // perform the publish - await RunPublishTest(outputDir, $"--no-restore /t:PublishContainer --property:ContainerRepository={repository} --property:ContainerImageTag={imageTag}"); - - // validate the image base - Tuple inspectResults = await new ProcessWrapper().RunProcessForOutput("docker", $"inspect {repository}:{imageTag} --format \"{{{{ index .Config.Labels \\\"org.opencontainers.image.base.name\\\"}}}}\"", outputDir, _testOutputHelper); - var inspectExitCode = inspectResults.Item1; - var inspectOutput = inspectResults.Item2; - Assert.True(inspectExitCode.HasValue && inspectExitCode.Value == 0); - Assert.Matches("mcr\\.microsoft\\.com/azure-functions/dotnet-isolated:(\\d)+-dotnet-isolated(\\d+\\.\\d+)", inspectOutput); - - // clean up - await TestUtility.RemoveDockerTestImage(repository, imageTag, _testOutputHelper); + try + { + // perform the publish + await RunPublishTest($"-t:PublishContainer -p:ContainerRepository={repository} -p:ContainerImageTag={imageTag}", false); + + // validate the image base + Tuple inspectResults = await ProcessWrapper.RunProcessForOutputAsync( + "docker", + $"inspect {repository}:{imageTag} --format \"{{{{ index .Config.Labels \\\"org.opencontainers.image.base.name\\\"}}}}\"", + _builder.OutputPath, + testOutputHelper.WriteLine); + + var inspectExitCode = inspectResults.Item1; + var inspectOutput = inspectResults.Item2; + Assert.True(inspectExitCode.HasValue && inspectExitCode.Value == 0); + Assert.Matches("mcr\\.microsoft\\.com/azure-functions/dotnet-isolated:(\\d)+-dotnet-isolated(\\d+\\.\\d+)", inspectOutput); + } + finally + { + // clean up + await TestUtility.RemoveDockerTestImage(repository, imageTag, testOutputHelper); + } } - private async Task RunPublishTest(string outputDir, string additionalParams = null) + private async Task RunPublishTest(string additionalParams, bool metadataGenerated) { - // Name of the csproj - string projectFileDirectory = Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj"); - - await TestUtility.RestoreAndPublishProjectAsync(projectFileDirectory, outputDir, additionalParams, _testOutputHelper); + await _builder.PublishAsync(additionalParams); // Make sure files are in /.azurefunctions - string azureFunctionsDir = Path.Combine(outputDir, ".azurefunctions"); + string azureFunctionsDir = Path.Combine(_builder.OutputPath, ".azurefunctions"); Assert.True(Directory.Exists(azureFunctionsDir)); // Verify files are present string metadataLoaderPath = Path.Combine(azureFunctionsDir, "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.dll"); - string extensionsJsonPath = Path.Combine(outputDir, "extensions.json"); - string functionsMetadataPath = Path.Combine(outputDir, "functions.metadata"); - Assert.True(File.Exists(metadataLoaderPath)); + string extensionsJsonPath = Path.Combine(_builder.OutputPath, "extensions.json"); + string functionsMetadataPath = Path.Combine(_builder.OutputPath, "functions.metadata"); Assert.True(File.Exists(extensionsJsonPath)); - Assert.True(File.Exists(functionsMetadataPath)); + Assert.True(File.Exists(metadataLoaderPath)); + Assert.Equal(metadataGenerated, File.Exists(functionsMetadataPath)); // Verify extensions.json JObject jObjects = JObject.Parse(File.ReadAllText(extensionsJsonPath)); @@ -102,26 +101,24 @@ private async Task RunPublishTest(string outputDir, string additionalParams = nu Assert.True(JToken.DeepEquals(extensionsJsonContents, expected), $"Actual: {extensionsJsonContents}{Environment.NewLine}Expected: {expected}"); // Verify functions.metadata - TestUtility.ValidateFunctionsMetadata(functionsMetadataPath, "Microsoft.Azure.Functions.Sdk.E2ETests.Contents.functions.metadata"); - } - - private class Extension - { - public Extension(string name, string typeName, string hintPath) + if (metadataGenerated) { - Name = name; - TypeName = typeName; - HintPath = hintPath; + TestUtility.ValidateFunctionsMetadata(functionsMetadataPath, "Microsoft.Azure.Functions.Sdk.E2ETests.Contents.functions.metadata"); } + } + + public void Dispose() => _builder.Dispose(); + private class Extension(string name, string typeName, string hintPath) + { [JsonProperty("name")] - public string Name { get; set; } + public string Name { get; set; } = name; [JsonProperty("typeName")] - public string TypeName { get; set; } + public string TypeName { get; set; } = typeName; [JsonProperty("hintPath")] - public string HintPath { get; set; } + public string HintPath { get; set; } = hintPath; } } } diff --git a/test/Sdk.E2ETests/TempDirectory.cs b/test/Sdk.E2ETests/TempDirectory.cs new file mode 100644 index 000000000..8afa936f8 --- /dev/null +++ b/test/Sdk.E2ETests/TempDirectory.cs @@ -0,0 +1,41 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.IO; +using IOPath = System.IO.Path; + +namespace Microsoft.Azure.Functions.Sdk.E2ETests +{ + public sealed class TempDirectory : IDisposable + { + public TempDirectory() : this(IOPath.Combine(IOPath.GetTempPath(), IOPath.GetRandomFileName())) + { + } + + public TempDirectory(string path) + { + ArgumentNullException.ThrowIfNull(path); + Path = path; + + if (!Directory.Exists(Path)) + { + Directory.CreateDirectory(Path); + } + } + + public string Path { get; } + + public void Dispose() + { + try + { + Directory.Delete(Path, true); + } + catch (IOException) + { + // Ignore IO exceptions during cleanup + } + } + } +} diff --git a/test/Sdk.E2ETests/TestUtility.cs b/test/Sdk.E2ETests/TestUtility.cs index b1217ad3f..26f0b6a98 100644 --- a/test/Sdk.E2ETests/TestUtility.cs +++ b/test/Sdk.E2ETests/TestUtility.cs @@ -26,120 +26,29 @@ public static class TestUtility public const string Net50 = "net5.0"; // Paths and executables - public static readonly string DotNetExecutable = "dotnet"; - public static readonly string PathToRepoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../../")); - public static readonly string SrcRoot = Path.Combine(PathToRepoRoot, "src"); - public static readonly string SdkSolutionRoot = Path.Combine(PathToRepoRoot, "sdk"); - public static readonly string SdkProjectRoot = Path.Combine(SdkSolutionRoot, "Sdk"); + public static readonly string PathToRepoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, @"../../../../../")); public static readonly string TestRoot = Path.Combine(PathToRepoRoot, "test"); public static readonly string SamplesRoot = Path.Combine(PathToRepoRoot, "samples"); - public static readonly string LocalPackages = Path.Combine(PathToRepoRoot, "local"); - public static readonly string TestOutputDir = Path.Combine(Path.GetTempPath(), "FunctionsWorkerSdk.E2ETests"); public static readonly string TestResourcesProjectsRoot = Path.Combine(TestRoot, "Resources", "Projects"); - public static readonly string NuGetOrgPackages = "https://api.nuget.org/v3/index.json"; - public static readonly string NuGetPackageSource = LocalPackages; - public static readonly string SdkVersion = "99.99.99-test"; - public static readonly string SdkBuildProj = Path.Combine(PathToRepoRoot, "build", "Sdk.slnf"); - - private static bool _isInitialized = false; - - public static async Task InitializeTestAsync(ITestOutputHelper testOutputHelper, string testName) - { - if (!_isInitialized) - { - testOutputHelper.WriteLine($"Packing {SdkBuildProj} with version {SdkVersion}"); - string arguments = $"pack {SdkBuildProj} -c {Configuration} -o {LocalPackages} -p:Version={SdkVersion}"; - - int? exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, arguments, SrcRoot, testOutputHelper); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - - _isInitialized = true; - } - - return InitializeOutputDir(testName); - } - public static void ValidateFunctionsMetadata(string actualFilePath, string embeddedResourceName) { JToken functionsMetadataContents = JToken.Parse(File.ReadAllText(actualFilePath)); var assembly = Assembly.GetExecutingAssembly(); string resourceName = assembly.GetManifestResourceNames() .Single(str => str.EndsWith(embeddedResourceName)); - using (Stream stream = assembly.GetManifestResourceStream(resourceName)) - { - using (StreamReader reader = new StreamReader(stream)) - { - using (var jsonReader = new JsonTextReader(reader)) - { - JsonSerializer serializer = new JsonSerializer(); - var expected = serializer.Deserialize(jsonReader); - Assert.True(JToken.DeepEquals(functionsMetadataContents, expected), $"Actual: {functionsMetadataContents}{Environment.NewLine}Expected: {expected}"); - } - } - } - } - - public static async Task RestoreAndBuildProjectAsync(string fullPathToProjFile, string outputDir, string additionalParams, ITestOutputHelper outputHelper) - { - // Name of the csproj - string projectNameToTest = Path.GetFileName(fullPathToProjFile); - string projectFileDirectory = Path.GetDirectoryName(fullPathToProjFile); - - // Restore - outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Restoring..."); - string dotnetArgs = $"restore {projectNameToTest} -s {NuGetOrgPackages} -s {LocalPackages} -p:SdkVersion={SdkVersion}"; - int? exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done."); - - // Build - outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Building..."); - dotnetArgs = $"build {projectNameToTest} --configuration {Configuration} -o {outputDir} -p:SdkVersion={SdkVersion} {additionalParams}"; - exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done."); - } - - public static async Task RestoreAndPublishProjectAsync(string fullPathToProjFile, string outputDir, string additionalParams, ITestOutputHelper outputHelper) - { - // Name of the csproj - string projectNameToTest = Path.GetFileName(fullPathToProjFile); - string projectFileDirectory = Path.GetDirectoryName(fullPathToProjFile); - - // Restore - outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Restoring..."); - string dotnetArgs = $"restore {projectNameToTest} -s {NuGetOrgPackages} -s {LocalPackages} -p:SdkVersion={SdkVersion}"; - int? exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done."); - - // Publish - outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Publishing..."); - dotnetArgs = $"publish {projectNameToTest} --configuration {Configuration} -o {outputDir} -p:SdkVersion={SdkVersion} {additionalParams}"; - exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done."); - } - - private static string InitializeOutputDir(string testName) - { - string outputDir = Path.Combine(TestOutputDir, testName); - - if (Directory.Exists(outputDir)) - { - Directory.Delete(outputDir, recursive: true); - } - - Directory.CreateDirectory(outputDir); - - return outputDir; + using Stream stream = assembly.GetManifestResourceStream(resourceName); + using StreamReader reader = new StreamReader(stream); + using var jsonReader = new JsonTextReader(reader); + JsonSerializer serializer = new JsonSerializer(); + var expected = serializer.Deserialize(jsonReader); + Assert.True(JToken.DeepEquals(functionsMetadataContents, expected), $"Actual: {functionsMetadataContents}{Environment.NewLine}Expected: {expected}"); } public static async Task RemoveDockerTestImage(string repository, string imageTag, ITestOutputHelper outputHelper) { outputHelper.WriteLine($"Removing image {repository}:{imageTag} from local registry"); - int? rmiExitCode = await new ProcessWrapper().RunProcess("docker", $"rmi -f {repository}:{imageTag}", TestOutputDir, outputHelper); + int? rmiExitCode = await ProcessWrapper.RunProcessAsync("docker", $"rmi -f {repository}:{imageTag}", log: outputHelper.WriteLine); Assert.True(rmiExitCode.HasValue && rmiExitCode.Value == 0); // daemon may still error if the image doesn't exist, but it will still return 0 } } diff --git a/test/Sdk.E2ETests/ZipDeployTests.cs b/test/Sdk.E2ETests/ZipDeployTests.cs index 5d8c522a2..b683a5041 100644 --- a/test/Sdk.E2ETests/ZipDeployTests.cs +++ b/test/Sdk.E2ETests/ZipDeployTests.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.InteropServices; using System.Threading.Tasks; using ICSharpCode.SharpZipLib.Zip; using Microsoft.NET.Sdk.Functions.MSBuild.Tasks; @@ -9,14 +8,11 @@ namespace Microsoft.Azure.Functions.Sdk.E2ETests { - public class ZipDeployTests + public sealed class ZipDeployTests(ITestOutputHelper testOutputHelper) : IDisposable { - private ITestOutputHelper _testOutputHelper; - - public ZipDeployTests(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } + private readonly ProjectBuilder _builder = new( + testOutputHelper, + Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj")); [Theory] [InlineData("linux-x64", true)] @@ -25,9 +21,7 @@ public ZipDeployTests(ITestOutputHelper testOutputHelper) public async Task CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContained(string rid, bool selfContained) { string testName = nameof(CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContained); - string directoryToZip = await TestUtility.InitializeTestAsync(_testOutputHelper, testName); - - string zipName = Path.Combine(Directory.GetParent(directoryToZip).FullName, $"{testName}.zip"); + string zipName = Path.Combine(Directory.GetParent(_builder.OutputPath).FullName, $"{testName}.zip"); if (File.Exists(zipName)) { @@ -36,13 +30,13 @@ public async Task CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContaine string projectFileDirectory = Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj"); - await TestUtility.RestoreAndPublishProjectAsync( - projectFileDirectory, directoryToZip, $"-r {rid} --self-contained {selfContained}", _testOutputHelper); + await _builder.PublishAsync($"-r {rid} --self-contained {selfContained}"); - CreateZipFileTask.CreateZipFileFromDirectory(directoryToZip, zipName); + CreateZipFileTask.CreateZipFileFromDirectory(_builder.OutputPath, zipName); using var zip = new ZipFile(zipName); - Assert.Equal(Directory.GetFiles(directoryToZip, "*", SearchOption.AllDirectories).Length, zip.Count); + Assert.Equal(Directory.GetFiles(_builder.OutputPath, "*", SearchOption.AllDirectories).Length, zip.Count); + foreach (ZipEntry entry in zip) { if (selfContained && (entry.Name == "FunctionApp" || entry.Name == "FunctionApp.exe")) @@ -64,6 +58,10 @@ await TestUtility.RestoreAndPublishProjectAsync( Assert.NotEqual(0, entry.ExternalFileAttributes); } } + + zip.Close(); } + + public void Dispose() => _builder.Dispose(); } } From ba0f4dc6e0318af9a493198179dc46a29afb7e57 Mon Sep 17 00:00:00 2001 From: sarah <35204912+satvu@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:59:53 -0700 Subject: [PATCH 03/45] Clear release notes (#3103) --- extensions/Worker.Extensions.EventHubs/release_notes.md | 4 ++-- extensions/Worker.Extensions.ServiceBus/release_notes.md | 4 ++-- extensions/Worker.Extensions.Tables/release_notes.md | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/extensions/Worker.Extensions.EventHubs/release_notes.md b/extensions/Worker.Extensions.EventHubs/release_notes.md index aa498c751..3d6e918a8 100644 --- a/extensions/Worker.Extensions.EventHubs/release_notes.md +++ b/extensions/Worker.Extensions.EventHubs/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.EventHubs 6.5.0 +### Microsoft.Azure.Functions.Worker.Extensions.EventHubs -- Updating `Microsoft.Azure.WebJobs.Extensions.EventHubs` reference to 6.5.2 (#3098) +- diff --git a/extensions/Worker.Extensions.ServiceBus/release_notes.md b/extensions/Worker.Extensions.ServiceBus/release_notes.md index eb4cbfcb5..c92b7d33c 100644 --- a/extensions/Worker.Extensions.ServiceBus/release_notes.md +++ b/extensions/Worker.Extensions.ServiceBus/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus 5.23.0 +### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus -- Update dependency `Microsoft.Azure.WebJobs.Extensions.ServiceBus` to 5.17.0 (#3098) \ No newline at end of file +- \ No newline at end of file diff --git a/extensions/Worker.Extensions.Tables/release_notes.md b/extensions/Worker.Extensions.Tables/release_notes.md index bffbc10ad..771cce0ca 100644 --- a/extensions/Worker.Extensions.Tables/release_notes.md +++ b/extensions/Worker.Extensions.Tables/release_notes.md @@ -4,7 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.Tables 1.5.0 +### Microsoft.Azure.Functions.Worker.Extensions.Tables -- Support deferred binding for poco types in Table Input binding (#3003) -- Update dependency `Microsoft.Azure.WebJobs.Extensions.Tables` to 1.4.0 (#3098) \ No newline at end of file +- \ No newline at end of file From 842276ea22342e722e8de1621e3f16084a7b0839 Mon Sep 17 00:00:00 2001 From: Jamie Magee Date: Wed, 16 Jul 2025 12:55:54 -0700 Subject: [PATCH 04/45] Add Dependabot configuration for .NET SDK updates (#3102) --- .github/dependabot.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..a1ef8b6a8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "dotnet-sdk" + directory: "/" + schedule: + interval: "weekly" + day: "wednesday" + ignore: + - dependency-name: "*" + update-types: + - "version-update:semver-major" + - "version-update:semver-minor" From c26e8660c5d1326a9b3822f023df10ede121c22a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steinblock?= Date: Thu, 17 Jul 2025 17:59:04 +0200 Subject: [PATCH 05/45] Fix ServiceBusMessageActions.cs AbandonMessageAsync doc inheritance (#3112) The AbandonMessageAsync method has a /// item, while it should inherit from `ServiceBusReceiver.AbandonMessageAsync` --- .../src/ServiceBusMessageActions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/Worker.Extensions.ServiceBus/src/ServiceBusMessageActions.cs b/extensions/Worker.Extensions.ServiceBus/src/ServiceBusMessageActions.cs index ad8bd4e47..f4b64825c 100644 --- a/extensions/Worker.Extensions.ServiceBus/src/ServiceBusMessageActions.cs +++ b/extensions/Worker.Extensions.ServiceBus/src/ServiceBusMessageActions.cs @@ -81,7 +81,7 @@ public virtual async Task CompleteMessageAsync( await _settlement.CompleteAsync(new() { Locktoken = message.LockToken }, cancellationToken: cancellationToken); } - /// + /// public virtual async Task AbandonMessageAsync( ServiceBusReceivedMessage message, IDictionary? propertiesToModify = default, From d26b46f1a75883908d2367ae3700c3ef9be5fa47 Mon Sep 17 00:00:00 2001 From: Jacob Viau Date: Tue, 22 Jul 2025 15:25:56 -0700 Subject: [PATCH 06/45] Fix steps indentation (#3117) --- eng/ci/host/official-release.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/eng/ci/host/official-release.yml b/eng/ci/host/official-release.yml index 8885134fd..dac00071a 100644 --- a/eng/ci/host/official-release.yml +++ b/eng/ci/host/official-release.yml @@ -94,13 +94,13 @@ extends: artifactName: $(artifact_name) pipeline: build - steps: - - task: 1ES.PublishNuget@1 - displayName: Publish packages - inputs: - packagesToPush: $(packages_pattern) - packageParentPath: $(drop_path) - publishVstsFeed: $(nuget_feed) - nuGetFeedType: internal - allowPackageConflicts: true - publishPackageMetadata: true + steps: + - task: 1ES.PublishNuget@1 + displayName: Publish packages + inputs: + packagesToPush: $(packages_pattern) + packageParentPath: $(drop_path) + publishVstsFeed: $(nuget_feed) + nuGetFeedType: internal + allowPackageConflicts: true + publishPackageMetadata: true From fe00d7cf39fb9c635edc6a713b675689ead93b6f Mon Sep 17 00:00:00 2001 From: Ciaran Liedeman <3578740+cliedeman@users.noreply.github.com> Date: Thu, 31 Jul 2025 17:11:18 +0200 Subject: [PATCH 07/45] Disable compiler warning CS0618 in DirectFunctionExecutor (#2952) * Disable compiler warning CS0618 in DirectFunctionExecutor * Use SymbolEqualityComparer * fix: removed hardcoded newline character * chore: feedback --- .../FunctionExecutorGenerator.Emitter.cs | 27 +++-- ...ionExecutorGenerator.ExecutableFunction.cs | 5 + .../FunctionExecutorGenerator.Parser.cs | 4 + sdk/Sdk.Generators/KnownTypes.cs | 4 + .../FunctionExecutorGeneratorTests.cs | 99 +++++++++++++++++++ 5 files changed, 129 insertions(+), 10 deletions(-) diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs index 022d5b676..199b4f61c 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Emitter.cs @@ -158,7 +158,7 @@ private static string GetMethodBody(IEnumerable functions, b if (string.Equals(context.FunctionDefinition.EntryPoint, "{{function.EntryPoint}}", StringComparison.Ordinal)) { - {{(fast ? EmitFastPath(function) : EmitSlowPath())}} + {{(fast ? EmitFastPath(function) : EmitSlowPath())}} return; } """); @@ -170,21 +170,22 @@ private static string GetMethodBody(IEnumerable functions, b private static string EmitFastPath(ExecutableFunction function) { var sb = new StringBuilder(); - + if (!function.IsStatic) { sb.Append($""" - var instanceType = types["{function.ParentFunctionClassName}"]; + var instanceType = types["{function.ParentFunctionClassName}"]; var i = _functionActivator.CreateInstance(instanceType, context) as {function.ParentFunctionFullyQualifiedClassName}; + """); } - sb.Append(!function.IsStatic - ? """ - - - """ - : " "); + if (function.IsObsolete) + { + sb.AppendLine("#pragma warning disable CS0618"); + } + + sb.Append(" "); if (function.IsReturnValueAssignable) { @@ -210,13 +211,19 @@ private static string EmitFastPath(ExecutableFunction function) sb.Append(function.IsStatic ? $"{function.ParentFunctionFullyQualifiedClassName}.{function.MethodName}({methodParamsStr});" : $"i.{function.MethodName}({methodParamsStr});"); + + if (function.IsObsolete) + { + sb.Append(Constants.NewLine); + sb.Append("#pragma warning restore CS0618"); + } return sb.ToString(); } private static string EmitSlowPath() { - return " await _defaultExecutor.Value.ExecuteAsync(context);"; + return " await _defaultExecutor.Value.ExecuteAsync(context);"; } } } diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs index 2dbcfe7b6..b9821b305 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.ExecutableFunction.cs @@ -63,5 +63,10 @@ internal class ExecutableFunction /// ex: FooAssembly, Version=1.2.3.4, Culture=neutral, PublicKeyToken=9475d07f10cb09df /// internal string AssemblyIdentity { get; set; } = null!; + + /// + /// Gets or sets if the function is Obsolete. + /// + internal bool IsObsolete { get; set; } } } diff --git a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs index fd6c570fc..c4b0b747f 100644 --- a/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionExecutor/FunctionExecutorGenerator.Parser.cs @@ -42,6 +42,9 @@ internal ICollection GetFunctions(IEnumerable var defaultFormatClassName = method.ContainingSymbol.ToDisplayString(); var fullyQualifiedClassName = method.ContainingSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + var isObsolete = method.GetAttributes() + .Any(attr => SymbolEqualityComparer.Default.Equals(attr.AttributeClass, _knownTypes.ObsoleteAttr)); + var function = new ExecutableFunction { EntryPoint = $"{defaultFormatClassName}.{method.Name}", @@ -54,6 +57,7 @@ internal ICollection GetFunctions(IEnumerable ParentFunctionFullyQualifiedClassName = fullyQualifiedClassName, Visibility = method.GetVisibility(), AssemblyIdentity = method.ContainingAssembly.Identity.GetDisplayName(), + IsObsolete = isObsolete, }; functionList.Add(function); diff --git a/sdk/Sdk.Generators/KnownTypes.cs b/sdk/Sdk.Generators/KnownTypes.cs index fc32225cc..81321ef38 100644 --- a/sdk/Sdk.Generators/KnownTypes.cs +++ b/sdk/Sdk.Generators/KnownTypes.cs @@ -27,6 +27,7 @@ internal readonly struct KnownTypes private readonly Lazy _readOnlyMemoryOfBytes; private readonly Lazy _lookupGeneric; private readonly Lazy _dictionaryGeneric; + private readonly Lazy _obsoleteAttr; internal KnownTypes(Compilation compilation) { @@ -44,6 +45,7 @@ internal KnownTypes(Compilation compilation) _readOnlyMemoryOfBytes = new Lazy(() => compilation.GetTypeByMetadataName(typeof(ReadOnlyMemory).FullName)!); _lookupGeneric = new Lazy(() => compilation.GetTypeByMetadataName(typeof(ILookup<,>).FullName)!); _dictionaryGeneric = new Lazy(() => compilation.GetTypeByMetadataName(typeof(IDictionary<,>).FullName)!); + _obsoleteAttr = new Lazy(() => compilation.GetTypeByMetadataName(typeof(ObsoleteAttribute).FullName)!); } public INamedTypeSymbol TaskType { get => _taskType.Value; } @@ -73,5 +75,7 @@ internal KnownTypes(Compilation compilation) public INamedTypeSymbol LookupGeneric { get => _lookupGeneric.Value; } public INamedTypeSymbol DictionaryGeneric { get => _dictionaryGeneric.Value; } + + public INamedTypeSymbol ObsoleteAttr { get => _obsoleteAttr.Value; } } } diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs index 289e8c2c5..dae5ca8db 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/FunctionExecutorGeneratorTests.cs @@ -816,5 +816,104 @@ public static IHostBuilder ConfigureGeneratedFunctionExecutor(this IHostBuilder } """; } + + [Theory] + [InlineData(LanguageVersion.CSharp7_3)] + [InlineData(LanguageVersion.CSharp8)] + [InlineData(LanguageVersion.CSharp9)] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersion.Latest)] + public async Task ObsoleteFunction(LanguageVersion languageVersion) + { + const string inputSourceCode = @" +using System; +using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using Azure.Storage.Queues.Models; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.Extensions.Logging; +namespace TestProject +{ + public class TestProject + { + [Function(""FunctionA"")] + [Obsolete(""Do not use"")] + public HttpResponseData Foo([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + + [Function(""FunctionB"")] + [Obsolete(""Do not use"")] + public static HttpResponseData FooStatic([HttpTrigger(AuthorizationLevel.User, ""get"")] HttpRequestData r, FunctionContext c) + { + return r.CreateResponse(System.Net.HttpStatusCode.OK); + } + } +} +"; + var expectedOutput = $@"// +using System; +using System.Threading.Tasks; +using System.Collections.Generic; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Context.Features; +using Microsoft.Azure.Functions.Worker.Invocation; +namespace TestProject +{{ + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + {Constants.GeneratedCodeAttribute} + internal class DirectFunctionExecutor : global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor + {{ + private readonly global::Microsoft.Azure.Functions.Worker.IFunctionActivator _functionActivator; + private readonly Dictionary types = new Dictionary() + {{ + {{ ""TestProject.TestProject"", Type.GetType(""TestProject.TestProject, TestProject, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"") }} + }}; + + public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunctionActivator functionActivator) + {{ + _functionActivator = functionActivator ?? throw new global::System.ArgumentNullException(nameof(functionActivator)); + }} + + /// + public async global::System.Threading.Tasks.ValueTask ExecuteAsync(global::Microsoft.Azure.Functions.Worker.FunctionContext context) + {{ + var inputBindingFeature = context.Features.Get(); + var inputBindingResult = await inputBindingFeature.BindFunctionInputAsync(context); + var inputArguments = inputBindingResult.Values; + + if (string.Equals(context.FunctionDefinition.EntryPoint, ""TestProject.TestProject.Foo"", StringComparison.Ordinal)) + {{ + var instanceType = types[""TestProject.TestProject""]; + var i = _functionActivator.CreateInstance(instanceType, context) as global::TestProject.TestProject; +#pragma warning disable CS0618 + context.GetInvocationResult().Value = i.Foo((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); +#pragma warning restore CS0618 + return; + }} + if (string.Equals(context.FunctionDefinition.EntryPoint, ""TestProject.TestProject.FooStatic"", StringComparison.Ordinal)) + {{ +#pragma warning disable CS0618 + context.GetInvocationResult().Value = global::TestProject.TestProject.FooStatic((global::Microsoft.Azure.Functions.Worker.Http.HttpRequestData)inputArguments[0], (global::Microsoft.Azure.Functions.Worker.FunctionContext)inputArguments[1]); +#pragma warning restore CS0618 + return; + }} + }} + }} +{GetExpectedExtensionMethodCode()} +}}".Replace("'", "\""); + + await TestHelpers.RunTestAsync( + _referencedAssemblies, + inputSourceCode, + Constants.FileNames.GeneratedFunctionExecutor, + expectedOutput, + languageVersion: languageVersion); + } } } From 2efafa83f9c6e9371a6b6adb2fc366d15c53de23 Mon Sep 17 00:00:00 2001 From: Aishwarya Bhandari <37918412+aishwaryabh@users.noreply.github.com> Date: Fri, 8 Aug 2025 10:18:25 -0700 Subject: [PATCH 08/45] Adding support for running `dotnet run` after installing core tools thru npm (#3127) * adding windows target * updating release notes * addressing comments --- ...Microsoft.Azure.Functions.Worker.Sdk.targets | 17 ++++++++++++----- sdk/release_notes.md | 1 + 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets index b08c2e352..8133074c8 100644 --- a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets +++ b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets @@ -106,12 +106,19 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and - - - func - host start $(RunArguments) - $(OutDir) + + + cmd + /C func start $(RunArguments) + $(OutDir) + + + + + func + start $(RunArguments) + $(OutDir) diff --git a/sdk/release_notes.md b/sdk/release_notes.md index 3d4479236..fb9fe089c 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -7,6 +7,7 @@ ### Microsoft.Azure.Functions.Worker.Sdk - Build no longer generates `functions.metadata` if source-generated metadata provider is enabled. (#2974) +- Fixing `dotnet run` to work on Windows when core tools is installed from NPM (#3127) ### Microsoft.Azure.Functions.Worker.Sdk.Generators From 0a74914c7b1fe98d7d75c63b9a19852e32dcb569 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Aug 2025 12:13:56 -0700 Subject: [PATCH 09/45] Bump dotnet-sdk from 9.0.201 to 9.0.304 (#3129) Bumps [dotnet-sdk](https://github.com/dotnet/sdk) from 9.0.201 to 9.0.304. - [Release notes](https://github.com/dotnet/sdk/releases) - [Commits](https://github.com/dotnet/sdk/compare/v9.0.201...v9.0.304) --- updated-dependencies: - dependency-name: dotnet-sdk dependency-version: 9.0.304 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index c8f2593ac..03064daa1 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.201", + "version": "9.0.304", "allowPrerelease": true, "rollForward": "latestFeature" }, From d0cf055d5fcff24e324cea9960dd680c2a98c0bf Mon Sep 17 00:00:00 2001 From: Matthew Henderson Date: Mon, 25 Aug 2025 14:23:19 -0700 Subject: [PATCH 10/45] Adding .NET 10 issue template (#3153) --- .../02b-net10-question-or-issue.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/02b-net10-question-or-issue.yml diff --git a/.github/ISSUE_TEMPLATE/02b-net10-question-or-issue.yml b/.github/ISSUE_TEMPLATE/02b-net10-question-or-issue.yml new file mode 100644 index 000000000..a2b7b595b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02b-net10-question-or-issue.yml @@ -0,0 +1,15 @@ +name: .NET 10 issue or question +description: Ask questions or raise issues related to .NET 10 +labels: [".NET 10"] +body: +- type: markdown + attributes: + value: | + Please use this template for any issues you have using .NET 10 or for any questions not answered in our [tracking thread for .NET 10 support](https://github.com/Azure/azure-functions-dotnet-worker/issues/3152). +- id: description + type: textarea + attributes: + label: Description + placeholder: Please provide a succinct description of the question or issue. For issue reports, please include the versions of the Azure Functions packages your projects references. + validations: + required: true \ No newline at end of file From d066058a0348f5c84b5ef9c2671ba87d2efd1c26 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Wed, 27 Aug 2025 14:46:22 -0700 Subject: [PATCH 11/45] Fix Azure Functions logo URL in README.md (#3154) * Initial plan * Fix logo URL in README.md Co-authored-by: liliankasem <2198905+liliankasem@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: liliankasem <2198905+liliankasem@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1375a8cbe..4ecf09044 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Azure Functions Logo](https://raw.githubusercontent.com/Azure/azure-functions-cli/master/src/Azure.Functions.Cli/npm/assets/azure-functions-logo-color-raster.png) +![Azure Functions Logo](https://raw.githubusercontent.com/Azure/azure-functions-cli/refs/heads/main/eng/res/functions.png) |Branch|Status| |---|---| From 23110d03d772ebefae99ae0a265cdf856e9cdc4a Mon Sep 17 00:00:00 2001 From: Matthew Henderson Date: Wed, 27 Aug 2025 15:13:58 -0700 Subject: [PATCH 12/45] Updating extension dependencies (#3155) --- .../Worker.Extensions.EventGrid/release_notes.md | 4 ++-- .../src/Worker.Extensions.EventGrid.csproj | 4 ++-- .../src/Worker.Extensions.Storage.Blobs.csproj | 6 +++--- .../src/Worker.Extensions.Storage.Queues.csproj | 4 ++-- .../Worker.Extensions.Storage/release_notes.md | 13 +++++++------ .../src/Worker.Extensions.Storage.csproj | 2 +- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/extensions/Worker.Extensions.EventGrid/release_notes.md b/extensions/Worker.Extensions.EventGrid/release_notes.md index 65290e0c6..f2aa5ff19 100644 --- a/extensions/Worker.Extensions.EventGrid/release_notes.md +++ b/extensions/Worker.Extensions.EventGrid/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.EventGrid +### Microsoft.Azure.Functions.Worker.Extensions.EventGrid 3.6.0 -- \ No newline at end of file +- Updating `Microsoft.Azure.WebJobs.Extensions.EventGrid` reference to 3.5.0 (#3155) \ No newline at end of file diff --git a/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj b/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj index d45de0ec0..d4f94c919 100644 --- a/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj +++ b/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj @@ -6,7 +6,7 @@ Azure Event Grid extensions for .NET isolated functions - 3.5.0 + 3.6.0 false @@ -24,7 +24,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj b/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj index b4c9ad017..6475cd6fd 100644 --- a/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj +++ b/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj @@ -6,7 +6,7 @@ Azure Blob Storage extensions for .NET isolated functions - 6.7.0 + 6.8.0 false @@ -20,7 +20,7 @@ - + @@ -29,7 +29,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj b/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj index 6333b6c85..7b4590d06 100644 --- a/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj +++ b/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj @@ -6,7 +6,7 @@ Azure Queue Storage extensions for .NET isolated functions - 5.5.2 + 5.5.3 false @@ -28,7 +28,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage/release_notes.md b/extensions/Worker.Extensions.Storage/release_notes.md index 4ab75b73f..32976bcb3 100644 --- a/extensions/Worker.Extensions.Storage/release_notes.md +++ b/extensions/Worker.Extensions.Storage/release_notes.md @@ -4,14 +4,15 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.Storage +### Microsoft.Azure.Functions.Worker.Extensions.Storage 6.8.0 -- +- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Blobs` reference to 5.3.5 (#3155) +- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Queues` reference to 5.3.5 (#3155) -### Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs +### Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs 6.8.0 -- +- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Blobs` reference to 5.3.5 (#3155) -### Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues +### Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues 5.5.3 -- +- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Queues` reference to 5.3.5 (#3155) diff --git a/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj b/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj index af4803771..6fbeccbaa 100644 --- a/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj +++ b/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj @@ -6,7 +6,7 @@ Azure Storage extensions for .NET isolated functions - 6.7.0 + 6.8.0 false From 64675cdfe8bfcfbdf59aa11b94e1f9ca6023ba73 Mon Sep 17 00:00:00 2001 From: Matthew Henderson Date: Wed, 3 Sep 2025 15:46:45 -0700 Subject: [PATCH 13/45] Updating extension dependencies (Cosmos DB) (#3157) --- extensions/Worker.Extensions.CosmosDB/release_notes.md | 5 +++-- .../src/Worker.Extensions.CosmosDB.csproj | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/extensions/Worker.Extensions.CosmosDB/release_notes.md b/extensions/Worker.Extensions.CosmosDB/release_notes.md index 921fdcf34..f0faef598 100644 --- a/extensions/Worker.Extensions.CosmosDB/release_notes.md +++ b/extensions/Worker.Extensions.CosmosDB/release_notes.md @@ -4,6 +4,7 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB +### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB 4.13.0 -- +- Update dependency `Microsoft.Azure.WebJobs.Extensions.CosmosDB` to 4.10.0 (#3157) +- Update dependency `Microsoft.Extensions.Azure` to 1.12.0 (#3157) diff --git a/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj b/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj index b5cb4b712..8217dac2e 100644 --- a/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj +++ b/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj @@ -6,7 +6,7 @@ Azure Cosmos DB extensions for .NET isolated functions - 4.12.0 + 4.13.0 false @@ -21,7 +21,7 @@ - + @@ -31,7 +31,7 @@ - + \ No newline at end of file From 194d7849ce9097aab51ee6af9908c5d3398ffd7e Mon Sep 17 00:00:00 2001 From: Fabio Cavalcante Date: Thu, 4 Sep 2025 10:32:16 -0700 Subject: [PATCH 14/45] Revert "Updating extension dependencies (#3155)" (#3159) This reverts commit 23110d03d772ebefae99ae0a265cdf856e9cdc4a. --- .../Worker.Extensions.EventGrid/release_notes.md | 4 ++-- .../src/Worker.Extensions.EventGrid.csproj | 4 ++-- .../src/Worker.Extensions.Storage.Blobs.csproj | 6 +++--- .../src/Worker.Extensions.Storage.Queues.csproj | 4 ++-- .../Worker.Extensions.Storage/release_notes.md | 13 ++++++------- .../src/Worker.Extensions.Storage.csproj | 2 +- 6 files changed, 16 insertions(+), 17 deletions(-) diff --git a/extensions/Worker.Extensions.EventGrid/release_notes.md b/extensions/Worker.Extensions.EventGrid/release_notes.md index f2aa5ff19..65290e0c6 100644 --- a/extensions/Worker.Extensions.EventGrid/release_notes.md +++ b/extensions/Worker.Extensions.EventGrid/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.EventGrid 3.6.0 +### Microsoft.Azure.Functions.Worker.Extensions.EventGrid -- Updating `Microsoft.Azure.WebJobs.Extensions.EventGrid` reference to 3.5.0 (#3155) \ No newline at end of file +- \ No newline at end of file diff --git a/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj b/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj index d4f94c919..d45de0ec0 100644 --- a/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj +++ b/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj @@ -6,7 +6,7 @@ Azure Event Grid extensions for .NET isolated functions - 3.6.0 + 3.5.0 false @@ -24,7 +24,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj b/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj index 6475cd6fd..b4c9ad017 100644 --- a/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj +++ b/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj @@ -6,7 +6,7 @@ Azure Blob Storage extensions for .NET isolated functions - 6.8.0 + 6.7.0 false @@ -20,7 +20,7 @@ - + @@ -29,7 +29,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj b/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj index 7b4590d06..6333b6c85 100644 --- a/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj +++ b/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj @@ -6,7 +6,7 @@ Azure Queue Storage extensions for .NET isolated functions - 5.5.3 + 5.5.2 false @@ -28,7 +28,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage/release_notes.md b/extensions/Worker.Extensions.Storage/release_notes.md index 32976bcb3..4ab75b73f 100644 --- a/extensions/Worker.Extensions.Storage/release_notes.md +++ b/extensions/Worker.Extensions.Storage/release_notes.md @@ -4,15 +4,14 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.Storage 6.8.0 +### Microsoft.Azure.Functions.Worker.Extensions.Storage -- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Blobs` reference to 5.3.5 (#3155) -- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Queues` reference to 5.3.5 (#3155) +- -### Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs 6.8.0 +### Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs -- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Blobs` reference to 5.3.5 (#3155) +- -### Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues 5.5.3 +### Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues -- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Queues` reference to 5.3.5 (#3155) +- diff --git a/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj b/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj index 6fbeccbaa..af4803771 100644 --- a/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj +++ b/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj @@ -6,7 +6,7 @@ Azure Storage extensions for .NET isolated functions - 6.8.0 + 6.7.0 false From c0f179a5981eae7568b2aece04c6fdecae065416 Mon Sep 17 00:00:00 2001 From: Matthew Henderson Date: Thu, 4 Sep 2025 11:10:13 -0700 Subject: [PATCH 15/45] Updating extension dependencies (Event Grid) (#3160) --- extensions/Worker.Extensions.EventGrid/release_notes.md | 4 ++-- .../src/Worker.Extensions.EventGrid.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/Worker.Extensions.EventGrid/release_notes.md b/extensions/Worker.Extensions.EventGrid/release_notes.md index 65290e0c6..bda5e6e80 100644 --- a/extensions/Worker.Extensions.EventGrid/release_notes.md +++ b/extensions/Worker.Extensions.EventGrid/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.EventGrid +### Microsoft.Azure.Functions.Worker.Extensions.EventGrid 3.6.0 -- \ No newline at end of file +- Updating `Microsoft.Azure.WebJobs.Extensions.EventGrid` reference to 3.5.0 (#3160) \ No newline at end of file diff --git a/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj b/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj index d45de0ec0..d4f94c919 100644 --- a/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj +++ b/extensions/Worker.Extensions.EventGrid/src/Worker.Extensions.EventGrid.csproj @@ -6,7 +6,7 @@ Azure Event Grid extensions for .NET isolated functions - 3.5.0 + 3.6.0 false @@ -24,7 +24,7 @@ - + \ No newline at end of file From c946a1245b89822aed3ef3e373965426479558c9 Mon Sep 17 00:00:00 2001 From: Fabio Cavalcante Date: Mon, 8 Sep 2025 09:40:00 -0700 Subject: [PATCH 16/45] Updating default file encoding configuration (#3147) --- .editorconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index 49de0d370..66212dc13 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,11 +5,11 @@ # All files [*] indent_style = space +charset = utf-8 # Code files [*.{cs,csx,vb,vbx}] indent_size = 4 insert_final_newline = true -charset = utf-8-bom ############################### # .NET Coding Conventions # ############################### From 00d012f4a110893cb29fd1425e7ae0f3c8a635de Mon Sep 17 00:00:00 2001 From: Matthew Henderson Date: Mon, 15 Sep 2025 11:35:52 -0700 Subject: [PATCH 17/45] Updating extension dependencies (Storage) (#3167) --- .../src/Worker.Extensions.Storage.Blobs.csproj | 6 +++--- .../src/Worker.Extensions.Storage.Queues.csproj | 4 ++-- .../Worker.Extensions.Storage/release_notes.md | 15 +++++++++------ .../src/Worker.Extensions.Storage.csproj | 2 +- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj b/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj index b4c9ad017..5773fd59e 100644 --- a/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj +++ b/extensions/Worker.Extensions.Storage.Blobs/src/Worker.Extensions.Storage.Blobs.csproj @@ -6,7 +6,7 @@ Azure Blob Storage extensions for .NET isolated functions - 6.7.0 + 6.8.0 false @@ -20,7 +20,7 @@ - + @@ -29,7 +29,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj b/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj index 6333b6c85..8ca17684f 100644 --- a/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj +++ b/extensions/Worker.Extensions.Storage.Queues/src/Worker.Extensions.Storage.Queues.csproj @@ -6,7 +6,7 @@ Azure Queue Storage extensions for .NET isolated functions - 5.5.2 + 5.5.3 false @@ -28,7 +28,7 @@ - + \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage/release_notes.md b/extensions/Worker.Extensions.Storage/release_notes.md index 4ab75b73f..4c0fae339 100644 --- a/extensions/Worker.Extensions.Storage/release_notes.md +++ b/extensions/Worker.Extensions.Storage/release_notes.md @@ -4,14 +4,17 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.Storage +### Microsoft.Azure.Functions.Worker.Extensions.Storage 6.8.0 -- +- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Blobs` reference to 5.3.6 (#3167) +- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Queues` reference to 5.3.6 (#3167) +- Update dependency `Microsoft.Extensions.Azure` to 1.12.0 (#3167) -### Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs +### Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs 6.8.0 -- +- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Blobs` reference to 5.3.6 (#3167) +- Update dependency `Microsoft.Extensions.Azure` to 1.12.0 (#3167) -### Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues +### Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues 5.5.3 -- +- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Queues` reference to 5.3.6 (#3167) diff --git a/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj b/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj index af4803771..6fbeccbaa 100644 --- a/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj +++ b/extensions/Worker.Extensions.Storage/src/Worker.Extensions.Storage.csproj @@ -6,7 +6,7 @@ Azure Storage extensions for .NET isolated functions - 6.7.0 + 6.8.0 false From f2f4165af3136d8671b2c3d7bdaa366e8b94c45f Mon Sep 17 00:00:00 2001 From: sarah <35204912+satvu@users.noreply.github.com> Date: Tue, 16 Sep 2025 10:52:57 -0700 Subject: [PATCH 18/45] Clear release notes (#3168) --- .../Worker.Extensions.CosmosDB/release_notes.md | 5 ++--- .../Worker.Extensions.EventGrid/release_notes.md | 4 ++-- .../Worker.Extensions.Storage/release_notes.md | 15 ++++++--------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/extensions/Worker.Extensions.CosmosDB/release_notes.md b/extensions/Worker.Extensions.CosmosDB/release_notes.md index f0faef598..921fdcf34 100644 --- a/extensions/Worker.Extensions.CosmosDB/release_notes.md +++ b/extensions/Worker.Extensions.CosmosDB/release_notes.md @@ -4,7 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB 4.13.0 +### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB -- Update dependency `Microsoft.Azure.WebJobs.Extensions.CosmosDB` to 4.10.0 (#3157) -- Update dependency `Microsoft.Extensions.Azure` to 1.12.0 (#3157) +- diff --git a/extensions/Worker.Extensions.EventGrid/release_notes.md b/extensions/Worker.Extensions.EventGrid/release_notes.md index bda5e6e80..755c456b7 100644 --- a/extensions/Worker.Extensions.EventGrid/release_notes.md +++ b/extensions/Worker.Extensions.EventGrid/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.EventGrid 3.6.0 +### Microsoft.Azure.Functions.Worker.Extensions.EventGrid -- Updating `Microsoft.Azure.WebJobs.Extensions.EventGrid` reference to 3.5.0 (#3160) \ No newline at end of file +- \ No newline at end of file diff --git a/extensions/Worker.Extensions.Storage/release_notes.md b/extensions/Worker.Extensions.Storage/release_notes.md index 4c0fae339..b47906b12 100644 --- a/extensions/Worker.Extensions.Storage/release_notes.md +++ b/extensions/Worker.Extensions.Storage/release_notes.md @@ -4,17 +4,14 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.Storage 6.8.0 +### Microsoft.Azure.Functions.Worker.Extensions.Storage -- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Blobs` reference to 5.3.6 (#3167) -- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Queues` reference to 5.3.6 (#3167) -- Update dependency `Microsoft.Extensions.Azure` to 1.12.0 (#3167) +- -### Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs 6.8.0 +### Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs -- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Blobs` reference to 5.3.6 (#3167) -- Update dependency `Microsoft.Extensions.Azure` to 1.12.0 (#3167) +- -### Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues 5.5.3 +### Microsoft.Azure.Functions.Worker.Extensions.Storage.Queues -- Updating `Microsoft.Azure.WebJobs.Extensions.Storage.Queues` reference to 5.3.6 (#3167) +- \ No newline at end of file From 06e630240a14704b228f3606f1953ec2305ad570 Mon Sep 17 00:00:00 2001 From: Lilian Kasem Date: Wed, 17 Sep 2025 15:26:53 -0700 Subject: [PATCH 19/45] Implementing function metadata transform support (#3145) * Implementing function metadata transform support * Updates to metadata transform feature --------- Co-authored-by: Fabio Cavalcante --- release_notes.md | 4 +- .../DefaultFunctionMetadataManager.cs | 62 ++++++++++++ .../IFunctionMetadataManager.cs | 20 ++++ .../IFunctionMetadataTransformer.cs | 24 +++++ .../Hosting/ServiceCollectionExtensions.cs | 5 +- src/DotNetWorker.Grpc/GrpcWorker.cs | 10 +- .../DefaultFunctionMetadataManagerTests.cs | 99 +++++++++++++++++++ test/DotNetWorkerTests/GrpcWorkerTests.cs | 4 +- 8 files changed, 217 insertions(+), 11 deletions(-) create mode 100644 src/DotNetWorker.Core/FunctionMetadata/DefaultFunctionMetadataManager.cs create mode 100644 src/DotNetWorker.Core/FunctionMetadata/IFunctionMetadataManager.cs create mode 100644 src/DotNetWorker.Core/FunctionMetadata/IFunctionMetadataTransformer.cs create mode 100644 test/DotNetWorkerTests/FunctionMetadata/DefaultFunctionMetadataManagerTests.cs diff --git a/release_notes.md b/release_notes.md index 173ffd02f..74db65101 100644 --- a/release_notes.md +++ b/release_notes.md @@ -10,8 +10,8 @@ ### Microsoft.Azure.Functions.Worker.Core -- +- Support for function metadata transforms (#3145) ### Microsoft.Azure.Functions.Worker.Grpc -- +- Updated to use the new metadata manage and leverage metadata transforms (#3145) diff --git a/src/DotNetWorker.Core/FunctionMetadata/DefaultFunctionMetadataManager.cs b/src/DotNetWorker.Core/FunctionMetadata/DefaultFunctionMetadataManager.cs new file mode 100644 index 000000000..ff4e03cb4 --- /dev/null +++ b/src/DotNetWorker.Core/FunctionMetadata/DefaultFunctionMetadataManager.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; + +namespace Microsoft.Azure.Functions.Worker.Core.FunctionMetadata +{ + internal sealed class DefaultFunctionMetadataManager : IFunctionMetadataManager + { + private readonly IFunctionMetadataProvider _functionMetadataProvider; + private readonly ImmutableArray _transformers; + private readonly ILogger _logger; + + public DefaultFunctionMetadataManager(IFunctionMetadataProvider functionMetadataProvider, + IEnumerable transformers, + ILogger logger) + { + _functionMetadataProvider = functionMetadataProvider; + _transformers = transformers.ToImmutableArray(); + _logger = logger; + } + + public async Task> GetFunctionMetadataAsync(string directory) + { + ImmutableArray functionMetadata = await _functionMetadataProvider.GetFunctionMetadataAsync(directory); + + return ApplyTransforms(functionMetadata); + } + + private ImmutableArray ApplyTransforms(ImmutableArray functionMetadata) + { + // Return early if there are no transformers to apply + if (_transformers.Length == 0) + { + return functionMetadata; + } + + var metadataResult = functionMetadata.ToBuilder(); + + foreach (var transformer in _transformers) + { + try + { + _logger?.LogTrace("Applying metadata transformer: {Transformer}.", transformer.Name); + transformer.Transform(metadataResult); + } + catch (Exception exc) + { + _logger?.LogError(exc, "Metadata transformer '{Transformer}' failed.", transformer.Name); + throw; + } + } + + return metadataResult.ToImmutable(); + } + } +} diff --git a/src/DotNetWorker.Core/FunctionMetadata/IFunctionMetadataManager.cs b/src/DotNetWorker.Core/FunctionMetadata/IFunctionMetadataManager.cs new file mode 100644 index 000000000..c81c26070 --- /dev/null +++ b/src/DotNetWorker.Core/FunctionMetadata/IFunctionMetadataManager.cs @@ -0,0 +1,20 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Immutable; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Functions.Worker.Core.FunctionMetadata +{ + /// + /// Manages function metadata, providing functionality that combines metadata from the registered provider and metadata transforms. + /// + public interface IFunctionMetadataManager + { + /// + /// Retrieves all function metadata for the current application. + /// + /// A representing the asynchronous metadata retrieval operation, where the result is an . + Task> GetFunctionMetadataAsync(string directory); + } +} diff --git a/src/DotNetWorker.Core/FunctionMetadata/IFunctionMetadataTransformer.cs b/src/DotNetWorker.Core/FunctionMetadata/IFunctionMetadataTransformer.cs new file mode 100644 index 000000000..98cdfdd42 --- /dev/null +++ b/src/DotNetWorker.Core/FunctionMetadata/IFunctionMetadataTransformer.cs @@ -0,0 +1,24 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; + +namespace Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; + +/// +/// Defines a contract for transforming instances for Azure Functions. +/// Implementations can modify, augment, or filter function metadata before it is used by the host. +/// +public interface IFunctionMetadataTransformer +{ + /// + /// Gets the name of the transformer. + /// + public string Name { get; } + + /// + /// Transforms the provided collection of instances. + /// + /// The original collection of function metadata. This collection may be modified. + void Transform(IList original); +} diff --git a/src/DotNetWorker.Core/Hosting/ServiceCollectionExtensions.cs b/src/DotNetWorker.Core/Hosting/ServiceCollectionExtensions.cs index 8ee1643de..daf3fca75 100644 --- a/src/DotNetWorker.Core/Hosting/ServiceCollectionExtensions.cs +++ b/src/DotNetWorker.Core/Hosting/ServiceCollectionExtensions.cs @@ -10,13 +10,13 @@ using Microsoft.Azure.Functions.Worker.Context.Features; using Microsoft.Azure.Functions.Worker.Converters; using Microsoft.Azure.Functions.Worker.Core; +using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; using Microsoft.Azure.Functions.Worker.Diagnostics; using Microsoft.Azure.Functions.Worker.Invocation; using Microsoft.Azure.Functions.Worker.Logging; using Microsoft.Azure.Functions.Worker.OutputBindings; using Microsoft.Azure.Functions.Worker.Pipeline; using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -71,6 +71,9 @@ public static IFunctionsWorkerApplicationBuilder AddFunctionsWorkerCore(this ISe // Worker initialization service services.AddHostedService(); + // Worker metadata management + services.TryAddSingleton(); + // Default serializer settings services.AddOptions(); services.TryAddEnumerable(ServiceDescriptor.Transient, WorkerOptionsSetup>()); diff --git a/src/DotNetWorker.Grpc/GrpcWorker.cs b/src/DotNetWorker.Grpc/GrpcWorker.cs index 581cfce78..4dc19e081 100644 --- a/src/DotNetWorker.Grpc/GrpcWorker.cs +++ b/src/DotNetWorker.Grpc/GrpcWorker.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Threading; @@ -18,7 +17,6 @@ using Microsoft.Azure.Functions.Worker.Invocation; using Microsoft.Azure.Functions.Worker.Rpc; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using MsgType = Microsoft.Azure.Functions.Worker.Grpc.Messages.StreamingMessage.ContentOneofCase; @@ -32,14 +30,14 @@ internal partial class GrpcWorker : IWorker, IMessageProcessor private readonly IHostApplicationLifetime _hostApplicationLifetime; private readonly IWorkerClientFactory _workerClientFactory; private readonly IInvocationHandler _invocationHandler; - private readonly IFunctionMetadataProvider _functionMetadataProvider; + private readonly IFunctionMetadataManager _metadataManager; private IWorkerClient? _workerClient; public GrpcWorker(IFunctionsApplication application, IWorkerClientFactory workerClientFactory, IMethodInfoLocator methodInfoLocator, IOptions workerOptions, - IFunctionMetadataProvider functionMetadataProvider, + IFunctionMetadataManager metadataManager, IHostApplicationLifetime hostApplicationLifetime, IInvocationHandler invocationHandler) { @@ -48,7 +46,7 @@ public GrpcWorker(IFunctionsApplication application, _application = application ?? throw new ArgumentNullException(nameof(application)); _methodInfoLocator = methodInfoLocator ?? throw new ArgumentNullException(nameof(methodInfoLocator)); _workerOptions = workerOptions.Value ?? throw new ArgumentNullException(nameof(workerOptions)); - _functionMetadataProvider = functionMetadataProvider ?? throw new ArgumentNullException(nameof(functionMetadataProvider)); + _metadataManager = metadataManager ?? throw new ArgumentNullException(nameof(metadataManager)); _invocationHandler = invocationHandler; } @@ -152,7 +150,7 @@ private async Task GetFunctionMetadataAsync(string fun try { - var functionMetadataList = await _functionMetadataProvider.GetFunctionMetadataAsync(functionAppDirectory); + var functionMetadataList = await _metadataManager.GetFunctionMetadataAsync(functionAppDirectory); foreach (var func in functionMetadataList) { diff --git a/test/DotNetWorkerTests/FunctionMetadata/DefaultFunctionMetadataManagerTests.cs b/test/DotNetWorkerTests/FunctionMetadata/DefaultFunctionMetadataManagerTests.cs new file mode 100644 index 000000000..c508b9a8b --- /dev/null +++ b/test/DotNetWorkerTests/FunctionMetadata/DefaultFunctionMetadataManagerTests.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; +using Microsoft.Extensions.Logging; +using Moq; +using Xunit; + +namespace Microsoft.Azure.Functions.Worker.Tests.FunctionMetadata +{ + public class DefaultFunctionMetadataManagerTests + { + [Fact] + public async Task GetFunctionMetadataAsync_ReturnsTransformedMetadata() + { + var mockProvider = new Mock(MockBehavior.Strict); + var mockTransformer = new Mock(MockBehavior.Strict); + var mockLogger = new Mock>(); + + var metadata = new List { new TestFunctionMetadata() }.ToImmutableArray(); + mockProvider.Setup(p => p.GetFunctionMetadataAsync(It.IsAny())).ReturnsAsync(metadata); + + mockTransformer.SetupGet(t => t.Name).Returns("TestTransformer"); + mockTransformer.Setup(t => t.Transform(It.IsAny>())); + + var manager = new DefaultFunctionMetadataManager( + mockProvider.Object, + new[] { mockTransformer.Object }, + mockLogger.Object); + + var result = await manager.GetFunctionMetadataAsync("test"); + + Assert.Single(result); + mockProvider.Verify(p => p.GetFunctionMetadataAsync("test"), Times.Once); + mockTransformer.Verify(t => t.Transform(It.IsAny>()), Times.Once); + } + + [Fact] + public async Task GetFunctionMetadataAsync_NoTransformers_ReturnsOriginalMetadata() + { + var mockProvider = new Mock(MockBehavior.Strict); + var mockLogger = new Mock>(); + var metadata = new List { new TestFunctionMetadata() }.ToImmutableArray(); + mockProvider.Setup(p => p.GetFunctionMetadataAsync(It.IsAny())).ReturnsAsync(metadata); + + var manager = new DefaultFunctionMetadataManager( + mockProvider.Object, + Array.Empty(), + mockLogger.Object); + + var result = await manager.GetFunctionMetadataAsync("test"); + + Assert.Single(result); + mockProvider.Verify(p => p.GetFunctionMetadataAsync("test"), Times.Once); + } + + [Fact] + public async Task GetFunctionMetadataAsync_TransformerThrows_LogsAndThrows() + { + var mockProvider = new Mock(MockBehavior.Strict); + var mockTransformer = new Mock(MockBehavior.Strict); + var mockLogger = new Mock>(); + var metadata = ImmutableArray.Empty; + + mockProvider.Setup(p => p.GetFunctionMetadataAsync(It.IsAny())).ReturnsAsync(metadata); + mockTransformer.SetupGet(t => t.Name).Returns("ThrowingTransformer"); + mockTransformer.Setup(t => t.Transform(It.IsAny>())) + .Throws(new InvalidOperationException("fail")); + + var manager = new DefaultFunctionMetadataManager( + mockProvider.Object, + new[] { mockTransformer.Object }, + mockLogger.Object); + + var ex = await Assert.ThrowsAsync(() => manager.GetFunctionMetadataAsync("test")); + Assert.Equal("fail", ex.Message); + mockLogger.Verify(l => l.Log( + LogLevel.Error, + It.IsAny(), + It.Is((v, t) => v.ToString().Contains("ThrowingTransformer")), + It.IsAny(), + It.IsAny>()), Times.Once); + } + + private class TestFunctionMetadata : IFunctionMetadata + { + public string? FunctionId => "id"; + public bool IsProxy => false; + public string? Language => "dotnet"; + public bool ManagedDependencyEnabled => false; + public string? Name => "Test"; + public string? EntryPoint => "Test.Run"; + public IList? RawBindings => new List(); + public string? ScriptFile => "Test.dll"; + public IRetryOptions? Retry => null; + } + } +} diff --git a/test/DotNetWorkerTests/GrpcWorkerTests.cs b/test/DotNetWorkerTests/GrpcWorkerTests.cs index f3e10b56f..01c722eb5 100644 --- a/test/DotNetWorkerTests/GrpcWorkerTests.cs +++ b/test/DotNetWorkerTests/GrpcWorkerTests.cs @@ -279,7 +279,7 @@ public async Task Invocation_WhenSynchronous_DoesNotBlock() var clientFactoryMock = new Mock(); var clientMock = new Mock(); - var metadataProvider = new Mock(); + var metadataManager = new Mock(); var invocationHandlerMock = new Mock(); InvocationResponse ValueFunction(InvocationRequest request) @@ -310,7 +310,7 @@ InvocationResponse ValueFunction(InvocationRequest request) clientFactoryMock.Object, _mockMethodInfoLocator.Object, new OptionsWrapper(new WorkerOptions()), - metadataProvider.Object, + metadataManager.Object, new ApplicationLifetime(TestLogger.Create()), invocationHandlerMock.Object); From 6ac6e358ba04b76459b87d0c03fa0db8f95a1da5 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 17 Sep 2025 16:52:48 -0700 Subject: [PATCH 20/45] Worker packages release prep - 2.1.0 (#3171) * Version bump and release notes updates for worker package release 2.1.0 * Fix assembly version in source gen test. --- release_notes.md | 6 +++--- src/DotNetWorker.Core/DotNetWorker.Core.csproj | 2 +- src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj | 2 +- src/DotNetWorker/DotNetWorker.csproj | 2 +- .../FunctionExecutor/DependentAssemblyTest.cs | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/release_notes.md b/release_notes.md index 74db65101..81290d933 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,14 +4,14 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker (metapackage) +### Microsoft.Azure.Functions.Worker (metapackage) 2.1.0 - `AZURE_FUNCTIONS_` environment variables are now loaded correctly when using `FunctionsApplicationBuilder`. (#2878) -### Microsoft.Azure.Functions.Worker.Core +### Microsoft.Azure.Functions.Worker.Core 2.1.0 - Support for function metadata transforms (#3145) -### Microsoft.Azure.Functions.Worker.Grpc +### Microsoft.Azure.Functions.Worker.Grpc 2.1.0 - Updated to use the new metadata manage and leverage metadata transforms (#3145) diff --git a/src/DotNetWorker.Core/DotNetWorker.Core.csproj b/src/DotNetWorker.Core/DotNetWorker.Core.csproj index 5844b5512..316b1c903 100644 --- a/src/DotNetWorker.Core/DotNetWorker.Core.csproj +++ b/src/DotNetWorker.Core/DotNetWorker.Core.csproj @@ -9,7 +9,7 @@ Microsoft.Azure.Functions.Worker.Core true 2 - 0 + 1 0 diff --git a/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj b/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj index 69bc14427..949718c97 100644 --- a/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj +++ b/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj @@ -9,7 +9,7 @@ Microsoft.Azure.Functions.Worker.Grpc true 2 - 0 + 1 0 true diff --git a/src/DotNetWorker/DotNetWorker.csproj b/src/DotNetWorker/DotNetWorker.csproj index b68c14392..e8ffb02c8 100644 --- a/src/DotNetWorker/DotNetWorker.csproj +++ b/src/DotNetWorker/DotNetWorker.csproj @@ -9,7 +9,7 @@ Microsoft.Azure.Functions.Worker true 2 - 0 + 1 0 diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs index 3ce6c543f..e76050829 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Reflection; @@ -139,7 +139,7 @@ public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunction private global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor CreateDefaultExecutorInstance(global::Microsoft.Azure.Functions.Worker.FunctionContext context) { - var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=2.0.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; + var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; var defaultExecutorType = global::System.Type.GetType(defaultExecutorFullName); return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; From a566c5471a94ba8592b6a580b6f5ee324849ab19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 14:04:49 -0700 Subject: [PATCH 21/45] Bump dotnet-sdk from 9.0.304 to 9.0.305 (#3166) Bumps [dotnet-sdk](https://github.com/dotnet/sdk) from 9.0.304 to 9.0.305. - [Release notes](https://github.com/dotnet/sdk/releases) - [Commits](https://github.com/dotnet/sdk/compare/v9.0.304...v9.0.305) --- updated-dependencies: - dependency-name: dotnet-sdk dependency-version: 9.0.305 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 03064daa1..75387d104 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.304", + "version": "9.0.305", "allowPrerelease": true, "rollForward": "latestFeature" }, From 088bd7c6b2171fc3a92ea1053f769af63c781b1a Mon Sep 17 00:00:00 2001 From: Fabio Cavalcante Date: Wed, 24 Sep 2025 10:04:26 -0700 Subject: [PATCH 22/45] Updating ASP.NET Core extension to use metadata manager (#3172) --- .../Worker.Extensions.Http.AspNetCore/release_notes.md | 2 +- .../AspNetMiddleware/FunctionsEndpointDataSource.cs | 10 +++++----- .../src/Worker.Extensions.Http.AspNetCore.csproj | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/Worker.Extensions.Http.AspNetCore/release_notes.md b/extensions/Worker.Extensions.Http.AspNetCore/release_notes.md index 97bb2a3ba..459b597e3 100644 --- a/extensions/Worker.Extensions.Http.AspNetCore/release_notes.md +++ b/extensions/Worker.Extensions.Http.AspNetCore/release_notes.md @@ -6,7 +6,7 @@ ### Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore -- +- Updating ASP.NET Core integration to support the metadata transformation feature (#3172) ### Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore.Analyzers diff --git a/extensions/Worker.Extensions.Http.AspNetCore/src/AspNetMiddleware/FunctionsEndpointDataSource.cs b/extensions/Worker.Extensions.Http.AspNetCore/src/AspNetMiddleware/FunctionsEndpointDataSource.cs index 51285d1f2..73276df24 100644 --- a/extensions/Worker.Extensions.Http.AspNetCore/src/AspNetMiddleware/FunctionsEndpointDataSource.cs +++ b/extensions/Worker.Extensions.Http.AspNetCore/src/AspNetMiddleware/FunctionsEndpointDataSource.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Text.Json; @@ -17,7 +17,7 @@ internal class FunctionsEndpointDataSource : EndpointDataSource private const string HostJsonFileName = "host.json"; private const string DefaultRoutePrefix = "api"; - private readonly IFunctionMetadataProvider _functionMetadataProvider; + private readonly IFunctionMetadataManager _functionMetadataManager; private readonly object _lock = new(); private static readonly JsonSerializerOptions _jsonSerializerOptions = new() @@ -30,9 +30,9 @@ internal class FunctionsEndpointDataSource : EndpointDataSource private List? _endpoints; - public FunctionsEndpointDataSource(IFunctionMetadataProvider functionMetadataProvider) + public FunctionsEndpointDataSource(IFunctionMetadataManager functionMetadataManager) { - _functionMetadataProvider = functionMetadataProvider ?? throw new ArgumentNullException(nameof(functionMetadataProvider)); + _functionMetadataManager = functionMetadataManager ?? throw new ArgumentNullException(nameof(functionMetadataManager)); } public override IReadOnlyList Endpoints @@ -58,7 +58,7 @@ private List BuildEndpoints() string scriptRoot = Environment.GetEnvironmentVariable(FunctionsApplicationDirectoryKey) ?? throw new InvalidOperationException("Cannot determine script root directory."); - var metadata = _functionMetadataProvider.GetFunctionMetadataAsync(scriptRoot).GetAwaiter().GetResult(); + var metadata = _functionMetadataManager.GetFunctionMetadataAsync(scriptRoot).GetAwaiter().GetResult(); string routePrefix = GetRoutePrefixFromHostJson(scriptRoot) ?? DefaultRoutePrefix; diff --git a/extensions/Worker.Extensions.Http.AspNetCore/src/Worker.Extensions.Http.AspNetCore.csproj b/extensions/Worker.Extensions.Http.AspNetCore/src/Worker.Extensions.Http.AspNetCore.csproj index f26b5fe99..842c74879 100644 --- a/extensions/Worker.Extensions.Http.AspNetCore/src/Worker.Extensions.Http.AspNetCore.csproj +++ b/extensions/Worker.Extensions.Http.AspNetCore/src/Worker.Extensions.Http.AspNetCore.csproj @@ -6,7 +6,7 @@ ASP.NET Core extensions for .NET isolated functions - 2.0.2 + 2.1.0 net6.0;net8.0 @@ -23,7 +23,7 @@ - + From 4c231f60ed16e95f080b09e8617c62fbb9b8b08a Mon Sep 17 00:00:00 2001 From: Lilian Kasem Date: Thu, 25 Sep 2025 14:25:26 -0700 Subject: [PATCH 23/45] Bump ServiceBus extension dependencies (#3178) --- .../Worker.Extensions.ServiceBus/release_notes.md | 9 +++++++-- .../src/Worker.Extensions.ServiceBus.csproj | 12 ++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/extensions/Worker.Extensions.ServiceBus/release_notes.md b/extensions/Worker.Extensions.ServiceBus/release_notes.md index c92b7d33c..bb1c984cc 100644 --- a/extensions/Worker.Extensions.ServiceBus/release_notes.md +++ b/extensions/Worker.Extensions.ServiceBus/release_notes.md @@ -4,6 +4,11 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus +### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus 5.24.0 -- \ No newline at end of file +- Update minor dependencies + - Azure.Identity to 1.16.0 + - Azure.Messaging.ServiceBus to 7.20.1 + - Microsoft.Extensions.Azure to 1.13.0 + - Google.Protobuf to 3.32.1 + - Grpc.Tools to 2.72.0 \ No newline at end of file diff --git a/extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj b/extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj index 048f513cc..2b0378c42 100644 --- a/extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj +++ b/extensions/Worker.Extensions.ServiceBus/src/Worker.Extensions.ServiceBus.csproj @@ -6,7 +6,7 @@ Azure Service Bus extensions for .NET isolated functions - 5.23.0 + 5.24.0 false @@ -15,11 +15,11 @@ - - - - - + + + + + From f199d208fe9062337d606625e7bd2cd94211769f Mon Sep 17 00:00:00 2001 From: Pranava <68387945+aloiva@users.noreply.github.com> Date: Tue, 30 Sep 2025 23:28:26 +0530 Subject: [PATCH 24/45] Bump Kafka worker extension version to 4.1.3 (#3181) --- extensions/Worker.Extensions.Kafka/release_notes.md | 5 +++-- .../src/Worker.Extensions.Kafka.csproj | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/extensions/Worker.Extensions.Kafka/release_notes.md b/extensions/Worker.Extensions.Kafka/release_notes.md index ca17cb549..3713cf33c 100644 --- a/extensions/Worker.Extensions.Kafka/release_notes.md +++ b/extensions/Worker.Extensions.Kafka/release_notes.md @@ -4,6 +4,7 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.Kafka +### Microsoft.Azure.Functions.Worker.Extensions.Kafka 4.1.3 -- \ No newline at end of file +- Fix key avro serialization and deserialization when schema registry URL is provided [(PR Link)](https://github.com/Azure/azure-functions-kafka-extension/pull/569) +- Fix null reference error when using avro deserialization [(PR Link)](https://github.com/Azure/azure-functions-kafka-extension/pull/594) diff --git a/extensions/Worker.Extensions.Kafka/src/Worker.Extensions.Kafka.csproj b/extensions/Worker.Extensions.Kafka/src/Worker.Extensions.Kafka.csproj index 84df6f91c..f58db4145 100644 --- a/extensions/Worker.Extensions.Kafka/src/Worker.Extensions.Kafka.csproj +++ b/extensions/Worker.Extensions.Kafka/src/Worker.Extensions.Kafka.csproj @@ -6,7 +6,7 @@ Kafka extensions for .NET isolated functions - 4.1.2 + 4.1.3 false @@ -21,7 +21,7 @@ - + From 505eb7bbc8aff7f84a49c8bee5413552b95eff3b Mon Sep 17 00:00:00 2001 From: sarah <35204912+satvu@users.noreply.github.com> Date: Tue, 30 Sep 2025 16:03:20 -0700 Subject: [PATCH 25/45] Clear ext release notes (#3184) --- extensions/Worker.Extensions.Kafka/release_notes.md | 5 ++--- extensions/Worker.Extensions.ServiceBus/release_notes.md | 9 ++------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/extensions/Worker.Extensions.Kafka/release_notes.md b/extensions/Worker.Extensions.Kafka/release_notes.md index 3713cf33c..e73c591ab 100644 --- a/extensions/Worker.Extensions.Kafka/release_notes.md +++ b/extensions/Worker.Extensions.Kafka/release_notes.md @@ -4,7 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.Kafka 4.1.3 +### Microsoft.Azure.Functions.Worker.Extensions.Kafka -- Fix key avro serialization and deserialization when schema registry URL is provided [(PR Link)](https://github.com/Azure/azure-functions-kafka-extension/pull/569) -- Fix null reference error when using avro deserialization [(PR Link)](https://github.com/Azure/azure-functions-kafka-extension/pull/594) +- diff --git a/extensions/Worker.Extensions.ServiceBus/release_notes.md b/extensions/Worker.Extensions.ServiceBus/release_notes.md index bb1c984cc..c92b7d33c 100644 --- a/extensions/Worker.Extensions.ServiceBus/release_notes.md +++ b/extensions/Worker.Extensions.ServiceBus/release_notes.md @@ -4,11 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus 5.24.0 +### Microsoft.Azure.Functions.Worker.Extensions.ServiceBus -- Update minor dependencies - - Azure.Identity to 1.16.0 - - Azure.Messaging.ServiceBus to 7.20.1 - - Microsoft.Extensions.Azure to 1.13.0 - - Google.Protobuf to 3.32.1 - - Grpc.Tools to 2.72.0 \ No newline at end of file +- \ No newline at end of file From ab7b3f25a6cd706aa9906c11b2feced98f59e273 Mon Sep 17 00:00:00 2001 From: Nalu Tripician <27316859+NaluTripician@users.noreply.github.com> Date: Thu, 9 Oct 2025 20:45:16 -0700 Subject: [PATCH 26/45] Updates Cosmos Extensions version to 4.11.0 (#3191) * Updates Cosmos Extensions version * Update release_notes.md --- extensions/Worker.Extensions.CosmosDB/release_notes.md | 4 ++-- .../src/Worker.Extensions.CosmosDB.csproj | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extensions/Worker.Extensions.CosmosDB/release_notes.md b/extensions/Worker.Extensions.CosmosDB/release_notes.md index 921fdcf34..b9c7c90b7 100644 --- a/extensions/Worker.Extensions.CosmosDB/release_notes.md +++ b/extensions/Worker.Extensions.CosmosDB/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB +### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB <4.14.0> -- +- Updates dependency `Microsoft.Azure.WebJobs.Extensions.CosmosDB` to version 4.11.0 (#3191) diff --git a/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj b/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj index 8217dac2e..1d0b09477 100644 --- a/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj +++ b/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj @@ -6,7 +6,7 @@ Azure Cosmos DB extensions for .NET isolated functions - 4.13.0 + 4.14.0 false @@ -31,7 +31,7 @@ - + \ No newline at end of file From 12bad22253251fd1d54aa914e12f432cafdf358c Mon Sep 17 00:00:00 2001 From: Brett Samblanet Date: Thu, 16 Oct 2025 11:04:41 -0400 Subject: [PATCH 27/45] updating Azure.Messaging.EventHubs package for bug fix (#3189) --- .../release_notes.md | 4 +-- .../src/Worker.Extensions.EventHubs.csproj | 4 +-- .../EventHubs/EventDataConverterTests.cs | 36 ++++++++++++++++++- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/extensions/Worker.Extensions.EventHubs/release_notes.md b/extensions/Worker.Extensions.EventHubs/release_notes.md index 3d6e918a8..c01c394dc 100644 --- a/extensions/Worker.Extensions.EventHubs/release_notes.md +++ b/extensions/Worker.Extensions.EventHubs/release_notes.md @@ -4,6 +4,6 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.EventHubs +### Microsoft.Azure.Functions.Worker.Extensions.EventHubs 6.5.1 -- +- updating `Azure.Messaging.EventHubs` to 5.12.2 (#3189) diff --git a/extensions/Worker.Extensions.EventHubs/src/Worker.Extensions.EventHubs.csproj b/extensions/Worker.Extensions.EventHubs/src/Worker.Extensions.EventHubs.csproj index 711ca3711..b9fdafa8a 100644 --- a/extensions/Worker.Extensions.EventHubs/src/Worker.Extensions.EventHubs.csproj +++ b/extensions/Worker.Extensions.EventHubs/src/Worker.Extensions.EventHubs.csproj @@ -6,7 +6,7 @@ Azure Event Hubs extensions for .NET isolated functions - 6.5.0 + 6.5.1 @@ -18,7 +18,7 @@ - + diff --git a/test/Worker.Extensions.Tests/EventHubs/EventDataConverterTests.cs b/test/Worker.Extensions.Tests/EventHubs/EventDataConverterTests.cs index 3f7c90f23..37e9dcce4 100644 --- a/test/Worker.Extensions.Tests/EventHubs/EventDataConverterTests.cs +++ b/test/Worker.Extensions.Tests/EventHubs/EventDataConverterTests.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using Azure.Core.Amqp; using Azure.Messaging.EventHubs; using Google.Protobuf; using Microsoft.Azure.Functions.Worker.Converters; @@ -167,6 +168,39 @@ public async Task ConvertAsync_Batch_ReturnsFailure_WrongSource() Assert.Equal("Unexpected binding source 'some-other-source'. Only 'AzureEventHubsEventData' is supported.", result.Error.Message); } + [Fact] + public async Task ConvertAsync_EnqueuedTime() + { + // Validate bug fixed in Azure.Messaging.EventHubs 5.12.2 + var amqpMessage = new AmqpAnnotatedMessage(AmqpMessageBody.FromValue("abc")); + var enqueuedTime = new DateTimeOffset(2025, 10, 8, 8, 43, 21, TimeSpan.Zero); + amqpMessage.MessageAnnotations.Add("x-opt-enqueued-time", enqueuedTime.UtcDateTime); + + var eventData = new EventData(amqpMessage); + + var data = new GrpcModelBindingData(new ModelBindingData() + { + Version = "1.0", + Source = "AzureEventHubsEventData", + Content = ByteString.CopyFrom(ConvertEventDataToBinaryData(eventData)), + ContentType = Constants.BinaryContentType + }); + + var context = new TestConverterContext(typeof(string), data); + var converter = new EventDataConverter(); + + // Act + var result = await converter.ConvertAsync(context); + + // Assert + Assert.Equal(ConversionStatus.Succeeded, result.Status); + var output = result.Value as EventData; + Assert.NotNull(output); + + // Check enqueued time + Assert.Equal(enqueuedTime.UtcDateTime, output.EnqueuedTime.UtcDateTime); + } + private static void AssertEventData(EventData output) { Assert.Equal("body", output.EventBody.ToString()); @@ -190,4 +224,4 @@ private static BinaryData ConvertEventDataToBinaryData(EventData @event) return @event.GetRawAmqpMessage().ToBytes(); } } -} \ No newline at end of file +} From e7dc45bd7f43739cab3542f76e277a1222828430 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 13:53:14 -0700 Subject: [PATCH 28/45] Bump Microsoft.Build.Utilities.Core from 17.12.6 to 17.12.50 (#3197) --- updated-dependencies: - dependency-name: Microsoft.Build.Utilities.Core dependency-version: 17.12.50 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- sdk/Sdk/Sdk.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/Sdk/Sdk.csproj b/sdk/Sdk/Sdk.csproj index 0e00dd207..cc22b2609 100644 --- a/sdk/Sdk/Sdk.csproj +++ b/sdk/Sdk/Sdk.csproj @@ -31,7 +31,7 @@ - + From a42e198d0d7ea896ee9a1cd234f8c856a7fff685 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 13:53:24 -0700 Subject: [PATCH 29/45] Bump dotnet-sdk from 9.0.305 to 9.0.306 (#3196) Bumps [dotnet-sdk](https://github.com/dotnet/sdk) from 9.0.305 to 9.0.306. - [Release notes](https://github.com/dotnet/sdk/releases) - [Commits](https://github.com/dotnet/sdk/compare/v9.0.305...v9.0.306) --- updated-dependencies: - dependency-name: dotnet-sdk dependency-version: 9.0.306 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- global.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/global.json b/global.json index 75387d104..e51901200 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.305", + "version": "9.0.306", "allowPrerelease": true, "rollForward": "latestFeature" }, From 92ebbac0634d0cb665dc253900564f510b2476ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:25:08 -0700 Subject: [PATCH 30/45] Bump Microsoft.Build.Utilities.Core from 17.11.4 to 17.11.48 (#3201) --- updated-dependencies: - dependency-name: Microsoft.Build.Utilities.Core dependency-version: 17.11.48 dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- test/Sdk.E2ETests/Sdk.E2ETests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Sdk.E2ETests/Sdk.E2ETests.csproj b/test/Sdk.E2ETests/Sdk.E2ETests.csproj index ef1dda5e9..8e3743503 100644 --- a/test/Sdk.E2ETests/Sdk.E2ETests.csproj +++ b/test/Sdk.E2ETests/Sdk.E2ETests.csproj @@ -21,7 +21,7 @@ - + From 96f371aaf88b0f3872360d173835988d9013d019 Mon Sep 17 00:00:00 2001 From: Jacob Viau Date: Fri, 17 Oct 2025 12:01:34 -0700 Subject: [PATCH 31/45] Enable IFunctionExecutor in InvocationFeatures (#3200) * Enable IFunctionExecutor in InvocationFeatures * Update release_notes.md * Fix test & warnings --- release_notes.md | 15 ++-- .../Pipeline/FunctionExecutionMiddleware.cs | 16 +++-- .../DefaultFunctionMetadataManagerTests.cs | 8 ++- .../FunctionsExecutionMiddlewareTests.cs | 69 +++++++++++++++++++ 4 files changed, 89 insertions(+), 19 deletions(-) create mode 100644 test/DotNetWorkerTests/Pipelines/FunctionsExecutionMiddlewareTests.cs diff --git a/release_notes.md b/release_notes.md index 81290d933..c7c0f8cd4 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,14 +4,11 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker (metapackage) 2.1.0 +### Microsoft.Azure.Functions.Worker (metapackage) +- -- `AZURE_FUNCTIONS_` environment variables are now loaded correctly when using `FunctionsApplicationBuilder`. (#2878) +### Microsoft.Azure.Functions.Worker.Core +- Support setting `IFunctionExecutor` in invocation features: `FunctionContext.Features` (#3200) -### Microsoft.Azure.Functions.Worker.Core 2.1.0 - -- Support for function metadata transforms (#3145) - -### Microsoft.Azure.Functions.Worker.Grpc 2.1.0 - -- Updated to use the new metadata manage and leverage metadata transforms (#3145) +### Microsoft.Azure.Functions.Worker.Grpc +- diff --git a/src/DotNetWorker.Core/Pipeline/FunctionExecutionMiddleware.cs b/src/DotNetWorker.Core/Pipeline/FunctionExecutionMiddleware.cs index 449c9afaa..cbde9e85d 100644 --- a/src/DotNetWorker.Core/Pipeline/FunctionExecutionMiddleware.cs +++ b/src/DotNetWorker.Core/Pipeline/FunctionExecutionMiddleware.cs @@ -1,22 +1,24 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System; using System.Threading.Tasks; using Microsoft.Azure.Functions.Worker.Invocation; namespace Microsoft.Azure.Functions.Worker.Pipeline { - internal class FunctionExecutionMiddleware + internal class FunctionExecutionMiddleware(IFunctionExecutor functionExecutor) { - private readonly IFunctionExecutor _functionExecutor; - - public FunctionExecutionMiddleware(IFunctionExecutor functionExecutor) - { - _functionExecutor = functionExecutor; - } + private readonly IFunctionExecutor _functionExecutor = functionExecutor + ?? throw new ArgumentNullException(nameof(functionExecutor)); public Task Invoke(FunctionContext context) { + if (context.Features.Get() is { } executor) + { + return executor.ExecuteAsync(context).AsTask(); + } + return _functionExecutor.ExecuteAsync(context).AsTask(); } } diff --git a/test/DotNetWorkerTests/FunctionMetadata/DefaultFunctionMetadataManagerTests.cs b/test/DotNetWorkerTests/FunctionMetadata/DefaultFunctionMetadataManagerTests.cs index c508b9a8b..04d10c6cb 100644 --- a/test/DotNetWorkerTests/FunctionMetadata/DefaultFunctionMetadataManagerTests.cs +++ b/test/DotNetWorkerTests/FunctionMetadata/DefaultFunctionMetadataManagerTests.cs @@ -7,6 +7,8 @@ using Moq; using Xunit; +#nullable enable + namespace Microsoft.Azure.Functions.Worker.Tests.FunctionMetadata { public class DefaultFunctionMetadataManagerTests @@ -70,7 +72,7 @@ public async Task GetFunctionMetadataAsync_TransformerThrows_LogsAndThrows() var manager = new DefaultFunctionMetadataManager( mockProvider.Object, - new[] { mockTransformer.Object }, + [mockTransformer.Object], mockLogger.Object); var ex = await Assert.ThrowsAsync(() => manager.GetFunctionMetadataAsync("test")); @@ -78,9 +80,9 @@ public async Task GetFunctionMetadataAsync_TransformerThrows_LogsAndThrows() mockLogger.Verify(l => l.Log( LogLevel.Error, It.IsAny(), - It.Is((v, t) => v.ToString().Contains("ThrowingTransformer")), + It.Is((v, t) => v.ToString()!.Contains("ThrowingTransformer")), It.IsAny(), - It.IsAny>()), Times.Once); + It.IsAny>()), Times.Once); } private class TestFunctionMetadata : IFunctionMetadata diff --git a/test/DotNetWorkerTests/Pipelines/FunctionsExecutionMiddlewareTests.cs b/test/DotNetWorkerTests/Pipelines/FunctionsExecutionMiddlewareTests.cs new file mode 100644 index 000000000..11e8b3ba0 --- /dev/null +++ b/test/DotNetWorkerTests/Pipelines/FunctionsExecutionMiddlewareTests.cs @@ -0,0 +1,69 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Worker.Invocation; +using Moq; +using Xunit; + +namespace Microsoft.Azure.Functions.Worker.Pipeline.Tests +{ + public class FunctionsExecutionMiddlewareTests + { + [Fact] + public void Ctor_ThrowsArgumentNullException_WhenFunctionExecutorIsNull() + { + // Arrange & Act & Assert + var exception = Assert.Throws(() => new FunctionExecutionMiddleware(null!)); + Assert.Equal("functionExecutor", exception.ParamName); + } + + [Fact] + public async Task Invoke_NoFeature_CallsInjectedExecutor() + { + // Arrange + Mock functionExecutorMock = new(MockBehavior.Strict); + FunctionExecutionMiddleware middleware = new(functionExecutorMock.Object); + Mock functionContextMock = new(MockBehavior.Strict); + functionContextMock.Setup(m => m.Features).Returns(new InvocationFeatures([])); + + functionExecutorMock + .Setup(m => m.ExecuteAsync(functionContextMock.Object)) + .Returns(ValueTask.CompletedTask) + .Verifiable(); + + // Act + await middleware.Invoke(functionContextMock.Object); + + // Assert + functionExecutorMock.Verify(f => f.ExecuteAsync(functionContextMock.Object), Times.Once); + } + + [Fact] + public async Task Invoke_Feature_CallsFeatureExecutor() + { + // Arrange + Mock functionExecutorMock1 = new(MockBehavior.Strict); + Mock functionExecutorMock2 = new(MockBehavior.Strict); + FunctionExecutionMiddleware middleware = new(functionExecutorMock1.Object); + Mock functionContextMock = new(MockBehavior.Strict); + + InvocationFeatures features = new([]); + features.Set(functionExecutorMock2.Object); + functionContextMock.Setup(m => m.Features).Returns(features); + + functionExecutorMock2 + .Setup(m => m.ExecuteAsync(functionContextMock.Object)) + .Returns(ValueTask.CompletedTask) + .Verifiable(); + + // Act + await middleware.Invoke(functionContextMock.Object); + + // Assert + functionExecutorMock1.Verify(f => f.ExecuteAsync(It.IsAny()), Times.Never); + functionExecutorMock2.Verify(f => f.ExecuteAsync(functionContextMock.Object), Times.Once); + } + } +} From ad8b090c4e25f30e7597828fb054cd3f4cf1d12e Mon Sep 17 00:00:00 2001 From: Fabio Cavalcante Date: Mon, 20 Oct 2025 16:28:42 -0700 Subject: [PATCH 32/45] Updating Worker.ApplicationInsights dependencies (#3204) --- .../DotNetWorker.ApplicationInsights.csproj | 4 ++-- src/DotNetWorker.ApplicationInsights/release_notes.md | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/DotNetWorker.ApplicationInsights/DotNetWorker.ApplicationInsights.csproj b/src/DotNetWorker.ApplicationInsights/DotNetWorker.ApplicationInsights.csproj index 1ab568c0e..48dfa8621 100644 --- a/src/DotNetWorker.ApplicationInsights/DotNetWorker.ApplicationInsights.csproj +++ b/src/DotNetWorker.ApplicationInsights/DotNetWorker.ApplicationInsights.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/src/DotNetWorker.ApplicationInsights/release_notes.md b/src/DotNetWorker.ApplicationInsights/release_notes.md index addb34ce4..756698416 100644 --- a/src/DotNetWorker.ApplicationInsights/release_notes.md +++ b/src/DotNetWorker.ApplicationInsights/release_notes.md @@ -2,4 +2,5 @@ ### Microsoft.Azure.Functions.Worker.ApplicationInsights -- +- Updating `Azure.Identity` from 1.12.0 to 1.17.0 +- Updating `Microsoft.ApplicationInsights.PerfCounterCollector` from 2.22.0 to 2.23.0 \ No newline at end of file From 4e60190bd49798f42731d103b1c59ace5bd08c67 Mon Sep 17 00:00:00 2001 From: Surbhi Gupta Date: Mon, 20 Oct 2025 22:18:41 -0500 Subject: [PATCH 33/45] Updating Microsoft.Build.Utilities.Core in Tests project (#3205) --- test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj index f0e9e900a..d2192b599 100644 --- a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj +++ b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj @@ -30,7 +30,7 @@ - + From e99be73f6c1b0d1a59b1788516675a82272667bb Mon Sep 17 00:00:00 2001 From: Jacob Viau Date: Tue, 21 Oct 2025 10:14:34 -0700 Subject: [PATCH 34/45] Prep 2.2.0 release (#3202) * Update core packages to 2.2.0 * Update test --- release_notes.md | 6 +++--- src/DotNetWorker.Core/DotNetWorker.Core.csproj | 2 +- src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj | 2 +- src/DotNetWorker/DotNetWorker.csproj | 2 +- .../FunctionExecutor/DependentAssemblyTest.cs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/release_notes.md b/release_notes.md index c7c0f8cd4..5bf13c50a 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,11 +4,11 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker (metapackage) +### Microsoft.Azure.Functions.Worker (metapackage) 2.2.0 - -### Microsoft.Azure.Functions.Worker.Core +### Microsoft.Azure.Functions.Worker.Core 2.2.0 - Support setting `IFunctionExecutor` in invocation features: `FunctionContext.Features` (#3200) -### Microsoft.Azure.Functions.Worker.Grpc +### Microsoft.Azure.Functions.Worker.Grpc 2.2.0 - diff --git a/src/DotNetWorker.Core/DotNetWorker.Core.csproj b/src/DotNetWorker.Core/DotNetWorker.Core.csproj index 316b1c903..2550b28de 100644 --- a/src/DotNetWorker.Core/DotNetWorker.Core.csproj +++ b/src/DotNetWorker.Core/DotNetWorker.Core.csproj @@ -9,7 +9,7 @@ Microsoft.Azure.Functions.Worker.Core true 2 - 1 + 2 0 diff --git a/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj b/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj index 949718c97..910940bb5 100644 --- a/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj +++ b/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj @@ -9,7 +9,7 @@ Microsoft.Azure.Functions.Worker.Grpc true 2 - 1 + 2 0 true diff --git a/src/DotNetWorker/DotNetWorker.csproj b/src/DotNetWorker/DotNetWorker.csproj index e8ffb02c8..34c9d19e1 100644 --- a/src/DotNetWorker/DotNetWorker.csproj +++ b/src/DotNetWorker/DotNetWorker.csproj @@ -9,7 +9,7 @@ Microsoft.Azure.Functions.Worker true 2 - 1 + 2 0 diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs index e76050829..b5dc262af 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs @@ -139,7 +139,7 @@ public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunction private global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor CreateDefaultExecutorInstance(global::Microsoft.Azure.Functions.Worker.FunctionContext context) { - var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=2.1.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; + var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; var defaultExecutorType = global::System.Type.GetType(defaultExecutorFullName); return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; From 37f97968dfba16d2a5e2b612aec8569d8b286d42 Mon Sep 17 00:00:00 2001 From: Jacob Viau Date: Wed, 29 Oct 2025 09:07:42 -0700 Subject: [PATCH 35/45] Remove extra global.json, AppInsights test to net8.0 (#3219) * Remove extra global.json, AppInsights test to net8.0 * Fix install-dotnet.yml update --- build/install-dotnet.yml | 20 ----------------- eng/ci/templates/steps/install-dotnet.yml | 22 ++++++++----------- host/src/FunctionsNetHost/global.json | 6 ----- .../Worker.ApplicationInsights.Tests.csproj | 7 ++---- 4 files changed, 11 insertions(+), 44 deletions(-) delete mode 100644 build/install-dotnet.yml delete mode 100644 host/src/FunctionsNetHost/global.json diff --git a/build/install-dotnet.yml b/build/install-dotnet.yml deleted file mode 100644 index 1cdaa3c38..000000000 --- a/build/install-dotnet.yml +++ /dev/null @@ -1,20 +0,0 @@ -steps: -# Some tests rely on 6.0.412 existing -- task: UseDotNet@2 - displayName: 'Install .NET6 SDK' - inputs: - packageType: 'sdk' - version: "6.x" - -- task: UseDotNet@2 - displayName: 'Install .NET7 SDK' - inputs: - packageType: 'sdk' - version: "7.x" - -# The SDK we use to build -- task: UseDotNet@2 - displayName: 'Install current .NET SDK' - inputs: - packageType: 'sdk' - useGlobalJson: true diff --git a/eng/ci/templates/steps/install-dotnet.yml b/eng/ci/templates/steps/install-dotnet.yml index f373c934f..139c7aeb6 100644 --- a/eng/ci/templates/steps/install-dotnet.yml +++ b/eng/ci/templates/steps/install-dotnet.yml @@ -1,19 +1,15 @@ steps: -- task: UseDotNet@2 # Needed by our projects and CI steps - displayName: Install .NET 6 +# Our tests target net8.0 +- task: UseDotNet@2 + displayName: 'Install .NET8 SDK' inputs: - packageType: sdk - version: 6.x + packageType: 'sdk' + version: "8.x" -- task: UseDotNet@2 # Needed by our projects and CI steps - displayName: Install .NET 7 +# The SDK we use to build +- task: UseDotNet@2 + displayName: 'Install current .NET SDK' inputs: - packageType: sdk - version: 7.x - -- task: UseDotNet@2 # The pinned SDK we use to build - displayName: Install .NET SDK from global.json - inputs: - packageType: sdk + packageType: 'sdk' useGlobalJson: true diff --git a/host/src/FunctionsNetHost/global.json b/host/src/FunctionsNetHost/global.json deleted file mode 100644 index 26228fbdf..000000000 --- a/host/src/FunctionsNetHost/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "8.0.110", - "rollForward": "latestMinor" - } -} \ No newline at end of file diff --git a/test/Worker.ApplicationInsights.Tests/Worker.ApplicationInsights.Tests.csproj b/test/Worker.ApplicationInsights.Tests/Worker.ApplicationInsights.Tests.csproj index 436cff015..6574cc2a5 100644 --- a/test/Worker.ApplicationInsights.Tests/Worker.ApplicationInsights.Tests.csproj +++ b/test/Worker.ApplicationInsights.Tests/Worker.ApplicationInsights.Tests.csproj @@ -1,7 +1,7 @@  - net6.0 + net8.0 false Microsoft.Azure.Functions.Worker.ApplicationInsights.Tests Microsoft.Azure.Functions.Worker.ApplicationInsights.Tests @@ -20,10 +20,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + From 9674a2ccbec59a702be5e00f9c046d8e12f35e3a Mon Sep 17 00:00:00 2001 From: sarah <35204912+satvu@users.noreply.github.com> Date: Thu, 6 Nov 2025 11:22:38 -0800 Subject: [PATCH 36/45] [SourceGen] Fix for duplicate properties generated (#3227) --- .../FunctionMetadataLoaderExtension.csproj | 2 +- ...unctionMetadataProviderGenerator.Parser.cs | 4 +- sdk/Sdk.Generators/Sdk.Generators.csproj | 2 +- sdk/release_notes.md | 5 +- .../BindingPropertiesParsingTests.cs | 266 ++++++++++++++++++ .../RetryOptionsTests.cs | 6 +- .../Sdk.Generator.Tests.csproj | 10 +- 7 files changed, 280 insertions(+), 15 deletions(-) create mode 100644 test/Sdk.Generator.Tests/FunctionMetadataProvider/BindingPropertiesParsingTests.cs diff --git a/sdk/FunctionMetadataLoaderExtension/FunctionMetadataLoaderExtension.csproj b/sdk/FunctionMetadataLoaderExtension/FunctionMetadataLoaderExtension.csproj index d354b3127..9b4c49c6b 100644 --- a/sdk/FunctionMetadataLoaderExtension/FunctionMetadataLoaderExtension.csproj +++ b/sdk/FunctionMetadataLoaderExtension/FunctionMetadataLoaderExtension.csproj @@ -11,7 +11,7 @@ - + diff --git a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs index 7acafdcaf..776c555d0 100644 --- a/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs +++ b/sdk/Sdk.Generators/FunctionMetadataProviderGenerator/FunctionMetadataProviderGenerator.Parser.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information.; using System; @@ -612,7 +612,7 @@ private bool TryCreateBindingDictionary(AttributeData bindingAttrData, string bi private bool TryGetAttributeProperties(AttributeData attributeData, Location? attribLocation, out IDictionary? attrProperties) { - attrProperties = new Dictionary(); + attrProperties = new Dictionary(StringComparer.OrdinalIgnoreCase); if (attributeData.ConstructorArguments.Any()) { diff --git a/sdk/Sdk.Generators/Sdk.Generators.csproj b/sdk/Sdk.Generators/Sdk.Generators.csproj index ed64e78c2..159fe0374 100644 --- a/sdk/Sdk.Generators/Sdk.Generators.csproj +++ b/sdk/Sdk.Generators/Sdk.Generators.csproj @@ -10,7 +10,7 @@ false true 3 - 5 + 6 true true diff --git a/sdk/release_notes.md b/sdk/release_notes.md index fb9fe089c..45c44e07c 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -8,7 +8,8 @@ - Build no longer generates `functions.metadata` if source-generated metadata provider is enabled. (#2974) - Fixing `dotnet run` to work on Windows when core tools is installed from NPM (#3127) +- `Microsoft.Azure.Functions.Worker.Sdk.Generators` bumped to `1.3.6`. -### Microsoft.Azure.Functions.Worker.Sdk.Generators +### Microsoft.Azure.Functions.Worker.Sdk.Generators 1.3.6 -- +- Fix bug that results in duplicate properties recorded for a binding (#3227) diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProvider/BindingPropertiesParsingTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProvider/BindingPropertiesParsingTests.cs new file mode 100644 index 000000000..abda775de --- /dev/null +++ b/test/Sdk.Generator.Tests/FunctionMetadataProvider/BindingPropertiesParsingTests.cs @@ -0,0 +1,266 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Reflection; +using System.Threading.Tasks; +using Microsoft.Azure.Functions.Sdk.Generator.Tests; +using Microsoft.Azure.Functions.Worker.Sdk.Generators; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Xunit; + +namespace Microsoft.Azure.Functions.Sdk.Generator.FunctionMetadataProvider.Tests +{ + public partial class FunctionMetadataProviderGeneratorTests + { + public class BindingPropertiesParsingTests + { + private readonly Assembly[] _referencedExtensionAssemblies; + + public BindingPropertiesParsingTests() + { + var abstractionsExtension = Assembly.LoadFrom("Microsoft.Azure.Functions.Worker.Extensions.Abstractions.dll"); + var httpExtension = Assembly.LoadFrom("Microsoft.Azure.Functions.Worker.Extensions.Http.dll"); + var hostingExtension = typeof(HostBuilder).Assembly; + var diExtension = typeof(DefaultServiceProviderFactory).Assembly; + var hostingAbExtension = typeof(IHost).Assembly; + var diAbExtension = typeof(IServiceCollection).Assembly; + var mcpExtension = Assembly.LoadFrom("Microsoft.Azure.Functions.Worker.Extensions.Mcp.dll"); + var blobExtension = Assembly.LoadFrom("Microsoft.Azure.Functions.Worker.Extensions.Storage.Blobs.dll"); + + _referencedExtensionAssemblies = new[] + { + abstractionsExtension, + httpExtension, + hostingExtension, + hostingAbExtension, + diExtension, + diAbExtension, + mcpExtension, + blobExtension + }; + } + + [Theory] + [InlineData(LanguageVersion.CSharp7_3)] + [InlineData(LanguageVersion.CSharp8)] + [InlineData(LanguageVersion.CSharp9)] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersion.Latest)] + public async Task BindingPropertiesWithDefaultValueDoeNotCreateDuplicatesWhenSetAsNamedArgument(LanguageVersion languageVersion) + { + string inputCode = """ + using System; + using System.Collections.Generic; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + using Microsoft.Azure.Functions.Worker.Extensions.Mcp; + + namespace MyCompany.Task + { + public static class SaveSnippetFunction + { + [Function(nameof(SaveSnippetFunction))] + [BlobOutput("blobPath")] + public static string SaveSnippet( + [McpToolTrigger("someString", "someString")] + ToolInvocationContext context, + [McpToolProperty("someString", "someString", IsRequired = true)] + string name + ) + { + throw new NotImplementedException(); + } + } + } + """; + + string expectedGeneratedFileName = $"GeneratedFunctionMetadataProvider.g.cs"; + string expectedOutput = $$""" + // + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Text.Json; + using System.Threading.Tasks; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + + namespace TestProject + { + /// + /// Custom implementation that returns function metadata definitions for the current worker. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + {{Constants.GeneratedCodeAttribute}} + public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider + { + /// + public Task> GetFunctionMetadataAsync(string directory) + { + var metadataList = new List(); + var Function0RawBindings = new List(); + Function0RawBindings.Add(@"{""name"":""$return"",""type"":""blob"",""direction"":""Out"",""blobPath"":""blobPath""}"); + Function0RawBindings.Add(@"{""name"":""context"",""type"":""mcpToolTrigger"",""direction"":""In"",""toolName"":""someString"",""description"":""someString""}"); + Function0RawBindings.Add(@"{""name"":""name"",""type"":""mcpToolProperty"",""direction"":""In"",""propertyName"":""someString"",""description"":""someString"",""isRequired"":true,""dataType"":""String""}"); + + var Function0 = new DefaultFunctionMetadata + { + Language = "dotnet-isolated", + Name = "SaveSnippetFunction", + EntryPoint = "MyCompany.Task.SaveSnippetFunction.SaveSnippet", + RawBindings = Function0RawBindings, + ScriptFile = "TestProject.dll" + }; + metadataList.Add(Function0); + + return global::System.Threading.Tasks.Task.FromResult(metadataList.ToImmutableArray()); + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + {{Constants.GeneratedCodeAttribute}} + public static class WorkerHostBuilderFunctionMetadataProviderExtension + { + /// + /// Adds the GeneratedFunctionMetadataProvider to the service collection. + /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. + /// + public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) + { + builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + return builder; + } + } + } + """; + + await TestHelpers.RunTestAsync( + _referencedExtensionAssemblies, + inputCode, + expectedGeneratedFileName, + expectedOutput, + languageVersion: languageVersion); + } + + [Theory] + [InlineData(LanguageVersion.CSharp7_3)] + [InlineData(LanguageVersion.CSharp8)] + [InlineData(LanguageVersion.CSharp9)] + [InlineData(LanguageVersion.CSharp10)] + [InlineData(LanguageVersion.CSharp11)] + [InlineData(LanguageVersion.Latest)] + public async Task BindingPropertyWithDefaultValueIsSet(LanguageVersion languageVersion) + { + string inputCode = """ + using System; + using System.Collections.Generic; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Http; + using Microsoft.Azure.Functions.Worker.Extensions.Mcp; + + namespace MyCompany.Task + { + public static class SaveSnippetFunction + { + [Function(nameof(SaveSnippetFunction))] + [BlobOutput("blobPath")] + public static string SaveSnippet( + [McpToolTrigger("someString", "someString")] + ToolInvocationContext context, + [McpToolProperty("someString", "someString")] + string name + ) + { + throw new NotImplementedException(); + } + } + } + """; + + string expectedGeneratedFileName = $"GeneratedFunctionMetadataProvider.g.cs"; + string expectedOutput = $$""" + // + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Text.Json; + using System.Threading.Tasks; + using Microsoft.Azure.Functions.Worker; + using Microsoft.Azure.Functions.Worker.Core.FunctionMetadata; + using Microsoft.Extensions.DependencyInjection; + using Microsoft.Extensions.Hosting; + + namespace TestProject + { + /// + /// Custom implementation that returns function metadata definitions for the current worker. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + {{Constants.GeneratedCodeAttribute}} + public class GeneratedFunctionMetadataProvider : IFunctionMetadataProvider + { + /// + public Task> GetFunctionMetadataAsync(string directory) + { + var metadataList = new List(); + var Function0RawBindings = new List(); + Function0RawBindings.Add(@"{""name"":""$return"",""type"":""blob"",""direction"":""Out"",""blobPath"":""blobPath""}"); + Function0RawBindings.Add(@"{""name"":""context"",""type"":""mcpToolTrigger"",""direction"":""In"",""toolName"":""someString"",""description"":""someString""}"); + Function0RawBindings.Add(@"{""name"":""name"",""type"":""mcpToolProperty"",""direction"":""In"",""propertyName"":""someString"",""description"":""someString"",""isRequired"":false,""dataType"":""String""}"); + + var Function0 = new DefaultFunctionMetadata + { + Language = "dotnet-isolated", + Name = "SaveSnippetFunction", + EntryPoint = "MyCompany.Task.SaveSnippetFunction.SaveSnippet", + RawBindings = Function0RawBindings, + ScriptFile = "TestProject.dll" + }; + metadataList.Add(Function0); + + return global::System.Threading.Tasks.Task.FromResult(metadataList.ToImmutableArray()); + } + } + + /// + /// Extension methods to enable registration of the custom implementation generated for the current worker. + /// + {{Constants.GeneratedCodeAttribute}} + public static class WorkerHostBuilderFunctionMetadataProviderExtension + { + /// + /// Adds the GeneratedFunctionMetadataProvider to the service collection. + /// During initialization, the worker will return generated function metadata instead of relying on the Azure Functions host for function indexing. + /// + public static IHostBuilder ConfigureGeneratedFunctionMetadataProvider(this IHostBuilder builder) + { + builder.ConfigureServices(s => + { + s.AddSingleton(); + }); + return builder; + } + } + } + """; + + await TestHelpers.RunTestAsync( + _referencedExtensionAssemblies, + inputCode, + expectedGeneratedFileName, + expectedOutput, + languageVersion: languageVersion); + } + } + } +} diff --git a/test/Sdk.Generator.Tests/FunctionMetadataProvider/RetryOptionsTests.cs b/test/Sdk.Generator.Tests/FunctionMetadataProvider/RetryOptionsTests.cs index caec922fb..f201634cf 100644 --- a/test/Sdk.Generator.Tests/FunctionMetadataProvider/RetryOptionsTests.cs +++ b/test/Sdk.Generator.Tests/FunctionMetadataProvider/RetryOptionsTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. using System.Reflection; @@ -27,7 +27,6 @@ public RetryOptionsTests() var diExtension = typeof(DefaultServiceProviderFactory).Assembly; var hostingAbExtension = typeof(IHost).Assembly; var diAbExtension = typeof(IServiceCollection).Assembly; - var cosmosDBExtension = Assembly.LoadFrom("Microsoft.Azure.Functions.Worker.Extensions.CosmosDB.dll"); var timerExtension = Assembly.LoadFrom("Microsoft.Azure.Functions.Worker.Extensions.Timer.dll"); @@ -39,8 +38,7 @@ public RetryOptionsTests() hostingAbExtension, diExtension, diAbExtension, - timerExtension, - cosmosDBExtension + timerExtension }; } diff --git a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj index d2192b599..cf86f476f 100644 --- a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj +++ b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj @@ -16,20 +16,20 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + + - - - + + From c7c5e53ef32db523da1d6f6c0bf2f60964be5349 Mon Sep 17 00:00:00 2001 From: sarah <35204912+satvu@users.noreply.github.com> Date: Thu, 6 Nov 2025 13:28:10 -0800 Subject: [PATCH 37/45] Prep for SDK release (#3228) --- sdk/Sdk/Sdk.csproj | 2 +- sdk/release_notes.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/Sdk/Sdk.csproj b/sdk/Sdk/Sdk.csproj index cc22b2609..9b3e90b49 100644 --- a/sdk/Sdk/Sdk.csproj +++ b/sdk/Sdk/Sdk.csproj @@ -3,7 +3,7 @@ 2 0 - 5 + 6 netstandard2.0;net472 Microsoft.Azure.Functions.Worker.Sdk This package provides development time support for the Azure Functions .NET Worker. diff --git a/sdk/release_notes.md b/sdk/release_notes.md index 45c44e07c..da6b6dc60 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -4,7 +4,7 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Sdk +### Microsoft.Azure.Functions.Worker.Sdk 2.0.6 - Build no longer generates `functions.metadata` if source-generated metadata provider is enabled. (#2974) - Fixing `dotnet run` to work on Windows when core tools is installed from NPM (#3127) From 6891db223c4f1e6089416350a807011ecc584a6a Mon Sep 17 00:00:00 2001 From: sarah <35204912+satvu@users.noreply.github.com> Date: Thu, 6 Nov 2025 15:37:46 -0800 Subject: [PATCH 38/45] Clear release notes (#3229) --- sdk/release_notes.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/sdk/release_notes.md b/sdk/release_notes.md index da6b6dc60..93083cfa3 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -4,12 +4,10 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Sdk 2.0.6 +### Microsoft.Azure.Functions.Worker.Sdk -- Build no longer generates `functions.metadata` if source-generated metadata provider is enabled. (#2974) -- Fixing `dotnet run` to work on Windows when core tools is installed from NPM (#3127) -- `Microsoft.Azure.Functions.Worker.Sdk.Generators` bumped to `1.3.6`. +- -### Microsoft.Azure.Functions.Worker.Sdk.Generators 1.3.6 +### Microsoft.Azure.Functions.Worker.Sdk.Generators -- Fix bug that results in duplicate properties recorded for a binding (#3227) +- \ No newline at end of file From 3a7d27e45350f19a6550cb184671f0860a7fec8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20=C3=85gren?= Date: Fri, 7 Nov 2025 00:50:53 +0100 Subject: [PATCH 39/45] Use name of function instead of class for input/output example (#3212) The example function with input/output is used standalone on https://learn.microsoft.com/en-us/azure/azure-functions/functions-bindings-storage-queue-trigger . When copying this the code does not compile because it uses the class name, which is not included in the example (doc snippet), as function name. Renaming the function to something more explicit, and using that as function name, should improve the example. --- samples/Extensions/Queue/QueueFunction.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Extensions/Queue/QueueFunction.cs b/samples/Extensions/Queue/QueueFunction.cs index cfa26b8b7..5248ce724 100644 --- a/samples/Extensions/Queue/QueueFunction.cs +++ b/samples/Extensions/Queue/QueueFunction.cs @@ -20,9 +20,9 @@ public QueueFunction(ILogger logger) // // - [Function(nameof(QueueFunction))] + [Function(nameof(QueueInputOutputFunction))] [QueueOutput("output-queue")] - public string[] Run([QueueTrigger("input-queue")] Album myQueueItem, FunctionContext context) + public string[] QueueInputOutputFunction([QueueTrigger("input-queue")] Album myQueueItem, FunctionContext context) // { // Use a string array to return more than one message. From a50707eb2c462098f6885a0c45d82956e05dccfe Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Tue, 11 Nov 2025 10:30:52 -0800 Subject: [PATCH 40/45] 2.50.0 worker packages release prep (#3237) * Adding net10 TFM to worker packages (#3123) * Fix steps indentation (#3117) * .NET 10 TFM support changes. --------- Co-authored-by: Jacob Viau * Updating the dependencies from P6 to NET10. * Switch sdk to 10.0.100 GA --------- Co-authored-by: Jacob Viau --- global.json | 2 +- release_notes.md | 15 +++++++++------ .../DotNetWorker.ApplicationInsights.csproj | 4 ++-- .../DotNetWorker.Core.csproj | 19 ++++++------------- .../DotNetWorker.Grpc.csproj | 6 +++--- src/DotNetWorker/DotNetWorker.csproj | 4 ++-- test/E2ETests/E2EApps/E2EApp/E2EApp.csproj | 2 +- test/E2ETests/E2ETests/E2ETests.csproj | 4 ++-- .../Sdk.Analyzers.Tests.csproj | 2 +- .../FunctionExecutor/DependentAssemblyTest.cs | 2 +- .../Sdk.Generator.Tests.csproj | 10 +++++----- test/TestUtility/TestUtility.csproj | 10 +++++----- .../Blob/StreamTests.cs | 2 +- 13 files changed, 39 insertions(+), 43 deletions(-) diff --git a/global.json b/global.json index e51901200..320ad044d 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.306", + "version": "10.0.100", "allowPrerelease": true, "rollForward": "latestFeature" }, diff --git a/release_notes.md b/release_notes.md index 5bf13c50a..577f8f61f 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,11 +4,14 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker (metapackage) 2.2.0 -- +### Microsoft.Azure.Functions.Worker (metapackage) 2.50.0 -### Microsoft.Azure.Functions.Worker.Core 2.2.0 -- Support setting `IFunctionExecutor` in invocation features: `FunctionContext.Features` (#3200) +- Adding `net10.0` TFM support. -### Microsoft.Azure.Functions.Worker.Grpc 2.2.0 -- +### Microsoft.Azure.Functions.Worker.Core 2.50.0 + +- Adding `net10.0` TFM support. + +### Microsoft.Azure.Functions.Worker.Grpc 2.50.0 + +- Adding `net10.0` TFM support. diff --git a/src/DotNetWorker.ApplicationInsights/DotNetWorker.ApplicationInsights.csproj b/src/DotNetWorker.ApplicationInsights/DotNetWorker.ApplicationInsights.csproj index 48dfa8621..8f70d04b2 100644 --- a/src/DotNetWorker.ApplicationInsights/DotNetWorker.ApplicationInsights.csproj +++ b/src/DotNetWorker.ApplicationInsights/DotNetWorker.ApplicationInsights.csproj @@ -6,7 +6,7 @@ Microsoft.Azure.Functions.Worker.ApplicationInsights Microsoft.Azure.Functions.Worker.ApplicationInsights 2 - 0 + 50 0 README.md $(BeforePack);GetReleaseNotes @@ -17,7 +17,7 @@ - + diff --git a/src/DotNetWorker.Core/DotNetWorker.Core.csproj b/src/DotNetWorker.Core/DotNetWorker.Core.csproj index 2550b28de..1c4ad650d 100644 --- a/src/DotNetWorker.Core/DotNetWorker.Core.csproj +++ b/src/DotNetWorker.Core/DotNetWorker.Core.csproj @@ -2,15 +2,15 @@ Library - net6.0;net8.0;net9.0;netstandard2.0 + net6.0;net8.0;net9.0;net10.0;netstandard2.0 Microsoft.Azure.Functions.Worker.Core This library provides the core functionality to build an Azure Functions .NET Worker, adding support for the isolated, out-of-process execution model. Microsoft.Azure.Functions.Worker.Core Microsoft.Azure.Functions.Worker.Core true 2 - 2 - 0 + 50 + 0 @@ -22,19 +22,12 @@ + - - - - - - - - - - + + diff --git a/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj b/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj index 910940bb5..4875586fe 100644 --- a/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj +++ b/src/DotNetWorker.Grpc/DotNetWorker.Grpc.csproj @@ -2,15 +2,15 @@ Library - net6.0;net7.0;net8.0;net9.0;netstandard2.0 + net6.0;net7.0;net8.0;net9.0;net10.0;netstandard2.0 Microsoft.Azure.Functions.Worker.Grpc This library provides gRPC support for Azure Functions .NET Worker communication with the Azure Functions Host. Microsoft.Azure.Functions.Worker.Grpc Microsoft.Azure.Functions.Worker.Grpc true 2 - 2 - 0 + 50 + 0 true diff --git a/src/DotNetWorker/DotNetWorker.csproj b/src/DotNetWorker/DotNetWorker.csproj index 34c9d19e1..56863d7d5 100644 --- a/src/DotNetWorker/DotNetWorker.csproj +++ b/src/DotNetWorker/DotNetWorker.csproj @@ -2,14 +2,14 @@ Library - net6.0;net7.0;net8.0;net9.0;netstandard2.0 + net6.0;net7.0;net8.0;net9.0;net10.0;netstandard2.0 Microsoft.Azure.Functions.Worker This library enables you to create an Azure Functions .NET Worker, adding support for the isolated, out-of-process execution model. Microsoft.Azure.Functions.Worker Microsoft.Azure.Functions.Worker true 2 - 2 + 50 0 diff --git a/test/E2ETests/E2EApps/E2EApp/E2EApp.csproj b/test/E2ETests/E2EApps/E2EApp/E2EApp.csproj index 45d9b700f..3409c286f 100644 --- a/test/E2ETests/E2EApps/E2EApp/E2EApp.csproj +++ b/test/E2ETests/E2EApps/E2EApp/E2EApp.csproj @@ -47,6 +47,6 @@ - + \ No newline at end of file diff --git a/test/E2ETests/E2ETests/E2ETests.csproj b/test/E2ETests/E2ETests/E2ETests.csproj index 9b23ae8e8..a2908ea02 100644 --- a/test/E2ETests/E2ETests/E2ETests.csproj +++ b/test/E2ETests/E2ETests/E2ETests.csproj @@ -12,10 +12,10 @@ - + - + all diff --git a/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj b/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj index 0ca059f81..2067b79d5 100644 --- a/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj +++ b/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj @@ -13,7 +13,7 @@ - + all diff --git a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs index b5dc262af..6854a4e36 100644 --- a/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs +++ b/test/Sdk.Generator.Tests/FunctionExecutor/DependentAssemblyTest.cs @@ -139,7 +139,7 @@ public DirectFunctionExecutor(global::Microsoft.Azure.Functions.Worker.IFunction private global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor CreateDefaultExecutorInstance(global::Microsoft.Azure.Functions.Worker.FunctionContext context) { - var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=2.2.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; + var defaultExecutorFullName = "Microsoft.Azure.Functions.Worker.Invocation.DefaultFunctionExecutor, Microsoft.Azure.Functions.Worker.Core, Version=2.50.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; var defaultExecutorType = global::System.Type.GetType(defaultExecutorFullName); return ActivatorUtilities.CreateInstance(context.InstanceServices, defaultExecutorType) as global::Microsoft.Azure.Functions.Worker.Invocation.IFunctionExecutor; diff --git a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj index cf86f476f..f17278856 100644 --- a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj +++ b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj @@ -36,17 +36,17 @@ - - - - + + + + - + diff --git a/test/TestUtility/TestUtility.csproj b/test/TestUtility/TestUtility.csproj index 46e6caf2d..482a2a7a5 100644 --- a/test/TestUtility/TestUtility.csproj +++ b/test/TestUtility/TestUtility.csproj @@ -10,11 +10,11 @@ - - - - - + + + + + diff --git a/test/Worker.Extensions.Tests/Blob/StreamTests.cs b/test/Worker.Extensions.Tests/Blob/StreamTests.cs index d7713f368..8a745326c 100644 --- a/test/Worker.Extensions.Tests/Blob/StreamTests.cs +++ b/test/Worker.Extensions.Tests/Blob/StreamTests.cs @@ -246,7 +246,7 @@ public async Task ConvertAsync_StreamCollection_WithFilePath_Throws_ReturnsFaile // Assert Assert.Equal(ConversionStatus.Failed, conversionResult.Status); Assert.IsType(conversionResult.Error); - Assert.Contains("Deserialization of types without a parameterless constructor, a singular parameterized constructor, or a parameterized constructor annotated with 'JsonConstructorAttribute' is not supported.", conversionResult.Error.Message); + Assert.Contains("Deserialization of interface or abstract types is not supported.", conversionResult.Error.Message); } } } From ebac71217a2e48c2e39b1d7eafd6d901068b3f8f Mon Sep 17 00:00:00 2001 From: Jacob Viau Date: Wed, 12 Nov 2025 14:04:50 -0800 Subject: [PATCH 41/45] Address some warnings from dotnet build -check (#3234) --- Directory.Build.props | 5 -- sdk/Sdk.Analyzers/Sdk.Analyzers.csproj | 4 -- sdk/Sdk.Analyzers/tools/install.ps1 | 58 ----------------- sdk/Sdk.Analyzers/tools/uninstall.ps1 | 65 ------------------- .../DotNetWorker.OpenTelemetry.Tests.csproj | 6 +- .../DotNetWorkerTests.csproj | 6 +- test/Sdk.E2ETests/Sdk.E2ETests.csproj | 4 +- .../Sdk.Generator.Tests.csproj | 2 +- 8 files changed, 4 insertions(+), 146 deletions(-) delete mode 100644 sdk/Sdk.Analyzers/tools/install.ps1 delete mode 100644 sdk/Sdk.Analyzers/tools/uninstall.ps1 diff --git a/Directory.Build.props b/Directory.Build.props index 34cd20ef5..7da35374a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,5 @@ - - - true - - $(MSBuildThisFileDirectory) $(RepoRoot)eng/ diff --git a/sdk/Sdk.Analyzers/Sdk.Analyzers.csproj b/sdk/Sdk.Analyzers/Sdk.Analyzers.csproj index aaef6653a..bfe34892a 100644 --- a/sdk/Sdk.Analyzers/Sdk.Analyzers.csproj +++ b/sdk/Sdk.Analyzers/Sdk.Analyzers.csproj @@ -27,10 +27,6 @@ - - - - diff --git a/sdk/Sdk.Analyzers/tools/install.ps1 b/sdk/Sdk.Analyzers/tools/install.ps1 deleted file mode 100644 index c1c3d8822..000000000 --- a/sdk/Sdk.Analyzers/tools/install.ps1 +++ /dev/null @@ -1,58 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -if($project.Object.SupportsPackageDependencyResolution) -{ - if($project.Object.SupportsPackageDependencyResolution()) - { - # Do not install analyzers via install.ps1, instead let the project system handle it. - return - } -} - -$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve - -foreach($analyzersPath in $analyzersPaths) -{ - if (Test-Path $analyzersPath) - { - # Install the language agnostic analyzers. - foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) - } - } - } -} - -# $project.Type gives the language name like (C# or VB.NET) -$languageFolder = "" -if($project.Type -eq "C#") -{ - $languageFolder = "cs" -} -if($project.Type -eq "VB.NET") -{ - $languageFolder = "vb" -} -if($languageFolder -eq "") -{ - return -} - -foreach($analyzersPath in $analyzersPaths) -{ - # Install language specific analyzers. - $languageAnalyzersPath = join-path $analyzersPath $languageFolder - if (Test-Path $languageAnalyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Add($analyzerFilePath.FullName) - } - } - } -} \ No newline at end of file diff --git a/sdk/Sdk.Analyzers/tools/uninstall.ps1 b/sdk/Sdk.Analyzers/tools/uninstall.ps1 deleted file mode 100644 index 65a862370..000000000 --- a/sdk/Sdk.Analyzers/tools/uninstall.ps1 +++ /dev/null @@ -1,65 +0,0 @@ -param($installPath, $toolsPath, $package, $project) - -if($project.Object.SupportsPackageDependencyResolution) -{ - if($project.Object.SupportsPackageDependencyResolution()) - { - # Do not uninstall analyzers via uninstall.ps1, instead let the project system handle it. - return - } -} - -$analyzersPaths = Join-Path (Join-Path (Split-Path -Path $toolsPath -Parent) "analyzers") * -Resolve - -foreach($analyzersPath in $analyzersPaths) -{ - # Uninstall the language agnostic analyzers. - if (Test-Path $analyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$analyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) - } - } - } -} - -# $project.Type gives the language name like (C# or VB.NET) -$languageFolder = "" -if($project.Type -eq "C#") -{ - $languageFolder = "cs" -} -if($project.Type -eq "VB.NET") -{ - $languageFolder = "vb" -} -if($languageFolder -eq "") -{ - return -} - -foreach($analyzersPath in $analyzersPaths) -{ - # Uninstall language specific analyzers. - $languageAnalyzersPath = join-path $analyzersPath $languageFolder - if (Test-Path $languageAnalyzersPath) - { - foreach ($analyzerFilePath in Get-ChildItem -Path "$languageAnalyzersPath\*.dll" -Exclude *.resources.dll) - { - if($project.Object.AnalyzerReferences) - { - try - { - $project.Object.AnalyzerReferences.Remove($analyzerFilePath.FullName) - } - catch - { - - } - } - } - } -} \ No newline at end of file diff --git a/test/DotNetWorker.OpenTelemetry.Tests/DotNetWorker.OpenTelemetry.Tests.csproj b/test/DotNetWorker.OpenTelemetry.Tests/DotNetWorker.OpenTelemetry.Tests.csproj index 23a7cd5d8..b9351243c 100644 --- a/test/DotNetWorker.OpenTelemetry.Tests/DotNetWorker.OpenTelemetry.Tests.csproj +++ b/test/DotNetWorker.OpenTelemetry.Tests/DotNetWorker.OpenTelemetry.Tests.csproj @@ -9,7 +9,6 @@ preview ..\..\key.snk disable - true @@ -17,10 +16,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/test/DotNetWorkerTests/DotNetWorkerTests.csproj b/test/DotNetWorkerTests/DotNetWorkerTests.csproj index d7f87d381..fa72e6f1b 100644 --- a/test/DotNetWorkerTests/DotNetWorkerTests.csproj +++ b/test/DotNetWorkerTests/DotNetWorkerTests.csproj @@ -9,7 +9,6 @@ preview ..\..\key.snk disable - true @@ -18,10 +17,7 @@ - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/test/Sdk.E2ETests/Sdk.E2ETests.csproj b/test/Sdk.E2ETests/Sdk.E2ETests.csproj index 8e3743503..5a3bdf227 100644 --- a/test/Sdk.E2ETests/Sdk.E2ETests.csproj +++ b/test/Sdk.E2ETests/Sdk.E2ETests.csproj @@ -36,9 +36,7 @@ - - Always - + diff --git a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj index f17278856..d07651f08 100644 --- a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj +++ b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj @@ -66,7 +66,7 @@ - + \ No newline at end of file From 4aed564646df811322c2ee8ee79ad69893de176e Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 12 Nov 2025 14:47:11 -0800 Subject: [PATCH 42/45] Reset release notes (post 2.50.0 release) (#3244) --- release_notes.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/release_notes.md b/release_notes.md index 577f8f61f..37057e890 100644 --- a/release_notes.md +++ b/release_notes.md @@ -4,14 +4,14 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker (metapackage) 2.50.0 +### Microsoft.Azure.Functions.Worker (metapackage) -- Adding `net10.0` TFM support. +- -### Microsoft.Azure.Functions.Worker.Core 2.50.0 +### Microsoft.Azure.Functions.Worker.Core -- Adding `net10.0` TFM support. +- -### Microsoft.Azure.Functions.Worker.Grpc 2.50.0 +### Microsoft.Azure.Functions.Worker.Grpc -- Adding `net10.0` TFM support. +- From 85b603f7139bfa671d586dc6a05080d8e7d83f2d Mon Sep 17 00:00:00 2001 From: Lilian Kasem Date: Wed, 12 Nov 2025 14:50:06 -0800 Subject: [PATCH 43/45] Bump "coverlet.collector" to "6.0.4" (#3245) --- test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj | 2 +- test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj | 2 +- .../Worker.Extensions.SignalRService.Tests.csproj | 2 +- .../Worker.Extensions.Http.AspNetCore.Tests.csproj | 2 +- .../Worker.Extensions.Timer.Tests.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj b/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj index 2067b79d5..872d41b88 100644 --- a/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj +++ b/test/Sdk.Analyzers.Tests/Sdk.Analyzers.Tests.csproj @@ -19,7 +19,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj index d07651f08..4e79e725e 100644 --- a/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj +++ b/test/Sdk.Generator.Tests/Sdk.Generator.Tests.csproj @@ -17,7 +17,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/Worker.Extensions.SignalRService.Tests/Worker.Extensions.SignalRService.Tests.csproj b/test/Worker.Extensions.SignalRService.Tests/Worker.Extensions.SignalRService.Tests.csproj index 4e707487e..5bb626984 100644 --- a/test/Worker.Extensions.SignalRService.Tests/Worker.Extensions.SignalRService.Tests.csproj +++ b/test/Worker.Extensions.SignalRService.Tests/Worker.Extensions.SignalRService.Tests.csproj @@ -16,7 +16,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/extensions/Worker.Extensions.Http.AspNetCore.Tests/Worker.Extensions.Http.AspNetCore.Tests.csproj b/test/extensions/Worker.Extensions.Http.AspNetCore.Tests/Worker.Extensions.Http.AspNetCore.Tests.csproj index d8e416a7c..74ea72db1 100644 --- a/test/extensions/Worker.Extensions.Http.AspNetCore.Tests/Worker.Extensions.Http.AspNetCore.Tests.csproj +++ b/test/extensions/Worker.Extensions.Http.AspNetCore.Tests/Worker.Extensions.Http.AspNetCore.Tests.csproj @@ -24,7 +24,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/test/extensions/Worker.Extensions.Timer.Tests/Worker.Extensions.Timer.Tests.csproj b/test/extensions/Worker.Extensions.Timer.Tests/Worker.Extensions.Timer.Tests.csproj index d6a5b1726..2e55a1320 100644 --- a/test/extensions/Worker.Extensions.Timer.Tests/Worker.Extensions.Timer.Tests.csproj +++ b/test/extensions/Worker.Extensions.Timer.Tests/Worker.Extensions.Timer.Tests.csproj @@ -13,7 +13,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From eef3aab11f0d8e6aa9160940204fdbd9ec296aa6 Mon Sep 17 00:00:00 2001 From: Fabio Cavalcante Date: Wed, 12 Nov 2025 16:10:29 -0800 Subject: [PATCH 44/45] Restore `functions.metadata` generation (#3246) Reversed suppression of `functions.metadata` file generation in the SDK targets file, restoring default behavior. --- ...crosoft.Azure.Functions.Worker.Sdk.targets | 10 +- sdk/Sdk/Tasks/GenerateFunctionMetadata.cs | 9 -- sdk/release_notes.md | 4 +- .../AspNetCore/CancellationEndToEndTests.cs | 8 +- .../E2ETests/Fixtures/FunctionAppFixture.cs | 5 +- test/Sdk.E2ETests/InnerBuildTests.cs | 42 +++---- test/Sdk.E2ETests/ProcessWrapper.cs | 33 +++--- test/Sdk.E2ETests/ProjectBuilder.cs | 106 ----------------- test/Sdk.E2ETests/PublishTests.cs | 109 +++++++++--------- test/Sdk.E2ETests/TempDirectory.cs | 41 ------- test/Sdk.E2ETests/TestUtility.cs | 107 +++++++++++++++-- test/Sdk.E2ETests/ZipDeployTests.cs | 28 ++--- 12 files changed, 216 insertions(+), 286 deletions(-) delete mode 100644 test/Sdk.E2ETests/ProjectBuilder.cs delete mode 100644 test/Sdk.E2ETests/TempDirectory.cs diff --git a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets index 8133074c8..dfc2c085d 100644 --- a/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets +++ b/sdk/Sdk/Targets/Microsoft.Azure.Functions.Worker.Sdk.targets @@ -48,9 +48,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and false true $(FunctionsEnableWorkerIndexing) - $(FunctionsEnableWorkerIndexing) - false - true + $(FunctionsEnableWorkerIndexing) true true @@ -193,10 +191,9 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and ReferencePaths="@(ReferencePath)" ExtensionsCsProjFilePath="$(ExtensionsCsProj)" AzureFunctionsVersion="$(AzureFunctionsVersion)" - WriteMetadataFile="$(FunctionsWriteMetadataJson)" TargetFrameworkIdentifier="$(TargetFrameworkIdentifier)" TargetFrameworkVersion="$(TargetFrameworkVersion)" - OutputPath="$(IntermediateOutputPath)" /> + OutputPath="$(IntermediateOutputPath)"/> @@ -250,8 +247,7 @@ WARNING: DO NOT MODIFY this file unless you are knowledgeable about MSBuild and - <_FunctionsAdditionalFile Include="$(_FunctionsMetadataPath)" Condition="'$(FunctionsWriteMetadataJson)' == 'true'" /> - <_FunctionsAdditionalFile Include="$(_FunctionsWorkerConfigPath);$(_FunctionsIntermediateExtensionUpdatedJsonPath)" /> + <_FunctionsAdditionalFile Include="$(_FunctionsMetadataPath);$(_FunctionsWorkerConfigPath);$(_FunctionsIntermediateExtensionUpdatedJsonPath)" /> <_FunctionsAdditionalFile Include="$(_FunctionsMetadataLoaderExtensionFile)" SubPath="$(_FunctionsExtensionsDirectory)/" /> <_NoneWithTargetPath Include="@(_FunctionsAdditionalFile)" TargetPath="%(_FunctionsAdditionalFile.SubPath)%(Filename)%(Extension)" diff --git a/sdk/Sdk/Tasks/GenerateFunctionMetadata.cs b/sdk/Sdk/Tasks/GenerateFunctionMetadata.cs index 58568268d..e54aa995a 100644 --- a/sdk/Sdk/Tasks/GenerateFunctionMetadata.cs +++ b/sdk/Sdk/Tasks/GenerateFunctionMetadata.cs @@ -26,8 +26,6 @@ public class GenerateFunctionMetadata : Task public string? ExtensionsCsProjFilePath { get; set; } - public bool WriteMetadataFile { get; set; } = true; - [Required] public ITaskItem[]? ReferencePaths { get; set; } @@ -69,18 +67,11 @@ public override bool Execute() private void WriteMetadataWithRetry(IEnumerable functions) { - if (!WriteMetadataFile) - { - Log.LogMessage("Skipping writing function metadata file."); - return; - } - int attempt = 0; while (attempt < 10) { try { - Log.LogMessage($"Writing function metadata to {OutputPath} directory."); FunctionMetadataJsonWriter.WriteMetadata(functions, OutputPath!); break; } diff --git a/sdk/release_notes.md b/sdk/release_notes.md index 93083cfa3..b83fe84e1 100644 --- a/sdk/release_notes.md +++ b/sdk/release_notes.md @@ -4,9 +4,9 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Sdk +### Microsoft.Azure.Functions.Worker.Sdk 2.0.7 -- +- Reversal of change to suppress generation of functions.metadata file ### Microsoft.Azure.Functions.Worker.Sdk.Generators diff --git a/test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs b/test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs index 0fc9dae82..4bfce6034 100644 --- a/test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs +++ b/test/E2ETests/E2ETests/AspNetCore/CancellationEndToEndTests.cs @@ -37,10 +37,10 @@ public async Task HttpTriggerFunctions_WithCancellationToken_BehaveAsExpected(st await TestUtility.RetryAsync(() => { invocationStartLog = _fixture.TestLogs.CoreToolsLogs.Where(p => p.Contains($"Executing 'Functions.{functionName}'")); - return Task.FromResult(invocationStartLog.Any()); + return Task.FromResult(invocationStartLog.Count() >= 1); }); - // The task should be cancelled before it completes, mimicking a client closing the connection. + // The task should be cancelled before it completes, mimicing a client closing the connection. // This should lead to the worker getting an InvocationCancel request from the functions host cts.Cancel(); await Assert.ThrowsAsync(async () => await task); @@ -49,13 +49,13 @@ await TestUtility.RetryAsync(() => await TestUtility.RetryAsync(() => { invocationEndLog = _fixture.TestLogs.CoreToolsLogs.Where(p => p.Contains($"Executed 'Functions.{functionName}'")); - return Task.FromResult(invocationEndLog.Any()); + return Task.FromResult(invocationEndLog.Count() >= 1); }); Assert.Contains(_fixture.TestLogs.CoreToolsLogs, log => log.Contains(expectedMessage, StringComparison.OrdinalIgnoreCase)); // TODO: 2/3 of the test invocations will fail until the host with the ForwarderProxy fix is released - uncomment this line when the fix is released - Assert.NotNull(invocationResult); // just here to 'use' invocationResult to avoid a warning. + Assert.NotEqual(null, invocationResult); // just here to 'use' invocationResult to avoid a warning. // Assert.Contains(_fixture.TestLogs.CoreToolsLogs, log => log.Contains($"'Functions.{functionName}' ({invocationResult}", StringComparison.OrdinalIgnoreCase)); } diff --git a/test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs b/test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs index be132df86..2eedeb61f 100644 --- a/test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs +++ b/test/E2ETests/E2ETests/Fixtures/FunctionAppFixture.cs @@ -17,11 +17,11 @@ namespace Microsoft.Azure.Functions.Tests.E2ETests { public class FunctionAppFixture : IAsyncLifetime { - private readonly string _testApp = Constants.TestAppNames.E2EApp; private readonly ILogger _logger; private bool _disposed; private Process _funcProcess; private JobObjectRegistry _jobObjectRegistry; + private string _testApp = Constants.TestAppNames.E2EApp; public FunctionAppFixture(IMessageSink messageSink) { @@ -32,8 +32,7 @@ public FunctionAppFixture(IMessageSink messageSink) _logger = loggerFactory.CreateLogger(); } - internal FunctionAppFixture(IMessageSink messageSink, string testApp) - : this(messageSink) + internal FunctionAppFixture(IMessageSink messageSink, string testApp) : this(messageSink) { _testApp = testApp; } diff --git a/test/Sdk.E2ETests/InnerBuildTests.cs b/test/Sdk.E2ETests/InnerBuildTests.cs index 455ec7307..e38822c14 100644 --- a/test/Sdk.E2ETests/InnerBuildTests.cs +++ b/test/Sdk.E2ETests/InnerBuildTests.cs @@ -1,7 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System; using System.IO; using System.Threading.Tasks; using Newtonsoft.Json.Linq; @@ -10,27 +9,25 @@ namespace Microsoft.Azure.Functions.Sdk.E2ETests { - public sealed class InnerBuildTests(ITestOutputHelper testOutputHelper) : IDisposable + public class InnerBuildTests { - private readonly ProjectBuilder _builder = new( - testOutputHelper, - Path.Combine(TestUtility.TestResourcesProjectsRoot, "FunctionApp01", "FunctionApp01.csproj")); + private readonly ITestOutputHelper _testOutputHelper; - [Theory] - [InlineData("", false)] - [InlineData("-p:FunctionsEnableWorkerIndexing=true", false)] - [InlineData("-p:FunctionsEnableWorkerIndexing=true -p:FunctionsWriteMetadataJson=false", false)] - [InlineData("-p:FunctionsEnableWorkerIndexing=true -p:FunctionsWriteMetadataJson=true", true)] - [InlineData("-p:FunctionsEnableWorkerIndexing=false", true)] - [InlineData("-p:FunctionsEnableWorkerIndexing=false -p:FunctionsWriteMetadataJson=false", false)] - [InlineData("-p:FunctionsEnableWorkerIndexing=false -p:FunctionsWriteMetadataJson=true", true)] - public async Task Build_ScansReferences(string parameters, bool metadataGenerated) + public InnerBuildTests(ITestOutputHelper testOutputHelper) { - await _builder.RestoreAsync(); - await _builder.BuildAsync(parameters, restore: false); + _testOutputHelper = testOutputHelper; + } + + [Fact] + public async Task Build_ScansReferences() + { + string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Build_ScansReferences)); + string projectFileDirectory = Path.Combine(TestUtility.TestResourcesProjectsRoot, "FunctionApp01", "FunctionApp01.csproj"); + + await TestUtility.RestoreAndBuildProjectAsync(projectFileDirectory, outputDir, null, _testOutputHelper); // Verify extensions.json contents - string extensionsJsonPath = Path.Combine(_builder.OutputPath, "extensions.json"); + string extensionsJsonPath = Path.Combine(outputDir, "extensions.json"); Assert.True(File.Exists(extensionsJsonPath)); JToken extensionsJsonContents = JObject.Parse(File.ReadAllText(extensionsJsonPath)); @@ -57,13 +54,8 @@ public async Task Build_ScansReferences(string parameters, bool metadataGenerate Assert.True(JToken.DeepEquals(expectedExtensionsJson, extensionsJsonContents)); // Verify functions.metadata contents - string functionsMetadataPath = Path.Combine(_builder.OutputPath, "functions.metadata"); - Assert.Equal(metadataGenerated, File.Exists(functionsMetadataPath)); - - if (!metadataGenerated) - { - return; - } + string functionsMetadataPath = Path.Combine(outputDir, "functions.metadata"); + Assert.True(File.Exists(functionsMetadataPath)); JToken functionsMetadataContents = JArray.Parse(File.ReadAllText(functionsMetadataPath)); JToken expectedFunctionsMetadata = JArray.Parse(@"[ @@ -123,7 +115,5 @@ public async Task Build_ScansReferences(string parameters, bool metadataGenerate Assert.True(JToken.DeepEquals(expectedFunctionsMetadata, functionsMetadataContents)); } - - public void Dispose() => _builder.Dispose(); } } diff --git a/test/Sdk.E2ETests/ProcessWrapper.cs b/test/Sdk.E2ETests/ProcessWrapper.cs index eb04f284b..ae250e4ae 100644 --- a/test/Sdk.E2ETests/ProcessWrapper.cs +++ b/test/Sdk.E2ETests/ProcessWrapper.cs @@ -6,27 +6,26 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using Xunit.Abstractions; namespace Microsoft.Azure.Functions.Sdk.E2ETests { - public static class ProcessWrapper + public class ProcessWrapper { - public static async Task RunProcessAsync( - string fileName, string arguments, string workingDirectory = null, Action log = null) + + public async Task RunProcess(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null) { - return await RunProcessInternalAsync(fileName, arguments, workingDirectory, log); + return await RunProcessInternal(fileName, arguments, workingDirectory, testOutputHelper); } - public static async Task> RunProcessForOutputAsync( - string fileName, string arguments, string workingDirectory = null, Action log = null) + public async Task> RunProcessForOutput(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null) { StringBuilder processOutputStringBuilder = new StringBuilder(); - var exitCode = await RunProcessInternalAsync(fileName, arguments, workingDirectory, log, processOutputStringBuilder); + var exitCode = await RunProcessInternal(fileName, arguments, workingDirectory, testOutputHelper, processOutputStringBuilder); return new Tuple(exitCode, processOutputStringBuilder.ToString()); } - private static async Task RunProcessInternalAsync( - string fileName, string arguments, string workingDirectory = null, Action log = null, StringBuilder processOutputBuilder = null) + private async Task RunProcessInternal(string fileName, string arguments, string workingDirectory, ITestOutputHelper testOutputHelper = null, StringBuilder processOutputBuilder = null) { SemaphoreSlim processExitSemaphore = new SemaphoreSlim(0, 1); @@ -54,8 +53,11 @@ public static class ProcessWrapper { if (o.Data != null) { - log?.Invoke($"[{DateTime.UtcNow:O}] Error: {o.Data}"); - processOutputBuilder?.AppendLine(o.Data); + testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] Error: {o.Data}"); + if (processOutputBuilder != null) + { + processOutputBuilder.AppendLine(o.Data); + } } }; @@ -63,8 +65,11 @@ public static class ProcessWrapper { if (o.Data != null) { - log?.Invoke($"[{DateTime.UtcNow:O}] {o.Data}"); - processOutputBuilder?.AppendLine(o.Data); + testOutputHelper.WriteLine($"[{DateTime.UtcNow:O}] {o.Data}"); + if (processOutputBuilder != null) + { + processOutputBuilder.AppendLine(o.Data); + } } }; @@ -76,7 +81,7 @@ public static class ProcessWrapper int wait = 3 * 60 * 1000; if (!await processExitSemaphore.WaitAsync(wait)) { - log?.Invoke($"Process '{testProcess.Id}' did not exit in {wait}ms."); + testOutputHelper?.WriteLine($"Process '{testProcess.Id}' did not exit in {wait}ms."); testProcess.Kill(); } diff --git a/test/Sdk.E2ETests/ProjectBuilder.cs b/test/Sdk.E2ETests/ProjectBuilder.cs deleted file mode 100644 index 9fff6b893..000000000 --- a/test/Sdk.E2ETests/ProjectBuilder.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Xunit; -using Xunit.Abstractions; - -namespace Microsoft.Azure.Functions.Sdk.E2ETests -{ - public sealed class ProjectBuilder(ITestOutputHelper logger, string project) : IDisposable - { -#if DEBUG - public const string Configuration = "Debug"; -#elif RELEASE - public const string Configuration = "Release"; -#endif - public static readonly string LocalPackages = Path.Combine(TestUtility.PathToRepoRoot, "local"); - public static readonly string SrcRoot = Path.Combine(TestUtility.PathToRepoRoot, "src"); - public static readonly string SdkSolutionRoot = Path.Combine(TestUtility.PathToRepoRoot, "sdk"); - public static readonly string SdkProjectRoot = Path.Combine(SdkSolutionRoot, "Sdk"); - public static readonly string DotNetExecutable = "dotnet"; - public static readonly string SdkVersion = "99.99.99-test"; - public static readonly string SdkBuildProj = Path.Combine(TestUtility.PathToRepoRoot, "build", "Sdk.slnf"); - public static readonly string NuGetOrgPackages = "https://api.nuget.org/v3/index.json"; - - private static Task _initialization; - private static object _sync; - - private readonly TempDirectory _tempDirectory = new(); - - public string OutputPath => _tempDirectory.Path; - - public async Task RestoreAsync() - { - await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync); - logger.WriteLine("Restoring..."); - string dotnetArgs = $"restore {project} -s {NuGetOrgPackages} -s {LocalPackages} -p:SdkVersion={SdkVersion}"; - Stopwatch stopwatch = Stopwatch.StartNew(); - int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)"); - } - - public async Task BuildAsync(string additionalParams = null, bool restore = true) - { - await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync); - - Stopwatch stopwatch = Stopwatch.StartNew(); - logger.WriteLine("Building..."); - string dotnetArgs = $"build {project} -c {Configuration} -o {OutputPath} -p:SdkVersion={SdkVersion} {additionalParams}"; - - if (!restore) - { - dotnetArgs += " --no-restore"; - } - - if (Debugger.IsAttached) - { - dotnetArgs += " -bl"; - } - - int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)"); - } - - public async Task PublishAsync(string additionalParams = null, bool restore = true) - { - await LazyInitializer.EnsureInitialized(ref _initialization, ref _sync, InitializeAsync); - - Stopwatch stopwatch = Stopwatch.StartNew(); - logger.WriteLine($"Publishing..."); - string dotnetArgs = $"publish {project} -c {Configuration} -o {OutputPath} -p:SdkVersion={SdkVersion} {additionalParams}"; - - if (!restore) - { - dotnetArgs += " --no-restore"; - } - - if (Debugger.IsAttached) - { - dotnetArgs += " -bl"; - } - - int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, dotnetArgs, log: logger.WriteLine); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - logger.WriteLine($"Done. ({stopwatch.ElapsedMilliseconds} ms)"); - } - - private async Task InitializeAsync() - { - logger.WriteLine($"Packing {SdkBuildProj} with version {SdkVersion}"); - string arguments = $"pack {SdkBuildProj} -c {Configuration} -o {LocalPackages} -p:Version={SdkVersion}"; - - int? exitCode = await ProcessWrapper.RunProcessAsync(DotNetExecutable, arguments, SrcRoot, logger.WriteLine); - Assert.True(exitCode.HasValue && exitCode.Value == 0); - } - - public void Dispose() => _tempDirectory.Dispose(); - } -} diff --git a/test/Sdk.E2ETests/PublishTests.cs b/test/Sdk.E2ETests/PublishTests.cs index 9f3cb6544..d707922e2 100644 --- a/test/Sdk.E2ETests/PublishTests.cs +++ b/test/Sdk.E2ETests/PublishTests.cs @@ -11,20 +11,27 @@ namespace Microsoft.Azure.Functions.Sdk.E2ETests { - public sealed class PublishTests(ITestOutputHelper testOutputHelper) : IDisposable + public class PublishTests { - private readonly ProjectBuilder _builder = new( - testOutputHelper, - Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj")); - - [Theory] - [InlineData("", false)] - [InlineData("-r win-x86", false)] - [InlineData("-p:FunctionsEnableWorkerIndexing=false", true)] - [InlineData("-p:FunctionsEnableWorkerIndexing=false -r win-x86", true)] - public async Task Publish(string parameters, bool metadataGenerated) + private ITestOutputHelper _testOutputHelper; + + public PublishTests(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + + [Fact] + public async Task Publish() + { + string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Publish)); + await RunPublishTest(outputDir); + } + + [Fact] + public async Task Publish_Rid() { - await RunPublishTest(parameters, metadataGenerated); + string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Publish_Rid)); + await RunPublishTest(outputDir, "-r win-x86"); } [Fact] @@ -34,51 +41,45 @@ public async Task Publish(string parameters, bool metadataGenerated) [Trait("Requirement", "Docker")] public async Task Publish_Container() { - var repository = "sdk." + nameof(E2ETests).ToLower(); + string outputDir = await TestUtility.InitializeTestAsync(_testOutputHelper, nameof(Publish_Container)); + var repository = nameof(Sdk.E2ETests).ToLower(); var imageTag = nameof(Publish_Container); // setup test environment state in case there is leftover data from previous runs - await TestUtility.RemoveDockerTestImage(repository, imageTag, testOutputHelper); + await TestUtility.RemoveDockerTestImage(repository, imageTag, _testOutputHelper); - try - { - // perform the publish - await RunPublishTest($"-t:PublishContainer -p:ContainerRepository={repository} -p:ContainerImageTag={imageTag}", false); - - // validate the image base - Tuple inspectResults = await ProcessWrapper.RunProcessForOutputAsync( - "docker", - $"inspect {repository}:{imageTag} --format \"{{{{ index .Config.Labels \\\"org.opencontainers.image.base.name\\\"}}}}\"", - _builder.OutputPath, - testOutputHelper.WriteLine); - - var inspectExitCode = inspectResults.Item1; - var inspectOutput = inspectResults.Item2; - Assert.True(inspectExitCode.HasValue && inspectExitCode.Value == 0); - Assert.Matches("mcr\\.microsoft\\.com/azure-functions/dotnet-isolated:(\\d)+-dotnet-isolated(\\d+\\.\\d+)", inspectOutput); - } - finally - { - // clean up - await TestUtility.RemoveDockerTestImage(repository, imageTag, testOutputHelper); - } + // perform the publish + await RunPublishTest(outputDir, $"--no-restore /t:PublishContainer --property:ContainerRepository={repository} --property:ContainerImageTag={imageTag}"); + + // validate the image base + Tuple inspectResults = await new ProcessWrapper().RunProcessForOutput("docker", $"inspect {repository}:{imageTag} --format \"{{{{ index .Config.Labels \\\"org.opencontainers.image.base.name\\\"}}}}\"", outputDir, _testOutputHelper); + var inspectExitCode = inspectResults.Item1; + var inspectOutput = inspectResults.Item2; + Assert.True(inspectExitCode.HasValue && inspectExitCode.Value == 0); + Assert.Matches("mcr\\.microsoft\\.com/azure-functions/dotnet-isolated:(\\d)+-dotnet-isolated(\\d+\\.\\d+)", inspectOutput); + + // clean up + await TestUtility.RemoveDockerTestImage(repository, imageTag, _testOutputHelper); } - private async Task RunPublishTest(string additionalParams, bool metadataGenerated) + private async Task RunPublishTest(string outputDir, string additionalParams = null) { - await _builder.PublishAsync(additionalParams); + // Name of the csproj + string projectFileDirectory = Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj"); + + await TestUtility.RestoreAndPublishProjectAsync(projectFileDirectory, outputDir, additionalParams, _testOutputHelper); // Make sure files are in /.azurefunctions - string azureFunctionsDir = Path.Combine(_builder.OutputPath, ".azurefunctions"); + string azureFunctionsDir = Path.Combine(outputDir, ".azurefunctions"); Assert.True(Directory.Exists(azureFunctionsDir)); // Verify files are present string metadataLoaderPath = Path.Combine(azureFunctionsDir, "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.dll"); - string extensionsJsonPath = Path.Combine(_builder.OutputPath, "extensions.json"); - string functionsMetadataPath = Path.Combine(_builder.OutputPath, "functions.metadata"); - Assert.True(File.Exists(extensionsJsonPath)); + string extensionsJsonPath = Path.Combine(outputDir, "extensions.json"); + string functionsMetadataPath = Path.Combine(outputDir, "functions.metadata"); Assert.True(File.Exists(metadataLoaderPath)); - Assert.Equal(metadataGenerated, File.Exists(functionsMetadataPath)); + Assert.True(File.Exists(extensionsJsonPath)); + Assert.True(File.Exists(functionsMetadataPath)); // Verify extensions.json JObject jObjects = JObject.Parse(File.ReadAllText(extensionsJsonPath)); @@ -101,24 +102,26 @@ private async Task RunPublishTest(string additionalParams, bool metadataGenerate Assert.True(JToken.DeepEquals(extensionsJsonContents, expected), $"Actual: {extensionsJsonContents}{Environment.NewLine}Expected: {expected}"); // Verify functions.metadata - if (metadataGenerated) - { - TestUtility.ValidateFunctionsMetadata(functionsMetadataPath, "Microsoft.Azure.Functions.Sdk.E2ETests.Contents.functions.metadata"); - } + TestUtility.ValidateFunctionsMetadata(functionsMetadataPath, "Microsoft.Azure.Functions.Sdk.E2ETests.Contents.functions.metadata"); } - public void Dispose() => _builder.Dispose(); - - private class Extension(string name, string typeName, string hintPath) + private class Extension { + public Extension(string name, string typeName, string hintPath) + { + Name = name; + TypeName = typeName; + HintPath = hintPath; + } + [JsonProperty("name")] - public string Name { get; set; } = name; + public string Name { get; set; } [JsonProperty("typeName")] - public string TypeName { get; set; } = typeName; + public string TypeName { get; set; } [JsonProperty("hintPath")] - public string HintPath { get; set; } = hintPath; + public string HintPath { get; set; } } } } diff --git a/test/Sdk.E2ETests/TempDirectory.cs b/test/Sdk.E2ETests/TempDirectory.cs deleted file mode 100644 index 8afa936f8..000000000 --- a/test/Sdk.E2ETests/TempDirectory.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; -using System.IO; -using IOPath = System.IO.Path; - -namespace Microsoft.Azure.Functions.Sdk.E2ETests -{ - public sealed class TempDirectory : IDisposable - { - public TempDirectory() : this(IOPath.Combine(IOPath.GetTempPath(), IOPath.GetRandomFileName())) - { - } - - public TempDirectory(string path) - { - ArgumentNullException.ThrowIfNull(path); - Path = path; - - if (!Directory.Exists(Path)) - { - Directory.CreateDirectory(Path); - } - } - - public string Path { get; } - - public void Dispose() - { - try - { - Directory.Delete(Path, true); - } - catch (IOException) - { - // Ignore IO exceptions during cleanup - } - } - } -} diff --git a/test/Sdk.E2ETests/TestUtility.cs b/test/Sdk.E2ETests/TestUtility.cs index 26f0b6a98..b1217ad3f 100644 --- a/test/Sdk.E2ETests/TestUtility.cs +++ b/test/Sdk.E2ETests/TestUtility.cs @@ -26,29 +26,120 @@ public static class TestUtility public const string Net50 = "net5.0"; // Paths and executables - public static readonly string PathToRepoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, @"../../../../../")); + public static readonly string DotNetExecutable = "dotnet"; + public static readonly string PathToRepoRoot = Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "../../../../../")); + public static readonly string SrcRoot = Path.Combine(PathToRepoRoot, "src"); + public static readonly string SdkSolutionRoot = Path.Combine(PathToRepoRoot, "sdk"); + public static readonly string SdkProjectRoot = Path.Combine(SdkSolutionRoot, "Sdk"); public static readonly string TestRoot = Path.Combine(PathToRepoRoot, "test"); public static readonly string SamplesRoot = Path.Combine(PathToRepoRoot, "samples"); + public static readonly string LocalPackages = Path.Combine(PathToRepoRoot, "local"); + public static readonly string TestOutputDir = Path.Combine(Path.GetTempPath(), "FunctionsWorkerSdk.E2ETests"); public static readonly string TestResourcesProjectsRoot = Path.Combine(TestRoot, "Resources", "Projects"); + public static readonly string NuGetOrgPackages = "https://api.nuget.org/v3/index.json"; + public static readonly string NuGetPackageSource = LocalPackages; + public static readonly string SdkVersion = "99.99.99-test"; + public static readonly string SdkBuildProj = Path.Combine(PathToRepoRoot, "build", "Sdk.slnf"); + + private static bool _isInitialized = false; + + public static async Task InitializeTestAsync(ITestOutputHelper testOutputHelper, string testName) + { + if (!_isInitialized) + { + testOutputHelper.WriteLine($"Packing {SdkBuildProj} with version {SdkVersion}"); + string arguments = $"pack {SdkBuildProj} -c {Configuration} -o {LocalPackages} -p:Version={SdkVersion}"; + + int? exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, arguments, SrcRoot, testOutputHelper); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + + _isInitialized = true; + } + + return InitializeOutputDir(testName); + } + public static void ValidateFunctionsMetadata(string actualFilePath, string embeddedResourceName) { JToken functionsMetadataContents = JToken.Parse(File.ReadAllText(actualFilePath)); var assembly = Assembly.GetExecutingAssembly(); string resourceName = assembly.GetManifestResourceNames() .Single(str => str.EndsWith(embeddedResourceName)); - using Stream stream = assembly.GetManifestResourceStream(resourceName); - using StreamReader reader = new StreamReader(stream); - using var jsonReader = new JsonTextReader(reader); - JsonSerializer serializer = new JsonSerializer(); - var expected = serializer.Deserialize(jsonReader); - Assert.True(JToken.DeepEquals(functionsMetadataContents, expected), $"Actual: {functionsMetadataContents}{Environment.NewLine}Expected: {expected}"); + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + { + using (StreamReader reader = new StreamReader(stream)) + { + using (var jsonReader = new JsonTextReader(reader)) + { + JsonSerializer serializer = new JsonSerializer(); + var expected = serializer.Deserialize(jsonReader); + Assert.True(JToken.DeepEquals(functionsMetadataContents, expected), $"Actual: {functionsMetadataContents}{Environment.NewLine}Expected: {expected}"); + } + } + } + } + + public static async Task RestoreAndBuildProjectAsync(string fullPathToProjFile, string outputDir, string additionalParams, ITestOutputHelper outputHelper) + { + // Name of the csproj + string projectNameToTest = Path.GetFileName(fullPathToProjFile); + string projectFileDirectory = Path.GetDirectoryName(fullPathToProjFile); + + // Restore + outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Restoring..."); + string dotnetArgs = $"restore {projectNameToTest} -s {NuGetOrgPackages} -s {LocalPackages} -p:SdkVersion={SdkVersion}"; + int? exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done."); + + // Build + outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Building..."); + dotnetArgs = $"build {projectNameToTest} --configuration {Configuration} -o {outputDir} -p:SdkVersion={SdkVersion} {additionalParams}"; + exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done."); + } + + public static async Task RestoreAndPublishProjectAsync(string fullPathToProjFile, string outputDir, string additionalParams, ITestOutputHelper outputHelper) + { + // Name of the csproj + string projectNameToTest = Path.GetFileName(fullPathToProjFile); + string projectFileDirectory = Path.GetDirectoryName(fullPathToProjFile); + + // Restore + outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Restoring..."); + string dotnetArgs = $"restore {projectNameToTest} -s {NuGetOrgPackages} -s {LocalPackages} -p:SdkVersion={SdkVersion}"; + int? exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done."); + + // Publish + outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Publishing..."); + dotnetArgs = $"publish {projectNameToTest} --configuration {Configuration} -o {outputDir} -p:SdkVersion={SdkVersion} {additionalParams}"; + exitCode = await new ProcessWrapper().RunProcess(DotNetExecutable, dotnetArgs, projectFileDirectory, testOutputHelper: outputHelper); + Assert.True(exitCode.HasValue && exitCode.Value == 0); + outputHelper.WriteLine($"[{DateTime.UtcNow:O}] Done."); + } + + private static string InitializeOutputDir(string testName) + { + string outputDir = Path.Combine(TestOutputDir, testName); + + if (Directory.Exists(outputDir)) + { + Directory.Delete(outputDir, recursive: true); + } + + Directory.CreateDirectory(outputDir); + + return outputDir; } public static async Task RemoveDockerTestImage(string repository, string imageTag, ITestOutputHelper outputHelper) { outputHelper.WriteLine($"Removing image {repository}:{imageTag} from local registry"); - int? rmiExitCode = await ProcessWrapper.RunProcessAsync("docker", $"rmi -f {repository}:{imageTag}", log: outputHelper.WriteLine); + int? rmiExitCode = await new ProcessWrapper().RunProcess("docker", $"rmi -f {repository}:{imageTag}", TestOutputDir, outputHelper); Assert.True(rmiExitCode.HasValue && rmiExitCode.Value == 0); // daemon may still error if the image doesn't exist, but it will still return 0 } } diff --git a/test/Sdk.E2ETests/ZipDeployTests.cs b/test/Sdk.E2ETests/ZipDeployTests.cs index b683a5041..5d8c522a2 100644 --- a/test/Sdk.E2ETests/ZipDeployTests.cs +++ b/test/Sdk.E2ETests/ZipDeployTests.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; using System.Threading.Tasks; using ICSharpCode.SharpZipLib.Zip; using Microsoft.NET.Sdk.Functions.MSBuild.Tasks; @@ -8,11 +9,14 @@ namespace Microsoft.Azure.Functions.Sdk.E2ETests { - public sealed class ZipDeployTests(ITestOutputHelper testOutputHelper) : IDisposable + public class ZipDeployTests { - private readonly ProjectBuilder _builder = new( - testOutputHelper, - Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj")); + private ITestOutputHelper _testOutputHelper; + + public ZipDeployTests(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } [Theory] [InlineData("linux-x64", true)] @@ -21,7 +25,9 @@ public sealed class ZipDeployTests(ITestOutputHelper testOutputHelper) : IDispos public async Task CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContained(string rid, bool selfContained) { string testName = nameof(CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContained); - string zipName = Path.Combine(Directory.GetParent(_builder.OutputPath).FullName, $"{testName}.zip"); + string directoryToZip = await TestUtility.InitializeTestAsync(_testOutputHelper, testName); + + string zipName = Path.Combine(Directory.GetParent(directoryToZip).FullName, $"{testName}.zip"); if (File.Exists(zipName)) { @@ -30,13 +36,13 @@ public async Task CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContaine string projectFileDirectory = Path.Combine(TestUtility.SamplesRoot, "FunctionApp", "FunctionApp.csproj"); - await _builder.PublishAsync($"-r {rid} --self-contained {selfContained}"); + await TestUtility.RestoreAndPublishProjectAsync( + projectFileDirectory, directoryToZip, $"-r {rid} --self-contained {selfContained}", _testOutputHelper); - CreateZipFileTask.CreateZipFileFromDirectory(_builder.OutputPath, zipName); + CreateZipFileTask.CreateZipFileFromDirectory(directoryToZip, zipName); using var zip = new ZipFile(zipName); - Assert.Equal(Directory.GetFiles(_builder.OutputPath, "*", SearchOption.AllDirectories).Length, zip.Count); - + Assert.Equal(Directory.GetFiles(directoryToZip, "*", SearchOption.AllDirectories).Length, zip.Count); foreach (ZipEntry entry in zip) { if (selfContained && (entry.Name == "FunctionApp" || entry.Name == "FunctionApp.exe")) @@ -58,10 +64,6 @@ public async Task CreateZipFileFromDirectory_SetsExecutableFlag_WhenSelfContaine Assert.NotEqual(0, entry.ExternalFileAttributes); } } - - zip.Close(); } - - public void Dispose() => _builder.Dispose(); } } From 81b2eb1c25a0cf2ac1d5e0ae3e2b72529968aca2 Mon Sep 17 00:00:00 2001 From: Shyju Krishnankutty Date: Wed, 12 Nov 2025 16:54:24 -0800 Subject: [PATCH 45/45] worker SDK 2.0.7 release prep (#3247) * 2.0.7 worker SDK release prep. * revert release note change --- sdk/Sdk/Sdk.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/Sdk/Sdk.csproj b/sdk/Sdk/Sdk.csproj index 9b3e90b49..1da879421 100644 --- a/sdk/Sdk/Sdk.csproj +++ b/sdk/Sdk/Sdk.csproj @@ -3,7 +3,7 @@ 2 0 - 6 + 7 netstandard2.0;net472 Microsoft.Azure.Functions.Worker.Sdk This package provides development time support for the Azure Functions .NET Worker.