diff --git a/NuGet.config b/NuGet.config index 06e49041de2f..f9ca5d18191c 100644 --- a/NuGet.config +++ b/NuGet.config @@ -4,10 +4,10 @@ - + - + @@ -27,10 +27,10 @@ - + - + diff --git a/eng/Baseline.Designer.props b/eng/Baseline.Designer.props index 7bbef3953446..67ffbf7b4c5d 100644 --- a/eng/Baseline.Designer.props +++ b/eng/Baseline.Designer.props @@ -2,28 +2,28 @@ $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - - - + + + @@ -34,120 +34,120 @@ - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 @@ -155,114 +155,114 @@ - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - - - + + + - + - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - - + + @@ -270,7 +270,7 @@ - 6.0.4 + 6.0.5 @@ -278,50 +278,50 @@ - 6.0.4 + 6.0.5 - + - + - - + + - + - + - - + + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - - + + @@ -331,8 +331,8 @@ - - + + @@ -340,8 +340,8 @@ - - + + @@ -352,58 +352,58 @@ - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 @@ -411,71 +411,71 @@ - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - + - + - + - + - 6.0.4 + 6.0.5 - - + + - + - - + + - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 @@ -491,195 +491,195 @@ - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - - + + - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - - + + - - + + - - + + - 6.0.4 + 6.0.5 - - + + - - + + - - + + - - + + - 6.0.4 + 6.0.5 - + - + - + - + - + - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - - - - + + + + - 6.0.4 + 6.0.5 @@ -688,69 +688,69 @@ - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 @@ -769,7 +769,7 @@ - 6.0.4 + 6.0.5 @@ -788,7 +788,7 @@ - 6.0.4 + 6.0.5 @@ -804,46 +804,46 @@ - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - - - + + + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 @@ -853,7 +853,7 @@ - 6.0.4 + 6.0.5 @@ -862,73 +862,73 @@ - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - + - + - + - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 @@ -957,11 +957,11 @@ - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 @@ -979,13 +979,13 @@ - 6.0.4 + 6.0.5 - 6.0.4 + 6.0.5 - + \ No newline at end of file diff --git a/eng/Baseline.xml b/eng/Baseline.xml index e20bb4bb51a8..32b7ff01b908 100644 --- a/eng/Baseline.xml +++ b/eng/Baseline.xml @@ -4,111 +4,111 @@ This file contains a list of all the packages and their versions which were rele Update this list when preparing for a new patch. --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/eng/Dependencies.props b/eng/Dependencies.props index 3c179c39aecb..75c41177ffa6 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -69,10 +69,12 @@ and are generated based on the last package release. + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 67ad201ccdf6..6f37175f3417 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -9,37 +9,37 @@ --> - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 9b03633bb18b617088b32260065ee385bf9c4491 + 796d2f8b516781d64bbdf4c21b0163de9fa56ab8 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 9b03633bb18b617088b32260065ee385bf9c4491 + 796d2f8b516781d64bbdf4c21b0163de9fa56ab8 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 9b03633bb18b617088b32260065ee385bf9c4491 + 796d2f8b516781d64bbdf4c21b0163de9fa56ab8 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 9b03633bb18b617088b32260065ee385bf9c4491 + 796d2f8b516781d64bbdf4c21b0163de9fa56ab8 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 9b03633bb18b617088b32260065ee385bf9c4491 + 796d2f8b516781d64bbdf4c21b0163de9fa56ab8 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 9b03633bb18b617088b32260065ee385bf9c4491 + 796d2f8b516781d64bbdf4c21b0163de9fa56ab8 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 9b03633bb18b617088b32260065ee385bf9c4491 + 796d2f8b516781d64bbdf4c21b0163de9fa56ab8 - + https://dev.azure.com/dnceng/internal/_git/dotnet-efcore - 9b03633bb18b617088b32260065ee385bf9c4491 + 796d2f8b516781d64bbdf4c21b0163de9fa56ab8 https://github.com/dotnet/runtime @@ -177,9 +177,9 @@ https://github.com/dotnet/runtime 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 70ae3df4a6f3c92fb6b315afc405edd10ff38579 + 7cca709db2944a09b4db6ca7b20c457ff260fb5a https://github.com/dotnet/runtime @@ -233,9 +233,9 @@ https://github.com/dotnet/runtime 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 70ae3df4a6f3c92fb6b315afc405edd10ff38579 + 7cca709db2944a09b4db6ca7b20c457ff260fb5a https://github.com/dotnet/runtime @@ -245,57 +245,57 @@ https://github.com/dotnet/runtime 4822e3c3aa77eb82b2fb33c9321f923cf11ddde6 - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 70ae3df4a6f3c92fb6b315afc405edd10ff38579 + 7cca709db2944a09b4db6ca7b20c457ff260fb5a - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 70ae3df4a6f3c92fb6b315afc405edd10ff38579 + 7cca709db2944a09b4db6ca7b20c457ff260fb5a - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 70ae3df4a6f3c92fb6b315afc405edd10ff38579 + 7cca709db2944a09b4db6ca7b20c457ff260fb5a - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 70ae3df4a6f3c92fb6b315afc405edd10ff38579 + 7cca709db2944a09b4db6ca7b20c457ff260fb5a - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 70ae3df4a6f3c92fb6b315afc405edd10ff38579 + 7cca709db2944a09b4db6ca7b20c457ff260fb5a - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - 70ae3df4a6f3c92fb6b315afc405edd10ff38579 + 7cca709db2944a09b4db6ca7b20c457ff260fb5a - + https://dev.azure.com/dnceng/internal/_git/dotnet-runtime - be98e88c760526452df94ef452fff4602fb5bded + 7cca709db2944a09b4db6ca7b20c457ff260fb5a - + https://github.com/dotnet/arcade - 1a6b24397e50146d0fece9cfb9c0b87275691e6f + e3cbecc5b0e51374e3d71dbb976004ab9cc90430 - + https://github.com/dotnet/arcade - 1a6b24397e50146d0fece9cfb9c0b87275691e6f + e3cbecc5b0e51374e3d71dbb976004ab9cc90430 - + https://github.com/dotnet/arcade - 1a6b24397e50146d0fece9cfb9c0b87275691e6f + e3cbecc5b0e51374e3d71dbb976004ab9cc90430 - + https://github.com/dotnet/arcade - 1a6b24397e50146d0fece9cfb9c0b87275691e6f + e3cbecc5b0e51374e3d71dbb976004ab9cc90430 diff --git a/eng/Versions.props b/eng/Versions.props index 2ba62764d9b6..3ee5a0799a42 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -8,7 +8,7 @@ 6 0 - 5 + 6 true 6.0.0 - 6.0.5 - 6.0.5 - 6.0.5 - 6.0.5 - 6.0.5 - 6.0.5-servicing.22213.9 + 6.0.6 + 6.0.6 + 6.0.6 + 6.0.6 + 6.0.6 + 6.0.6-servicing.22267.7 6.0.0 6.0.1 6.0.0 @@ -103,7 +103,7 @@ 6.0.0 6.0.0 6.0.0 - 6.0.5-servicing.22213.9 + 6.0.6-servicing.22267.7 6.0.0 6.0.0 6.0.1 @@ -117,22 +117,22 @@ 6.0.0 6.0.0 6.0.0 - 6.0.4 + 6.0.5 6.0.0 - 6.0.3 + 6.0.4 - 6.0.5 - 6.0.5 - 6.0.5 - 6.0.5 - 6.0.5 - 6.0.5 - 6.0.5 - 6.0.5 + 6.0.6 + 6.0.6 + 6.0.6 + 6.0.6 + 6.0.6 + 6.0.6 + 6.0.6 + 6.0.6 - 6.0.0-beta.22212.5 - 6.0.0-beta.22212.5 + 6.0.0-beta.22261.7 + 6.0.0-beta.22261.7 2.1.1 2.2.0 - 3.1.24-servicing-22180-6 + 3.1.25-servicing-22219-10 $(MicrosoftAspNetCoreAzureAppServicesSiteExtension31Version) $(MicrosoftAspNetCoreAzureAppServicesSiteExtension31Version) - 5.0.16-servicing-22167-6 + 5.0.17-servicing-22215-7 $(MicrosoftAspNetCoreAzureAppServicesSiteExtension50Version) $(MicrosoftAspNetCoreAzureAppServicesSiteExtension50Version) diff --git a/eng/helix/content/RunTests/ProcessUtil.cs b/eng/helix/content/RunTests/ProcessUtil.cs index 416ec05a9087..4437e9b40b34 100644 --- a/eng/helix/content/RunTests/ProcessUtil.cs +++ b/eng/helix/content/RunTests/ProcessUtil.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Text; @@ -80,7 +81,7 @@ public static async Task RunAsync( Action? onStart = null, CancellationToken cancellationToken = default) { - Console.WriteLine($"Running '{filename} {arguments}'"); + PrintMessage($"Running '{filename} {arguments}'"); using var process = new Process() { StartInfo = @@ -153,7 +154,7 @@ public static async Task RunAsync( process.Exited += (_, e) => { - Console.WriteLine($"'{process.StartInfo.FileName} {process.StartInfo.Arguments}' completed with exit code '{process.ExitCode}'"); + PrintMessage($"'{process.StartInfo.FileName} {process.StartInfo.Arguments}' completed with exit code '{process.ExitCode}'"); if (throwOnError && process.ExitCode != 0) { processLifetimeTask.TrySetException(new InvalidOperationException($"Command {filename} {arguments} returned exit code {process.ExitCode} output: {outputBuilder.ToString()}")); @@ -208,5 +209,8 @@ public static async Task RunAsync( return await processLifetimeTask.Task; } + + public static void PrintMessage(string message) => Console.WriteLine($"{DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)} {message}"); + public static void PrintErrorMessage(string message) => Console.Error.WriteLine($"{DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)} {message}"); } } diff --git a/eng/helix/content/RunTests/Program.cs b/eng/helix/content/RunTests/Program.cs index 7247c80c364d..e3cbbfbe3a44 100644 --- a/eng/helix/content/RunTests/Program.cs +++ b/eng/helix/content/RunTests/Program.cs @@ -25,7 +25,7 @@ static async Task Main(string[] args) keepGoing = await runner.InstallPlaywrightAsync(); } #else - Console.WriteLine("Playwright install skipped."); + ProcessUtil.PrintMessage("Playwright install skipped."); #endif runner.DisplayContents(); @@ -34,23 +34,23 @@ static async Task Main(string[] args) { if (!await runner.CheckTestDiscoveryAsync()) { - Console.WriteLine("RunTest stopping due to test discovery failure."); + ProcessUtil.PrintMessage("RunTest stopping due to test discovery failure."); Environment.Exit(1); return; } var exitCode = await runner.RunTestsAsync(); runner.UploadResults(); - Console.WriteLine($"Completed Helix job with exit code '{exitCode}'"); + ProcessUtil.PrintMessage($"Completed Helix job with exit code '{exitCode}'"); Environment.Exit(exitCode); } - Console.WriteLine("Tests were not run due to previous failures. Exit code=1"); + ProcessUtil.PrintMessage("Tests were not run due to previous failures. Exit code=1"); Environment.Exit(1); } catch (Exception e) { - Console.WriteLine($"RunTests uncaught exception: {e.ToString()}"); + ProcessUtil.PrintMessage($"RunTests uncaught exception: {e.ToString()}"); Environment.Exit(1); } } diff --git a/eng/helix/content/RunTests/TestRunner.cs b/eng/helix/content/RunTests/TestRunner.cs index f08fac5fc260..d951ea512f93 100644 --- a/eng/helix/content/RunTests/TestRunner.cs +++ b/eng/helix/content/RunTests/TestRunner.cs @@ -33,36 +33,36 @@ public bool SetupEnvironment() EnvironmentVariables.Add("PATH", Options.Path); EnvironmentVariables.Add("helix", Options.HelixQueue); - Console.WriteLine($"Current Directory: {Options.HELIX_WORKITEM_ROOT}"); + ProcessUtil.PrintMessage($"Current Directory: {Options.HELIX_WORKITEM_ROOT}"); var helixDir = Options.HELIX_WORKITEM_ROOT; - Console.WriteLine($"Setting HELIX_DIR: {helixDir}"); + ProcessUtil.PrintMessage($"Setting HELIX_DIR: {helixDir}"); EnvironmentVariables.Add("HELIX_DIR", helixDir); EnvironmentVariables.Add("NUGET_FALLBACK_PACKAGES", helixDir); var nugetRestore = Path.Combine(helixDir, "nugetRestore"); EnvironmentVariables.Add("NUGET_RESTORE", nugetRestore); var dotnetEFFullPath = Path.Combine(nugetRestore, helixDir, "dotnet-ef.exe"); - Console.WriteLine($"Set DotNetEfFullPath: {dotnetEFFullPath}"); + ProcessUtil.PrintMessage($"Set DotNetEfFullPath: {dotnetEFFullPath}"); EnvironmentVariables.Add("DotNetEfFullPath", dotnetEFFullPath); var appRuntimePath = $"{Options.DotnetRoot}/shared/Microsoft.AspNetCore.App/{Options.RuntimeVersion}"; - Console.WriteLine($"Set ASPNET_RUNTIME_PATH: {appRuntimePath}"); + ProcessUtil.PrintMessage($"Set ASPNET_RUNTIME_PATH: {appRuntimePath}"); EnvironmentVariables.Add("ASPNET_RUNTIME_PATH", appRuntimePath); var dumpPath = Environment.GetEnvironmentVariable("HELIX_DUMP_FOLDER"); - Console.WriteLine($"Set VSTEST_DUMP_PATH: {dumpPath}"); + ProcessUtil.PrintMessage($"Set VSTEST_DUMP_PATH: {dumpPath}"); EnvironmentVariables.Add("VSTEST_DUMP_PATH", dumpPath); #if INSTALLPLAYWRIGHT // Playwright will download and look for browsers to this directory var playwrightBrowsers = Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH"); - Console.WriteLine($"Setting PLAYWRIGHT_BROWSERS_PATH: {playwrightBrowsers}"); + ProcessUtil.PrintMessage($"Setting PLAYWRIGHT_BROWSERS_PATH: {playwrightBrowsers}"); EnvironmentVariables.Add("PLAYWRIGHT_BROWSERS_PATH", playwrightBrowsers); var playrightDriver = Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_PATH"); - Console.WriteLine($"Setting PLAYWRIGHT_DRIVER_PATH: {playrightDriver}"); + ProcessUtil.PrintMessage($"Setting PLAYWRIGHT_DRIVER_PATH: {playrightDriver}"); EnvironmentVariables.Add("PLAYWRIGHT_DRIVER_PATH", playrightDriver); #else - Console.WriteLine($"Skipping setting PLAYWRIGHT_BROWSERS_PATH"); + ProcessUtil.PrintMessage($"Skipping setting PLAYWRIGHT_BROWSERS_PATH"); #endif - Console.WriteLine($"Creating nuget restore directory: {nugetRestore}"); + ProcessUtil.PrintMessage($"Creating nuget restore directory: {nugetRestore}"); Directory.CreateDirectory(nugetRestore); // Rename default.runner.json to xunit.runner.json if there is not a custom one from the project @@ -80,7 +80,7 @@ public bool SetupEnvironment() } catch (Exception e) { - Console.WriteLine($"Exception in SetupEnvironment: {e.ToString()}"); + ProcessUtil.PrintMessage($"Exception in SetupEnvironment: {e.ToString()}"); return false; } } @@ -90,20 +90,20 @@ public void DisplayContents(string path = "./") try { Console.WriteLine(); - Console.WriteLine($"Displaying directory contents for {path}:"); + ProcessUtil.PrintMessage($"Displaying directory contents for {path}:"); foreach (var file in Directory.EnumerateFiles(path)) { - Console.WriteLine(Path.GetFileName(file)); + ProcessUtil.PrintMessage(Path.GetFileName(file)); } foreach (var file in Directory.EnumerateDirectories(path)) { - Console.WriteLine(Path.GetFileName(file)); + ProcessUtil.PrintMessage(Path.GetFileName(file)); } Console.WriteLine(); } catch (Exception e) { - Console.WriteLine($"Exception in DisplayContents: {e.ToString()}"); + ProcessUtil.PrintMessage($"Exception in DisplayContents: {e.ToString()}"); } } @@ -112,14 +112,14 @@ public async Task InstallPlaywrightAsync() { try { - Console.WriteLine($"Installing Playwright to Browsers: {Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH")} Driver: {Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_PATH")}"); + ProcessUtil.PrintMessage($"Installing Playwright to Browsers: {Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH")} Driver: {Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_PATH")}"); await Playwright.InstallAsync(Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH"), Environment.GetEnvironmentVariable("PLAYWRIGHT_DRIVER_PATH")); DisplayContents(Environment.GetEnvironmentVariable("PLAYWRIGHT_BROWSERS_PATH")); return true; } catch (Exception e) { - Console.WriteLine($"Exception installing playwright: {e.ToString()}"); + ProcessUtil.PrintMessage($"Exception installing playwright: {e.ToString()}"); return false; } } @@ -133,18 +133,18 @@ public async Task InstallDotnetToolsAsync() await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", $"tool install dotnet-dump --tool-path {Options.HELIX_WORKITEM_ROOT} --version 5.0.0-*", environmentVariables: EnvironmentVariables, - outputDataReceived: Console.WriteLine, - errorDataReceived: Console.Error.WriteLine, + outputDataReceived: ProcessUtil.PrintMessage, + errorDataReceived: ProcessUtil.PrintErrorMessage, throwOnError: false, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); - Console.WriteLine($"Adding current directory to nuget sources: {Options.HELIX_WORKITEM_ROOT}"); + ProcessUtil.PrintMessage($"Adding current directory to nuget sources: {Options.HELIX_WORKITEM_ROOT}"); await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", $"nuget add source {Options.HELIX_WORKITEM_ROOT} --configfile NuGet.config", environmentVariables: EnvironmentVariables, - outputDataReceived: Console.WriteLine, - errorDataReceived: Console.Error.WriteLine, + outputDataReceived: ProcessUtil.PrintMessage, + errorDataReceived: ProcessUtil.PrintErrorMessage, throwOnError: false, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); @@ -152,24 +152,24 @@ await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", "nuget list source", environmentVariables: EnvironmentVariables, - outputDataReceived: Console.WriteLine, - errorDataReceived: Console.Error.WriteLine, + outputDataReceived: ProcessUtil.PrintMessage, + errorDataReceived: ProcessUtil.PrintErrorMessage, throwOnError: false, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", $"tool install dotnet-ef --version {Options.EfVersion} --tool-path {Options.HELIX_WORKITEM_ROOT}", environmentVariables: EnvironmentVariables, - outputDataReceived: Console.WriteLine, - errorDataReceived: Console.Error.WriteLine, + outputDataReceived: ProcessUtil.PrintMessage, + errorDataReceived: ProcessUtil.PrintErrorMessage, throwOnError: false, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", $"tool install dotnet-serve --tool-path {Options.HELIX_WORKITEM_ROOT}", environmentVariables: EnvironmentVariables, - outputDataReceived: Console.WriteLine, - errorDataReceived: Console.Error.WriteLine, + outputDataReceived: ProcessUtil.PrintMessage, + errorDataReceived: ProcessUtil.PrintErrorMessage, throwOnError: false, cancellationToken: new CancellationTokenSource(TimeSpan.FromMinutes(2)).Token); @@ -177,7 +177,7 @@ await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", } catch (Exception e) { - Console.WriteLine($"Exception in InstallDotnetTools: {e}"); + ProcessUtil.PrintMessage($"Exception in InstallDotnetTools: {e}"); return false; } } @@ -194,15 +194,15 @@ public async Task CheckTestDiscoveryAsync() if (discoveryResult.StandardOutput.Contains("Exception thrown")) { - Console.WriteLine("Exception thrown during test discovery."); - Console.WriteLine(discoveryResult.StandardOutput); + ProcessUtil.PrintMessage("Exception thrown during test discovery."); + ProcessUtil.PrintMessage(discoveryResult.StandardOutput); return false; } return true; } catch (Exception e) { - Console.WriteLine($"Exception in CheckTestDiscovery: {e.ToString()}"); + ProcessUtil.PrintMessage($"Exception in CheckTestDiscovery: {e.ToString()}"); return false; } } @@ -213,50 +213,60 @@ public async Task RunTestsAsync() try { // Timeout test run 5 minutes before the Helix job would timeout - var cts = new CancellationTokenSource(Options.Timeout.Subtract(TimeSpan.FromMinutes(5))); + var testProcessTimeout = Options.Timeout.Subtract(TimeSpan.FromMinutes(5)); + var cts = new CancellationTokenSource(testProcessTimeout); var diagLog = Path.Combine(Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"), "vstest.log"); - var commonTestArgs = $"test {Options.Target} --diag:{diagLog} --logger:xunit --logger:\"console;verbosity=normal\" --blame \"CollectHangDump;TestTimeout=15m\""; + var commonTestArgs = $"test {Options.Target} --diag:{diagLog} --logger xunit --logger \"console;verbosity=normal\" " + + "--blame-crash --blame-hang-timeout 15m"; if (Options.Quarantined) { - Console.WriteLine("Running quarantined tests."); + ProcessUtil.PrintMessage("Running quarantined tests."); // Filter syntax: https://github.com/Microsoft/vstest-docs/blob/master/docs/filter.md var result = await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", commonTestArgs + " --TestCaseFilter:\"Quarantined=true\"", environmentVariables: EnvironmentVariables, - outputDataReceived: Console.WriteLine, - errorDataReceived: Console.Error.WriteLine, + outputDataReceived: ProcessUtil.PrintMessage, + errorDataReceived: ProcessUtil.PrintErrorMessage, throwOnError: false, cancellationToken: cts.Token); + if (cts.Token.IsCancellationRequested) + { + ProcessUtil.PrintMessage($"Quarantined tests exceeded configured timeout: {testProcessTimeout.TotalMinutes}m."); + } if (result.ExitCode != 0) { - Console.WriteLine($"Failure in quarantined tests. Exit code: {result.ExitCode}."); + ProcessUtil.PrintMessage($"Failure in quarantined tests. Exit code: {result.ExitCode}."); } } else { - Console.WriteLine("Running non-quarantined tests."); + ProcessUtil.PrintMessage("Running non-quarantined tests."); // Filter syntax: https://github.com/Microsoft/vstest-docs/blob/master/docs/filter.md var result = await ProcessUtil.RunAsync($"{Options.DotnetRoot}/dotnet", commonTestArgs + " --TestCaseFilter:\"Quarantined!=true|Quarantined=false\"", environmentVariables: EnvironmentVariables, - outputDataReceived: Console.WriteLine, - errorDataReceived: Console.Error.WriteLine, + outputDataReceived: ProcessUtil.PrintMessage, + errorDataReceived: ProcessUtil.PrintErrorMessage, throwOnError: false, cancellationToken: cts.Token); + if (cts.Token.IsCancellationRequested) + { + ProcessUtil.PrintMessage($"Non-quarantined tests exceeded configured timeout: {testProcessTimeout.TotalMinutes}m."); + } if (result.ExitCode != 0) { - Console.WriteLine($"Failure in non-quarantined tests. Exit code: {result.ExitCode}."); + ProcessUtil.PrintMessage($"Failure in non-quarantined tests. Exit code: {result.ExitCode}."); exitCode = result.ExitCode; } } } catch (Exception e) { - Console.WriteLine($"Exception in RunTests: {e.ToString()}"); + ProcessUtil.PrintMessage($"Exception in RunTests: {e.ToString()}"); exitCode = 1; } return exitCode; @@ -265,51 +275,51 @@ public async Task RunTestsAsync() public void UploadResults() { // 'testResults.xml' is the file Helix looks for when processing test results - Console.WriteLine("Trying to upload results..."); + ProcessUtil.PrintMessage("Trying to upload results..."); if (File.Exists("TestResults/TestResults.xml")) { - Console.WriteLine("Copying TestResults/TestResults.xml to ./testResults.xml"); + ProcessUtil.PrintMessage("Copying TestResults/TestResults.xml to ./testResults.xml"); File.Copy("TestResults/TestResults.xml", "testResults.xml"); } else { - Console.WriteLine("No test results found."); + ProcessUtil.PrintMessage("No test results found."); } var HELIX_WORKITEM_UPLOAD_ROOT = Environment.GetEnvironmentVariable("HELIX_WORKITEM_UPLOAD_ROOT"); if (string.IsNullOrEmpty(HELIX_WORKITEM_UPLOAD_ROOT)) { - Console.WriteLine("No HELIX_WORKITEM_UPLOAD_ROOT specified, skipping log copy"); + ProcessUtil.PrintMessage("No HELIX_WORKITEM_UPLOAD_ROOT specified, skipping log copy"); return; } - Console.WriteLine($"Copying artifacts/log/ to {HELIX_WORKITEM_UPLOAD_ROOT}/"); + ProcessUtil.PrintMessage($"Copying artifacts/log/ to {HELIX_WORKITEM_UPLOAD_ROOT}/"); if (Directory.Exists("artifacts/log")) { foreach (var file in Directory.EnumerateFiles("artifacts/log", "*.log", SearchOption.AllDirectories)) { // Combine the directory name + log name for the copied log file name to avoid overwriting duplicate test names in different test projects var logName = $"{Path.GetFileName(Path.GetDirectoryName(file))}_{Path.GetFileName(file)}"; - Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName)}"); + ProcessUtil.PrintMessage($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName)}"); File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, logName)); } } else { - Console.WriteLine("No logs found in artifacts/log"); + ProcessUtil.PrintMessage("No logs found in artifacts/log"); } - Console.WriteLine($"Copying TestResults/**/Sequence*.xml to {HELIX_WORKITEM_UPLOAD_ROOT}/"); + ProcessUtil.PrintMessage($"Copying TestResults/**/Sequence*.xml to {HELIX_WORKITEM_UPLOAD_ROOT}/"); if (Directory.Exists("TestResults")) { foreach (var file in Directory.EnumerateFiles("TestResults", "Sequence*.xml", SearchOption.AllDirectories)) { var fileName = Path.GetFileName(file); - Console.WriteLine($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}"); + ProcessUtil.PrintMessage($"Copying: {file} to {Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)}"); File.Copy(file, Path.Combine(HELIX_WORKITEM_UPLOAD_ROOT, fileName)); } } else { - Console.WriteLine("No TestResults directory found."); + ProcessUtil.PrintMessage("No TestResults directory found."); } } } diff --git a/eng/targets/Helix.props b/eng/targets/Helix.props index dc2bad9552b9..3554c48dd037 100644 --- a/eng/targets/Helix.props +++ b/eng/targets/Helix.props @@ -11,8 +11,7 @@ true - 00:30:00 - 00:40:00 + 00:45:00 false $(MSBuildProjectName)--$(TargetFramework) false diff --git a/global.json b/global.json index 30a17d2cce7a..538e11d2fced 100644 --- a/global.json +++ b/global.json @@ -1,9 +1,9 @@ { "sdk": { - "version": "6.0.104" + "version": "6.0.105" }, "tools": { - "dotnet": "6.0.104", + "dotnet": "6.0.105", "runtimes": { "dotnet/x64": [ "2.1.30", @@ -13,7 +13,7 @@ "$(MicrosoftNETCoreBrowserDebugHostTransportVersion)" ], "aspnetcore/x64": [ - "3.1.24" + "3.1.25" ] }, "Git": "2.22.0", @@ -29,7 +29,7 @@ }, "msbuild-sdks": { "Yarn.MSBuild": "1.22.10", - "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.22212.5", - "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.22212.5" + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.22261.7", + "Microsoft.DotNet.Helix.Sdk": "6.0.0-beta.22261.7" } } diff --git a/src/Components/Web.JS/yarn.lock b/src/Components/Web.JS/yarn.lock index f67cf3464f37..36dc8fce6c96 100644 --- a/src/Components/Web.JS/yarn.lock +++ b/src/Components/Web.JS/yarn.lock @@ -3738,9 +3738,9 @@ minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== ms@2.1.2: version "2.1.2" diff --git a/src/Components/WebAssembly/Authentication.Msal/src/Interop/yarn.lock b/src/Components/WebAssembly/Authentication.Msal/src/Interop/yarn.lock index 51b9f4e1bfde..0a5574d5d439 100644 --- a/src/Components/WebAssembly/Authentication.Msal/src/Interop/yarn.lock +++ b/src/Components/WebAssembly/Authentication.Msal/src/Interop/yarn.lock @@ -2169,9 +2169,9 @@ minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== ms@2.1.2: version "2.1.2" diff --git a/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/yarn.lock b/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/yarn.lock index 6c69bb84ab52..a11ccca0b617 100644 --- a/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/yarn.lock +++ b/src/Components/WebAssembly/WebAssembly.Authentication/src/Interop/yarn.lock @@ -2170,9 +2170,9 @@ minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== ms@2.1.2: version "2.1.2" diff --git a/src/Components/test/E2ETest/yarn.lock b/src/Components/test/E2ETest/yarn.lock index 55b89cd5cc35..cd5a69ac5f3a 100644 --- a/src/Components/test/E2ETest/yarn.lock +++ b/src/Components/test/E2ETest/yarn.lock @@ -71,9 +71,9 @@ arg@^4.1.0: integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== async@^2.1.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity "sha1-cGt/9ghGZM1+rnE/b5ZUM7VQQiE= sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==" dependencies: lodash "^4.17.14" @@ -363,9 +363,9 @@ minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp@^1.0.4: version "1.0.4" diff --git a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj index 476107e41440..f81ddcae75ca 100644 --- a/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj +++ b/src/Components/test/testassets/BasicTestApp/BasicTestApp.csproj @@ -26,6 +26,7 @@ + diff --git a/src/DefaultBuilder/src/ConfigureHostBuilder.cs b/src/DefaultBuilder/src/ConfigureHostBuilder.cs index fdf8b2d0bc00..87e24d848894 100644 --- a/src/DefaultBuilder/src/ConfigureHostBuilder.cs +++ b/src/DefaultBuilder/src/ConfigureHostBuilder.cs @@ -62,6 +62,7 @@ public IHostBuilder ConfigureHostConfiguration(Action con var previousApplicationName = _configuration[HostDefaults.ApplicationKey]; // Use the real content root so we can compare paths var previousContentRoot = _context.HostingEnvironment.ContentRootPath; + var previousContentRootConfig = _configuration[HostDefaults.ContentRootKey]; var previousEnvironment = _configuration[HostDefaults.EnvironmentKey]; // Run these immediately so that they are observable by the imperative code @@ -74,7 +75,8 @@ public IHostBuilder ConfigureHostConfiguration(Action con throw new NotSupportedException($"The application name changed from \"{previousApplicationName}\" to \"{_configuration[HostDefaults.ApplicationKey]}\". Changing the host configuration using WebApplicationBuilder.Host is not supported. Use WebApplication.CreateBuilder(WebApplicationOptions) instead."); } - if (!string.Equals(previousContentRoot, HostingPathResolver.ResolvePath(_configuration[HostDefaults.ContentRootKey]), StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(previousContentRootConfig, _configuration[HostDefaults.ContentRootKey], StringComparison.OrdinalIgnoreCase) + && !string.Equals(previousContentRoot, HostingPathResolver.ResolvePath(_configuration[HostDefaults.ContentRootKey]), StringComparison.OrdinalIgnoreCase)) { throw new NotSupportedException($"The content root changed from \"{previousContentRoot}\" to \"{HostingPathResolver.ResolvePath(_configuration[HostDefaults.ContentRootKey])}\". Changing the host configuration using WebApplicationBuilder.Host is not supported. Use WebApplication.CreateBuilder(WebApplicationOptions) instead."); } diff --git a/src/DefaultBuilder/src/ConfigureWebHostBuilder.cs b/src/DefaultBuilder/src/ConfigureWebHostBuilder.cs index 0a2c482f53f0..d0ed6a822ab1 100644 --- a/src/DefaultBuilder/src/ConfigureWebHostBuilder.cs +++ b/src/DefaultBuilder/src/ConfigureWebHostBuilder.cs @@ -39,7 +39,9 @@ IWebHost IWebHostBuilder.Build() public IWebHostBuilder ConfigureAppConfiguration(Action configureDelegate) { var previousContentRoot = _context.HostingEnvironment.ContentRootPath; + var previousContentRootConfig = _configuration[WebHostDefaults.ContentRootKey]; var previousWebRoot = _context.HostingEnvironment.WebRootPath; + var previousWebRootConfig = _configuration[WebHostDefaults.WebRootKey]; var previousApplication = _configuration[WebHostDefaults.ApplicationKey]; var previousEnvironment = _configuration[WebHostDefaults.EnvironmentKey]; var previousHostingStartupAssemblies = _configuration[WebHostDefaults.HostingStartupAssembliesKey]; @@ -48,7 +50,8 @@ public IWebHostBuilder ConfigureAppConfiguration(Action { }); + + using var app = builder.Build(); + var hostEnv = app.Services.GetRequiredService(); + Assert.Equal(wwwroot, hostEnv.WebRootPath); + } + finally + { + if (createdDirectory) + { + Directory.Delete(wwwroot); + } + } + } + [Fact] public void HostConfigurationNotAffectedByConfiguration() { diff --git a/src/Hosting/Hosting/src/Internal/WebHost.cs b/src/Hosting/Hosting/src/Internal/WebHost.cs index ca52bd1f400b..bf9e339ddb51 100644 --- a/src/Hosting/Hosting/src/Internal/WebHost.cs +++ b/src/Hosting/Hosting/src/Internal/WebHost.cs @@ -81,12 +81,12 @@ public WebHost( // There's no way to to register multiple service types per definition. See https://github.com/aspnet/DependencyInjection/issues/360 #pragma warning disable CS8634 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'class' constraint. _applicationServiceCollection.AddSingleton(services - => services.GetService() as IHostApplicationLifetime); + => services.GetService()! as IHostApplicationLifetime); #pragma warning disable CS0618 // Type or member is obsolete _applicationServiceCollection.AddSingleton(services - => services.GetService() as AspNetCore.Hosting.IApplicationLifetime); + => services.GetService()! as AspNetCore.Hosting.IApplicationLifetime); _applicationServiceCollection.AddSingleton(services - => services.GetService() as Extensions.Hosting.IApplicationLifetime); + => services.GetService()! as Extensions.Hosting.IApplicationLifetime); #pragma warning restore CS0618 // Type or member is obsolete #pragma warning restore CS8634 // The type cannot be used as type parameter in the generic type or method. Nullability of type argument doesn't match 'class' constraint. _applicationServiceCollection.AddSingleton(); diff --git a/src/Http/Http.Abstractions/src/Extensions/MapMiddleware.cs b/src/Http/Http.Abstractions/src/Extensions/MapMiddleware.cs index 35b25f40e62b..3122b7640909 100644 --- a/src/Http/Http.Abstractions/src/Extensions/MapMiddleware.cs +++ b/src/Http/Http.Abstractions/src/Extensions/MapMiddleware.cs @@ -64,7 +64,7 @@ public Task Invoke(HttpContext context) return _next(context); } - private async Task InvokeCore(HttpContext context, string matchedPath, string remainingPath) + private async Task InvokeCore(HttpContext context, PathString matchedPath, PathString remainingPath) { var path = context.Request.Path; var pathBase = context.Request.PathBase; diff --git a/src/Http/Http.Abstractions/src/Extensions/UsePathBaseExtensions.cs b/src/Http/Http.Abstractions/src/Extensions/UsePathBaseExtensions.cs index 0bdf4fcfed97..46280bb4f221 100644 --- a/src/Http/Http.Abstractions/src/Extensions/UsePathBaseExtensions.cs +++ b/src/Http/Http.Abstractions/src/Extensions/UsePathBaseExtensions.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; @@ -26,7 +26,7 @@ public static IApplicationBuilder UsePathBase(this IApplicationBuilder app, Path } // Strip trailing slashes - pathBase = pathBase.Value?.TrimEnd('/'); + pathBase = new PathString(pathBase.Value?.TrimEnd('/')); if (!pathBase.HasValue) { return app; @@ -35,4 +35,4 @@ public static IApplicationBuilder UsePathBase(this IApplicationBuilder app, Path return app.UseMiddleware(pathBase); } } -} \ No newline at end of file +} diff --git a/src/Http/Http.Abstractions/src/Extensions/UsePathBaseMiddleware.cs b/src/Http/Http.Abstractions/src/Extensions/UsePathBaseMiddleware.cs index 34ffc5738d40..ef7b9d586cfd 100644 --- a/src/Http/Http.Abstractions/src/Extensions/UsePathBaseMiddleware.cs +++ b/src/Http/Http.Abstractions/src/Extensions/UsePathBaseMiddleware.cs @@ -55,7 +55,7 @@ public Task Invoke(HttpContext context) return _next(context); } - private async Task InvokeCore(HttpContext context, string matchedPath, string remainingPath) + private async Task InvokeCore(HttpContext context, PathString matchedPath, PathString remainingPath) { var originalPath = context.Request.Path; var originalPathBase = context.Request.PathBase; diff --git a/src/Http/Http.Abstractions/test/MapPathMiddlewareTests.cs b/src/Http/Http.Abstractions/test/MapPathMiddlewareTests.cs index 48c718a47c16..acaa0e0bce6d 100644 --- a/src/Http/Http.Abstractions/test/MapPathMiddlewareTests.cs +++ b/src/Http/Http.Abstractions/test/MapPathMiddlewareTests.cs @@ -43,24 +43,27 @@ public void NullArguments_ArgumentNullException() } [Theory] - [InlineData("/foo", "", "/foo")] - [InlineData("/foo", "", "/foo/")] - [InlineData("/foo", "/Bar", "/foo")] - [InlineData("/foo", "/Bar", "/foo/cho")] - [InlineData("/foo", "/Bar", "/foo/cho/")] - [InlineData("/foo/cho", "/Bar", "/foo/cho")] - [InlineData("/foo/cho", "/Bar", "/foo/cho/do")] - public async Task PathMatchFunc_BranchTaken(string matchPath, string basePath, string requestPath) + [InlineData("/foo", "", "/foo", "/foo", "")] + [InlineData("/foo", "", "/foo/", "/foo", "/")] + [InlineData("/foo", "/Bar", "/foo", "/Bar/foo", "")] + [InlineData("/foo", "/Bar", "/foo/cho", "/Bar/foo", "/cho")] + [InlineData("/foo", "/Bar", "/foo/cho/", "/Bar/foo", "/cho/")] + [InlineData("/foo/cho", "/Bar", "/foo/cho", "/Bar/foo/cho", "")] + [InlineData("/foo/cho", "/Bar", "/foo/cho/do", "/Bar/foo/cho", "/do")] + [InlineData("/foo%42/cho", "/Bar%42", "/foo%42/cho/do%42", "/Bar%42/foo%42/cho", "/do%42")] + public async Task PathMatchFunc_BranchTaken(string matchPath, string basePath, string requestPath, string expectedPathBase, string expectedPath) { HttpContext context = CreateRequest(basePath, requestPath); var builder = new ApplicationBuilder(serviceProvider: null!); - builder.Map(matchPath, UseSuccess); + builder.Map(new PathString(matchPath), UseSuccess); var app = builder.Build(); await app.Invoke(context); Assert.Equal(200, context.Response.StatusCode); Assert.Equal(basePath, context.Request.PathBase.Value); Assert.Equal(requestPath, context.Request.Path.Value); + Assert.Equal(expectedPathBase, (string)context.Items["test.PathBase"]!); + Assert.Equal(expectedPath, context.Items["test.Path"]); } [Theory] diff --git a/src/Http/Http.Abstractions/test/UsePathBaseExtensionsTests.cs b/src/Http/Http.Abstractions/test/UsePathBaseExtensionsTests.cs index a5511ef0c18b..ebb695ed0656 100644 --- a/src/Http/Http.Abstractions/test/UsePathBaseExtensionsTests.cs +++ b/src/Http/Http.Abstractions/test/UsePathBaseExtensionsTests.cs @@ -131,11 +131,23 @@ public Task PathBaseCanHaveUnicodeCharacters(string registeredPathBase, string p return TestPathBase(registeredPathBase, pathBase, requestPath, expectedPathBase, expectedPath); } + [Theory] + [InlineData("/b%42", "", "/b%42/something%42", "/b%42", "/something%42")] + [InlineData("/b%42", "", "/B%42/something%42", "/B%42", "/something%42")] + [InlineData("/b%42", "", "/b%42/Something%42", "/b%42", "/Something%42")] + [InlineData("/b%42", "/oldb%42", "/b%42/something%42", "/oldb%42/b%42", "/something%42")] + [InlineData("/b%42", "/oldb%42", "/b%42/Something%42", "/oldb%42/b%42", "/Something%42")] + [InlineData("/b%42", "/oldb%42", "/B%42/something%42", "/oldb%42/B%42", "/something%42")] + public Task PathBaseCanHavePercentCharacters(string registeredPathBase, string pathBase, string requestPath, string expectedPathBase, string expectedPath) + { + return TestPathBase(registeredPathBase, pathBase, requestPath, expectedPathBase, expectedPath); + } + private static async Task TestPathBase(string registeredPathBase, string pathBase, string requestPath, string expectedPathBase, string expectedPath) { HttpContext requestContext = CreateRequest(pathBase, requestPath); var builder = CreateBuilder() - .UsePathBase(registeredPathBase); + .UsePathBase(new PathString(registeredPathBase)); builder.Run(context => { context.Items["test.Path"] = context.Request.Path; diff --git a/src/Http/WebUtilities/src/FileBufferingReadStream.cs b/src/Http/WebUtilities/src/FileBufferingReadStream.cs index 720b8921b553..f66916c8ebff 100644 --- a/src/Http/WebUtilities/src/FileBufferingReadStream.cs +++ b/src/Http/WebUtilities/src/FileBufferingReadStream.cs @@ -317,7 +317,8 @@ public override int Read(Span buffer) { _buffer.Write(buffer.Slice(0, read)); } - else + // Allow zero-byte reads + else if (buffer.Length > 0) { _completelyBuffered = true; } @@ -392,7 +393,8 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation { await _buffer.WriteAsync(buffer.Slice(0, read), cancellationToken); } - else + // Allow zero-byte reads + else if (buffer.Length > 0) { _completelyBuffered = true; } diff --git a/src/Http/WebUtilities/test/FileBufferingReadStreamTests.cs b/src/Http/WebUtilities/test/FileBufferingReadStreamTests.cs index 7d6c42229239..640562b6c6a0 100644 --- a/src/Http/WebUtilities/test/FileBufferingReadStreamTests.cs +++ b/src/Http/WebUtilities/test/FileBufferingReadStreamTests.cs @@ -35,6 +35,39 @@ public void FileBufferingReadStream_Properties_ExpectedValues() } } + [Fact] + public void FileBufferingReadStream_Sync0ByteReadUnderThreshold_DoesntCreateFile() + { + var inner = MakeStream(1024); + using (var stream = new FileBufferingReadStream(inner, 1024 * 2, null, Directory.GetCurrentDirectory())) + { + var bytes = new byte[1000]; + var read0 = stream.Read(bytes, 0, 0); + Assert.Equal(0, read0); + Assert.Equal(read0, stream.Length); + Assert.Equal(read0, stream.Position); + Assert.True(stream.InMemory); + Assert.Null(stream.TempFileName); + + var read1 = stream.Read(bytes, 0, bytes.Length); + Assert.Equal(bytes.Length, read1); + Assert.Equal(read0 + read1, stream.Length); + Assert.Equal(read0 + read1, stream.Position); + Assert.True(stream.InMemory); + Assert.Null(stream.TempFileName); + + var read2 = stream.Read(bytes, 0, bytes.Length); + Assert.Equal(inner.Length - read0 - read1, read2); + Assert.Equal(read0 + read1 + read2, stream.Length); + Assert.Equal(read0 + read1 + read2, stream.Position); + Assert.True(stream.InMemory); + Assert.Null(stream.TempFileName); + + var read3 = stream.Read(bytes, 0, bytes.Length); + Assert.Equal(0, read3); + } + } + [Fact] public void FileBufferingReadStream_SyncReadUnderThreshold_DoesntCreateFile() { @@ -164,6 +197,39 @@ public void FileBufferingReadStream_SyncReadWithOnDiskLimit_EnforcesLimit() /////////////////// + [Fact] + public async Task FileBufferingReadStream_Async0ByteReadUnderThreshold_DoesntCreateFile() + { + var inner = MakeStream(1024); + using (var stream = new FileBufferingReadStream(inner, 1024 * 2, null, Directory.GetCurrentDirectory())) + { + var bytes = new byte[1000]; + var read0 = await stream.ReadAsync(bytes, 0, 0); + Assert.Equal(0, read0); + Assert.Equal(read0, stream.Length); + Assert.Equal(read0, stream.Position); + Assert.True(stream.InMemory); + Assert.Null(stream.TempFileName); + + var read1 = await stream.ReadAsync(bytes, 0, bytes.Length); + Assert.Equal(bytes.Length, read1); + Assert.Equal(read0 + read1, stream.Length); + Assert.Equal(read0 + read1, stream.Position); + Assert.True(stream.InMemory); + Assert.Null(stream.TempFileName); + + var read2 = await stream.ReadAsync(bytes, 0, bytes.Length); + Assert.Equal(inner.Length - read0 - read1, read2); + Assert.Equal(read0 + read1 + read2, stream.Length); + Assert.Equal(read0 + read1 + read2, stream.Position); + Assert.True(stream.InMemory); + Assert.Null(stream.TempFileName); + + var read3 = await stream.ReadAsync(bytes, 0, bytes.Length); + Assert.Equal(0, read3); + } + } + [Fact] public async Task FileBufferingReadStream_AsyncReadUnderThreshold_DoesntCreateFile() { @@ -236,6 +302,47 @@ public async Task FileBufferingReadStream_AsyncReadOverThreshold_CreatesFile() Assert.False(File.Exists(tempFileName)); } + [Fact] + public async Task FileBufferingReadStream_Async0ByteReadAfterBuffering_ReadsFromFile() + { + var inner = MakeStream(1024 * 2); + string tempFileName; + using (var stream = new FileBufferingReadStream(inner, 1024, null, GetCurrentDirectory())) + { + await stream.DrainAsync(default); + stream.Position = 0; + Assert.Equal(inner.Length, stream.Length); + Assert.Equal(0, stream.Position); + Assert.False(stream.InMemory); + Assert.NotNull(stream.TempFileName); + tempFileName = stream.TempFileName!; + Assert.True(File.Exists(tempFileName)); + + var bytes = new byte[1000]; + var read0 = await stream.ReadAsync(bytes, 0, 0); + Assert.Equal(0, read0); + Assert.Equal(read0, stream.Position); + + var read1 = await stream.ReadAsync(bytes, 0, bytes.Length); + Assert.Equal(bytes.Length, read1); + Assert.Equal(read0 + read1, stream.Position); + + var read2 = await stream.ReadAsync(bytes, 0, bytes.Length); + Assert.Equal(bytes.Length, read2); + Assert.Equal(read0 + read1 + read2, stream.Position); + + var read3 = await stream.ReadAsync(bytes, 0, bytes.Length); + Assert.Equal(inner.Length - read0 - read1 - read2, read3); + Assert.Equal(read0 + read1 + read2 + read3, stream.Length); + Assert.Equal(read0 + read1 + read2 + read3, stream.Position); + + var read4 = await stream.ReadAsync(bytes, 0, bytes.Length); + Assert.Equal(0, read4); + } + + Assert.False(File.Exists(tempFileName)); + } + [Fact] public async Task FileBufferingReadStream_AsyncReadWithInMemoryLimit_EnforcesLimit() { diff --git a/src/JSInterop/Microsoft.JSInterop.JS/src/yarn.lock b/src/JSInterop/Microsoft.JSInterop.JS/src/yarn.lock index b438080d556a..886066a3154c 100644 --- a/src/JSInterop/Microsoft.JSInterop.JS/src/yarn.lock +++ b/src/JSInterop/Microsoft.JSInterop.JS/src/yarn.lock @@ -176,9 +176,9 @@ minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp@^0.5.3: version "0.5.5" @@ -200,9 +200,9 @@ path-is-absolute@^1.0.0: integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== resolve@^1.3.2: version "1.20.0" diff --git a/src/Middleware/HttpLogging/src/FileLoggerProcessor.cs b/src/Middleware/HttpLogging/src/FileLoggerProcessor.cs index 9f0e4f5821ba..60b4a6265867 100644 --- a/src/Middleware/HttpLogging/src/FileLoggerProcessor.cs +++ b/src/Middleware/HttpLogging/src/FileLoggerProcessor.cs @@ -156,23 +156,28 @@ private async Task WriteMessagesAsync(List messages, CancellationToken c { // Files are written up to _maxFileSize before rolling to a new file DateTime today = DateTime.Now; + + if (!TryCreateDirectory()) + { + // return early if we fail to create the directory + return; + } + var fullName = GetFullName(today); // Don't write to an incomplete file left around by a previous FileLoggerProcessor if (_firstFile) { - while (File.Exists(fullName)) + _fileNumber = GetFirstFileCount(today); + fullName = GetFullName(today); + if (_fileNumber >= W3CLoggerOptions.MaxFileCount) { - _fileNumber++; - if (_fileNumber >= W3CLoggerOptions.MaxFileCount) - { - _maxFilesReached = true; - // Return early if log directory is already full - Log.MaxFilesReached(_logger); - return; - } - fullName = GetFullName(today); + _maxFilesReached = true; + // Return early if log directory is already full + Log.MaxFilesReached(_logger); + return; } } + _firstFile = false; if (_maxFilesReached) { @@ -300,6 +305,23 @@ public async ValueTask DisposeAsync() await _outputTask; } + private int GetFirstFileCount(DateTime date) + { + lock (_pathLock) + { + var searchString = FormattableString.Invariant($"{_fileName}{date.Year:0000}{date.Month:00}{date.Day:00}.*.txt"); + var files = new DirectoryInfo(_path) + .GetFiles(searchString); + + return files.Length == 0 + ? 0 + : files + .Max(x => int.TryParse(x.Name.Split('.').ElementAtOrDefault(Index.FromEnd(2)), out var parsed) + ? parsed + 1 + : 0); + } + } + private string GetFullName(DateTime date) { lock (_pathLock) diff --git a/src/Middleware/HttpLogging/src/W3CLoggerProcessor.cs b/src/Middleware/HttpLogging/src/W3CLoggerProcessor.cs index 749da2b6cfbe..d3a44994a8bb 100644 --- a/src/Middleware/HttpLogging/src/W3CLoggerProcessor.cs +++ b/src/Middleware/HttpLogging/src/W3CLoggerProcessor.cs @@ -27,7 +27,7 @@ public override async Task OnFirstWrite(StreamWriter streamWriter, CancellationT { await WriteMessageAsync("#Version: 1.0", streamWriter, cancellationToken); - await WriteMessageAsync("#Start-Date: " + DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), streamWriter, cancellationToken); + await WriteMessageAsync("#Start-Date: " + DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture), streamWriter, cancellationToken); await WriteMessageAsync(GetFieldsDirective(), streamWriter, cancellationToken); } diff --git a/src/Middleware/HttpLogging/test/FileLoggerProcessorTests.cs b/src/Middleware/HttpLogging/test/FileLoggerProcessorTests.cs index b051337a5c7d..fd00142756f4 100644 --- a/src/Middleware/HttpLogging/test/FileLoggerProcessorTests.cs +++ b/src/Middleware/HttpLogging/test/FileLoggerProcessorTests.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Hosting.Internal; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Logging.Testing; using Microsoft.Net.Http.Headers; using Xunit; @@ -23,6 +24,8 @@ public class FileLoggerProcessorTests private string _messageOne = "Message one"; private string _messageTwo = "Message two"; private string _messageThree = "Message three"; + private string _messageFour = "Message four"; + private readonly DateTime _today = DateTime.UtcNow; public FileLoggerProcessorTests() { @@ -190,6 +193,60 @@ public async Task RespectsMaxFileCount() } } + [Fact] + public async Task StopsLoggingAfter10000Files() + { + var path = Path.Combine(TempPath, Path.GetRandomFileName()); + Directory.CreateDirectory(path); + + try + { + string lastFileName; + var options = new W3CLoggerOptions() + { + LogDirectory = path, + FileSizeLimit = 5, + RetainedFileCountLimit = 10000 + }; + var testSink = new TestSink(); + var testLogger = new TestLoggerFactory(testSink, enabled:true); + await using (var logger = new FileLoggerProcessor(new OptionsWrapperMonitor(options), new HostingEnvironment(), testLogger)) + { + for (int i = 0; i < 10000; i++) + { + logger.EnqueueMessage(_messageOne); + } + lastFileName = Path.Combine(path, FormattableString.Invariant($"{options.FileName}{_today.Year:0000}{_today.Month:00}{_today.Day:00}.9999.txt")); + await WaitForFile(lastFileName, _messageOne.Length).DefaultTimeout(); + + // directory is full, no warnings yet + Assert.Equal(0, testSink.Writes.Count); + + logger.EnqueueMessage(_messageOne); + await WaitForCondition(() => testSink.Writes.FirstOrDefault()?.EventId.Name == "MaxFilesReached").DefaultTimeout(); + } + + Assert.Equal(10000, new DirectoryInfo(path) + .GetFiles() + .ToArray().Length); + + // restarting the logger should do nothing since the folder is still full + var testSink2 = new TestSink(); + var testLogger2 = new TestLoggerFactory(testSink2, enabled:true); + await using (var logger = new FileLoggerProcessor(new OptionsWrapperMonitor(options), new HostingEnvironment(), testLogger2)) + { + Assert.Equal(0, testSink2.Writes.Count); + + logger.EnqueueMessage(_messageOne); + await WaitForCondition(() => testSink2.Writes.FirstOrDefault()?.EventId.Name == "MaxFilesReached").DefaultTimeout(); + } + } + finally + { + Helpers.DisposeDirectory(path); + } + } + [Fact] public async Task InstancesWriteToSameDirectory() { @@ -340,6 +397,66 @@ public async Task WritesToNewFileOnNewInstance() } } + [Fact] + public async Task RollsTextFilesWhenFirstLogOfDayIsMissing() + { + var path = Path.Combine(TempPath, Path.GetRandomFileName()); + Directory.CreateDirectory(path); + + try + { + var options = new W3CLoggerOptions() + { + LogDirectory = path, + FileSizeLimit = 5, + RetainedFileCountLimit = 2, + }; + var fileName1 = Path.Combine(path, FormattableString.Invariant($"{options.FileName}{_today.Year:0000}{_today.Month:00}{_today.Day:00}.0000.txt")); + var fileName2 = Path.Combine(path, FormattableString.Invariant($"{options.FileName}{_today.Year:0000}{_today.Month:00}{_today.Day:00}.0001.txt")); + var fileName3 = Path.Combine(path, FormattableString.Invariant($"{options.FileName}{_today.Year:0000}{_today.Month:00}{_today.Day:00}.0002.txt")); + var fileName4 = Path.Combine(path, FormattableString.Invariant($"{options.FileName}{_today.Year:0000}{_today.Month:00}{_today.Day:00}.0003.txt")); + + await using (var logger = new FileLoggerProcessor(new OptionsWrapperMonitor(options), new HostingEnvironment(), NullLoggerFactory.Instance)) + { + logger.EnqueueMessage(_messageOne); + logger.EnqueueMessage(_messageTwo); + logger.EnqueueMessage(_messageThree); + // Pause for a bit before disposing so logger can finish logging + await WaitForFile(fileName3, _messageThree.Length).DefaultTimeout(); + } + + // Even with a big enough FileSizeLimit, we still won't try to write to files from a previous instance. + options.FileSizeLimit = 10000; + + await using (var logger = new FileLoggerProcessor(new OptionsWrapperMonitor(options), new HostingEnvironment(), NullLoggerFactory.Instance)) + { + logger.EnqueueMessage(_messageFour); + // Pause for a bit before disposing so logger can finish logging + await WaitForFile(fileName4, _messageFour.Length).DefaultTimeout(); + } + + var actualFiles = new DirectoryInfo(path) + .GetFiles() + .Select(f => f.Name) + .OrderBy(f => f) + .ToArray(); + + Assert.Equal(2, actualFiles.Length); + + Assert.False(File.Exists(fileName1)); + Assert.False(File.Exists(fileName2)); + Assert.True(File.Exists(fileName3)); + Assert.True(File.Exists(fileName4)); + + Assert.Equal(_messageThree + Environment.NewLine, File.ReadAllText(fileName3)); + Assert.Equal(_messageFour + Environment.NewLine, File.ReadAllText(fileName4)); + } + finally + { + Helpers.DisposeDirectory(path); + } + } + [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/34982")] [Fact] public async Task WritesToNewFileOnOptionsChange() @@ -420,6 +537,14 @@ private async Task WaitForFile(string fileName, int length) } } + private async Task WaitForCondition(Func waitForLog) + { + while (!waitForLog()) + { + await Task.Delay(10); + } + } + private async Task WaitForRoll(string fileName) { while (File.Exists(fileName)) diff --git a/src/Middleware/HttpLogging/test/W3CLoggerTests.cs b/src/Middleware/HttpLogging/test/W3CLoggerTests.cs index 759f6354ea86..078aa5af4c46 100644 --- a/src/Middleware/HttpLogging/test/W3CLoggerTests.cs +++ b/src/Middleware/HttpLogging/test/W3CLoggerTests.cs @@ -25,7 +25,7 @@ public class W3CLoggerTests public async Task WritesDateTime() { var path = Path.GetTempFileName() + "_"; - var now = DateTime.Now; + var now = DateTime.UtcNow; var options = new W3CLoggerOptions() { LoggingFields = W3CLoggingFields.Date | W3CLoggingFields.Time, @@ -48,7 +48,9 @@ public async Task WritesDateTime() Assert.StartsWith("#Start-Date: ", lines[1]); var startDate = DateTime.Parse(lines[1].Substring(13), CultureInfo.InvariantCulture); // Assert that the log was written in the last 10 seconds - Assert.True(now.Subtract(startDate).TotalSeconds < 10); + // W3CLogger writes start-time to second precision, so delta could be as low as -0.999... + var delta = startDate.Subtract(now).TotalSeconds; + Assert.InRange(delta, -1, 10); Assert.Equal("#Fields: date time", lines[2]); @@ -89,7 +91,9 @@ public async Task HandlesNullValuesAsync() Assert.StartsWith("#Start-Date: ", lines[1]); var startDate = DateTime.Parse(lines[1].Substring(13), CultureInfo.InvariantCulture); // Assert that the log was written in the last 10 seconds - Assert.True(now.Subtract(startDate).TotalSeconds < 10); + // W3CLogger writes start-time to second precision, so delta could be as low as -0.999... + var delta = startDate.Subtract(now).TotalSeconds; + Assert.InRange(delta, -1, 10); Assert.Equal("#Fields: cs-uri-query sc-status cs-host", lines[2]); Assert.Equal("- - -", lines[3]); diff --git a/src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs b/src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs index e1f1d0ab6849..9c4a20f3e72c 100644 --- a/src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs +++ b/src/Middleware/HttpLogging/test/W3CLoggingMiddlewareTests.cs @@ -94,7 +94,7 @@ public async Task DefaultDoesNotLogOptionalFields() httpContext.Request.Headers["Cookie"] = "Snickerdoodle"; httpContext.Response.StatusCode = 200; - var now = DateTime.Now; + var now = DateTime.UtcNow; await middleware.Invoke(httpContext); await logger.Processor.WaitForWrites(4).DefaultTimeout(); @@ -104,7 +104,9 @@ public async Task DefaultDoesNotLogOptionalFields() Assert.StartsWith("#Start-Date: ", lines[1]); var startDate = DateTime.Parse(lines[1].Substring(13), CultureInfo.InvariantCulture); // Assert that the log was written in the last 10 seconds - Assert.True(now.Subtract(startDate).TotalSeconds < 10); + // W3CLogger writes start-time to second precision, so delta could be as low as -0.999... + var delta = startDate.Subtract(now).TotalSeconds; + Assert.InRange(delta, -1, 10); Assert.Equal("#Fields: date time c-ip s-computername s-ip s-port cs-method cs-uri-stem cs-uri-query sc-status time-taken cs-version cs-host cs(User-Agent) cs(Referer)", lines[2]); Assert.DoesNotContain(lines[3], "Snickerdoodle"); @@ -128,7 +130,7 @@ public async Task TimeTakenIsInMilliseconds() var httpContext = new DefaultHttpContext(); - var now = DateTime.Now; + var now = DateTime.UtcNow; await middleware.Invoke(httpContext); await logger.Processor.WaitForWrites(4).DefaultTimeout(); @@ -138,7 +140,9 @@ public async Task TimeTakenIsInMilliseconds() Assert.StartsWith("#Start-Date: ", lines[1]); var startDate = DateTime.Parse(lines[1].Substring(13), CultureInfo.InvariantCulture); // Assert that the log was written in the last 10 seconds - Assert.True(now.Subtract(startDate).TotalSeconds < 10); + // W3CLogger writes start-time to second precision, so delta could be as low as -0.999... + var delta = startDate.Subtract(now).TotalSeconds; + Assert.InRange(delta, -1, 10); Assert.Equal("#Fields: time-taken", lines[2]); double num; diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs index 7ff582dcd896..3a8523a5e23f 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs +++ b/src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs @@ -29,7 +29,7 @@ protected async Task CreateBuildPublishAsync(string projectName, string // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - var project = await ProjectFactory.GetOrCreateProject(projectName, Output); + var project = await ProjectFactory.CreateProject(Output); if (targetFramework != null) { project.TargetFramework = targetFramework; diff --git a/src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock b/src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock index 136eccbe7f67..8eb6533d4475 100644 --- a/src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock +++ b/src/ProjectTemplates/BlazorTemplates.Tests/yarn.lock @@ -254,9 +254,9 @@ mimic-response@^3.1.0: integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp@^1.0.4: version "1.0.4" diff --git a/src/ProjectTemplates/Shared/ProcessResult.cs b/src/ProjectTemplates/Shared/ProcessResult.cs index 81ff0f2bd13b..474f252f2e80 100644 --- a/src/ProjectTemplates/Shared/ProcessResult.cs +++ b/src/ProjectTemplates/Shared/ProcessResult.cs @@ -17,7 +17,7 @@ public ProcessResult(ProcessEx process) public string Process { get; } - public int ExitCode { get; } + public int ExitCode { get; set; } public string Error { get; } diff --git a/src/ProjectTemplates/Shared/Project.cs b/src/ProjectTemplates/Shared/Project.cs index f78a7d4ab238..14e4c1985e1a 100644 --- a/src/ProjectTemplates/Shared/Project.cs +++ b/src/ProjectTemplates/Shared/Project.cs @@ -54,6 +54,7 @@ internal async Task RunDotNetNewAsync( string language = null, bool useLocalDB = false, bool noHttps = false, + bool errorOnRestoreError = true, string[] args = null, // Used to set special options in MSBuild IDictionary environmentVariables = null) @@ -95,29 +96,15 @@ internal async Task RunDotNetNewAsync( argString += $" -o {TemplateOutputDir}"; - // Only run one instance of 'dotnet new' at once, as a workaround for - // https://github.com/aspnet/templating/issues/63 - - await DotNetNewLock.WaitAsync(); - try - { - Output.WriteLine("Acquired DotNetNewLock"); - - if (Directory.Exists(TemplateOutputDir)) - { - Output.WriteLine($"Template directory already exists, deleting contents of {TemplateOutputDir}"); - Directory.Delete(TemplateOutputDir, recursive: true); - } - - using var execution = ProcessEx.Run(Output, AppContext.BaseDirectory, DotNetMuxer.MuxerPathOrDefault(), argString, environmentVariables); - await execution.Exited; - return new ProcessResult(execution); - } - finally + if (Directory.Exists(TemplateOutputDir)) { - DotNetNewLock.Release(); - Output.WriteLine("Released DotNetNewLock"); + Output.WriteLine($"Template directory already exists, deleting contents of {TemplateOutputDir}"); + Directory.Delete(TemplateOutputDir, recursive: true); } + + using var execution = ProcessEx.Run(Output, AppContext.BaseDirectory, DotNetMuxer.MuxerPathOrDefault(), argString, environmentVariables); + await execution.Exited; + return new ProcessResult(execution); } internal async Task RunDotNetPublishAsync(IDictionary packageOptions = null, string additionalArgs = null, bool noRestore = true) @@ -183,31 +170,19 @@ internal async Task RunDotNetEfCreateMigrationAsync(string migrat { var args = $"--verbose --no-build migrations add {migrationName}"; - // Only run one instance of 'dotnet new' at once, as a workaround for - // https://github.com/aspnet/templating/issues/63 - await DotNetNewLock.WaitAsync(); - try + var command = DotNetMuxer.MuxerPathOrDefault(); + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath"))) { - Output.WriteLine("Acquired DotNetNewLock"); - var command = DotNetMuxer.MuxerPathOrDefault(); - if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath"))) - { - args = $"\"{DotNetEfFullPath}\" " + args; - } - else - { - command = "dotnet-ef"; - } - - using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args); - await result.Exited; - return new ProcessResult(result); + args = $"\"{DotNetEfFullPath}\" " + args; } - finally + else { - DotNetNewLock.Release(); - Output.WriteLine("Released DotNetNewLock"); + command = "dotnet-ef"; } + + using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args); + await result.Exited; + return new ProcessResult(result); } internal async Task RunDotNetEfUpdateDatabaseAsync() @@ -216,31 +191,19 @@ internal async Task RunDotNetEfUpdateDatabaseAsync() var args = "--verbose --no-build database update"; - // Only run one instance of 'dotnet new' at once, as a workaround for - // https://github.com/aspnet/templating/issues/63 - await DotNetNewLock.WaitAsync(); - try + var command = DotNetMuxer.MuxerPathOrDefault(); + if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath"))) { - Output.WriteLine("Acquired DotNetNewLock"); - var command = DotNetMuxer.MuxerPathOrDefault(); - if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath"))) - { - args = $"\"{DotNetEfFullPath}\" " + args; - } - else - { - command = "dotnet-ef"; - } - - using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args); - await result.Exited; - return new ProcessResult(result); + args = $"\"{DotNetEfFullPath}\" " + args; } - finally + else { - DotNetNewLock.Release(); - Output.WriteLine("Released DotNetNewLock"); + command = "dotnet-ef"; } + + using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args); + await result.Exited; + return new ProcessResult(result); } // If this fails, you should generate new migrations via migrations/updateMigrations.cmd @@ -294,25 +257,15 @@ public string ReadFile(string path) internal async Task RunDotNetNewRawAsync(string arguments) { - await DotNetNewLock.WaitAsync(); - try - { - Output.WriteLine("Acquired DotNetNewLock"); - var result = ProcessEx.Run( - Output, - AppContext.BaseDirectory, - DotNetMuxer.MuxerPathOrDefault(), - arguments + - $" --debug:disable-sdk-templates --debug:custom-hive \"{TemplatePackageInstaller.CustomHivePath}\"" + - $" -o {TemplateOutputDir}"); - await result.Exited; - return result; - } - finally - { - DotNetNewLock.Release(); - Output.WriteLine("Released DotNetNewLock"); - } + var result = ProcessEx.Run( + Output, + AppContext.BaseDirectory, + DotNetMuxer.MuxerPathOrDefault(), + arguments + + $" --debug:disable-sdk-templates --debug:custom-hive \"{TemplatePackageInstaller.CustomHivePath}\"" + + $" -o {TemplateOutputDir}"); + await result.Exited; + return result; } public void Dispose() diff --git a/src/ProjectTemplates/Shared/ProjectFactoryFixture.cs b/src/ProjectTemplates/Shared/ProjectFactoryFixture.cs index 9af84efc680d..f18518286018 100644 --- a/src/ProjectTemplates/Shared/ProjectFactoryFixture.cs +++ b/src/ProjectTemplates/Shared/ProjectFactoryFixture.cs @@ -14,6 +14,7 @@ namespace Templates.Test.Helpers { public class ProjectFactoryFixture : IDisposable { + private const string LetterChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private readonly ConcurrentDictionary _projects = new ConcurrentDictionary(); public IMessageSink DiagnosticsMessageSink { get; } @@ -23,6 +24,21 @@ public ProjectFactoryFixture(IMessageSink diagnosticsMessageSink) DiagnosticsMessageSink = diagnosticsMessageSink; } + public async Task CreateProject(ITestOutputHelper output) + { + await TemplatePackageInstaller.EnsureTemplatingEngineInitializedAsync(output); + + var project = CreateProjectImpl(output); + + var projectKey = Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant(); + if (!_projects.TryAdd(projectKey, project)) + { + throw new InvalidOperationException($"Project key collision in {nameof(ProjectFactoryFixture)}.{nameof(CreateProject)}!"); + } + + return project; + } + public async Task GetOrCreateProject(string projectKey, ITestOutputHelper output) { await TemplatePackageInstaller.EnsureTemplatingEngineInitializedAsync(output); @@ -34,24 +50,31 @@ public async Task GetOrCreateProject(string projectKey, ITestOutputHelp } return _projects.GetOrAdd( projectKey, - (key, outputHelper) => - { - var project = new Project - { - Output = outputHelper, - DiagnosticsMessageSink = DiagnosticsMessageSink, - ProjectGuid = Path.GetRandomFileName().Replace(".", string.Empty) - }; - project.ProjectName = $"AspNet.{project.ProjectGuid}"; - - var assemblyPath = GetType().Assembly; - var basePath = GetTemplateFolderBasePath(assemblyPath); - project.TemplateOutputDir = Path.Combine(basePath, project.ProjectName); - return project; - }, + (_, outputHelper) => CreateProjectImpl(outputHelper), output); } + private Project CreateProjectImpl(ITestOutputHelper output) + { + var project = new Project + { + Output = output, + DiagnosticsMessageSink = DiagnosticsMessageSink, + // Ensure first character is a letter to avoid random insertions of '_' into template namespace + // declarations (i.e. make it more stable for testing) + ProjectGuid = GetRandomLetter() + Path.GetRandomFileName().Replace(".", string.Empty) + }; + project.ProjectName = $"AspNetCore.{project.ProjectGuid}"; + + var assemblyPath = GetType().Assembly; + var basePath = GetTemplateFolderBasePath(assemblyPath); + project.TemplateOutputDir = Path.Combine(basePath, project.ProjectName); + + return project; + } + + private static char GetRandomLetter() => LetterChars[Random.Shared.Next(LetterChars.Length - 1)]; + private static string GetTemplateFolderBasePath(Assembly assembly) => (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX_DIR"))) ? assembly.GetCustomAttributes() diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs index 92eb45d80a91..a0b654422797 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs @@ -37,7 +37,7 @@ #endif using BlazorServerWeb_CSharp.Data; -namespace Company.WebApplication1; +namespace BlazorServerWeb_CSharp; public class Program { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Program.Main.cs index 8b870e5dc9d3..18be2444cb69 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Program.Main.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Client/Program.Main.cs @@ -9,7 +9,11 @@ using ComponentsWebAssembly_CSharp; #endif -namespace Company.WebApplication1; +#if (Hosted) +namespace ComponentsWebAssembly_CSharp.Client; +#else +namespace ComponentsWebAssembly_CSharp; +#endif public class Program { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs index 31835439cd28..f6e53531c467 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/ComponentsWebAssembly-CSharp/Server/Program.Main.cs @@ -19,7 +19,7 @@ using ComponentsWebAssembly_CSharp.Server.Models; #endif -namespace Company.WebApplication1; +namespace ComponentsWebAssembly_CSharp; public class Program { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.Main.cs index ec1af1a7e9c6..04fc97d104f1 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.Main.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/GrpcService-CSharp/Program.Main.cs @@ -1,6 +1,6 @@ using GrpcService_CSharp.Services; -namespace Company.WebApplication1; +namespace GrpcService_CSharp; public class Program { diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json index 5c9b53aedcd1..9cf6ab2fa69e 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/.template.config/template.json @@ -44,9 +44,11 @@ ] }, { - "condition": "(UseProgramMain && !UseMinimalAPIs)", + "condition": "(UseProgramMain)", "exclude": [ - "Program.cs" + "Program.cs", + "Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs", + "Program.MinimalAPIs.WindowsOrNoAuth.cs" ], "rename": { "Program.Main.cs": "Program.cs" @@ -55,13 +57,17 @@ { "condition": "(UseMinimalAPIs)", "exclude": [ - "Controllers/WeatherForecastController.cs", - "Program.cs", + "Controllers/WeatherForecastController.cs" + ] + }, + { + "condition": "(UseMinimalAPIs && !UseProgramMain)", + "exclude": [ "WeatherForecast.cs" ] }, { - "condition": "(UseMinimalAPIs && (NoAuth || WindowsAuth))", + "condition": "(!UseProgramMain && UseMinimalAPIs && (NoAuth || WindowsAuth))", "rename": { "Program.MinimalAPIs.WindowsOrNoAuth.cs": "Program.cs" }, @@ -70,7 +76,7 @@ ] }, { - "condition": "(UseMinimalAPIs && (IndividualAuth || OrganizationalAuth))", + "condition": "(!UseProgramMain && UseMinimalAPIs && (IndividualAuth || OrganizationalAuth))", "rename": { "Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs": "Program.cs" }, diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs index a0c9ad67e817..a882ad35c648 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.Main.cs @@ -10,6 +10,7 @@ #endif #if (OrganizationalAuth || IndividualB2CAuth) using Microsoft.Identity.Web; +using Microsoft.Identity.Web.Resource; #endif #if (OrganizationalAuth || IndividualB2CAuth || GenerateGraph || WindowsAuth) @@ -20,76 +21,168 @@ public class Program { public static void Main(string[] args) { - var builder = WebApplication.CreateBuilder(args); - - // Add services to the container. - #if (OrganizationalAuth) - builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - #if (GenerateApiOrGraph) - .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")) - .EnableTokenAcquisitionToCallDownstreamApi() - #if (GenerateApi) - .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi")) - #endif - #if (GenerateGraph) - .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi")) - #endif - .AddInMemoryTokenCaches(); - #else - .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")); - #endif - #elif (IndividualB2CAuth) - builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) - #if (GenerateApi) - .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C")) - .EnableTokenAcquisitionToCallDownstreamApi() - .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi")) - .AddInMemoryTokenCaches(); - #else - .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C")); - #endif - #endif - - builder.Services.AddControllers(); - #if (EnableOpenAPI) - // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle - builder.Services.AddEndpointsApiExplorer(); - builder.Services.AddSwaggerGen(); - #endif - #if (WindowsAuth) - - builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) - .AddNegotiate(); - - builder.Services.AddAuthorization(options => - { - // By default, all incoming requests will be authorized according to the default policy. - options.FallbackPolicy = options.DefaultPolicy; - }); - #endif + var builder = WebApplication.CreateBuilder(args); - var app = builder.Build(); + // Add services to the container. + #if (OrganizationalAuth) + builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + #if (GenerateApiOrGraph) + .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")) + .EnableTokenAcquisitionToCallDownstreamApi() + #if (GenerateApi) + .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi")) + #endif + #if (GenerateGraph) + .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi")) + #endif + .AddInMemoryTokenCaches(); + #else + .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")); + #endif + #elif (IndividualB2CAuth) + builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + #if (GenerateApi) + .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C")) + .EnableTokenAcquisitionToCallDownstreamApi() + .AddDownstreamWebApi("DownstreamApi", builder.Configuration.GetSection("DownstreamApi")) + .AddInMemoryTokenCaches(); + #else + .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAdB2C")); + #endif + #endif + #if (UseMinimalAPIs) + builder.Services.AddAuthorization(); + #endif - // Configure the HTTP request pipeline. - #if (EnableOpenAPI) - if (app.Environment.IsDevelopment()) - { - app.UseSwagger(); - app.UseSwaggerUI(); - } - #endif - #if (RequiresHttps) + #if (UseControllers) + builder.Services.AddControllers(); + #endif + #if (EnableOpenAPI) + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(); + #endif + #if (WindowsAuth) + + builder.Services.AddAuthentication(NegotiateDefaults.AuthenticationScheme) + .AddNegotiate(); + + builder.Services.AddAuthorization(options => + { + // By default, all incoming requests will be authorized according to the default policy. + options.FallbackPolicy = options.DefaultPolicy; + }); + #endif + + var app = builder.Build(); + + // Configure the HTTP request pipeline. + #if (EnableOpenAPI) + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + #endif + #if (RequiresHttps) + + app.UseHttpsRedirection(); + #endif + + #if (OrganizationalAuth || IndividualAuth || WindowsAuth) + app.UseAuthentication(); + #endif + app.UseAuthorization(); + + #if (UseMinimalAPIs) + #if (OrganizationalAuth || IndividualB2CAuth) + var scopeRequiredByApi = app.Configuration["AzureAd:Scopes"] ?? ""; + #endif + var summaries = new[] + { + "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" + }; + + #if (GenerateApi) + app.MapGet("/weatherforecast", async (HttpContext httpContext, IDownstreamWebApi downstreamWebApi) => + { + httpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); + + using var response = await downstreamWebApi.CallWebApiForUserAsync("DownstreamApi").ConfigureAwait(false); + if (response.StatusCode == System.Net.HttpStatusCode.OK) + { + var apiResult = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + // Do something + } + else + { + var error = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + throw new HttpRequestException($"Invalid status code in the HttpResponseMessage: {response.StatusCode}: {error}"); + } + + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }) + .ToArray(); + + return forecast; + #elif (GenerateGraph) + app.MapGet("/weatherforecast", async (HttpContext httpContext, Graph.GraphServiceClient graphServiceClient) => + { + httpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); + + var user = await graphServiceClient.Me.Request().GetAsync(); + + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }) + .ToArray(); - app.UseHttpsRedirection(); - #endif + return forecast; + #else + app.MapGet("/weatherforecast", (HttpContext httpContext) => + { + #if (OrganizationalAuth || IndividualB2CAuth) + httpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); - #if (OrganizationalAuth || IndividualAuth || WindowsAuth) - app.UseAuthentication(); - #endif - app.UseAuthorization(); + #endif + var forecast = Enumerable.Range(1, 5).Select(index => + new WeatherForecast + { + Date = DateTime.Now.AddDays(index), + TemperatureC = Random.Shared.Next(-20, 55), + Summary = summaries[Random.Shared.Next(summaries.Length)] + }) + .ToArray(); + return forecast; + #endif + #if (EnableOpenAPI && !NoAuth) + }) + .WithName("GetWeatherForecast") + .RequireAuthorization(); + #elif (EnableOpenAPI && NoAuth) + }) + .WithName("GetWeatherForecast"); + #elif (!EnableOpenAPI && !NoAuth) + }) + .RequireAuthorization(); + #else + }); + #endif + #endif + #if (UseControllers) - app.MapControllers(); + app.MapControllers(); + #endif - app.Run(); + app.Run(); } } diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs index 2329d42dafe3..c83adb9d8201 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.OrgOrIndividualB2CAuth.cs @@ -64,14 +64,14 @@ app.UseAuthentication(); app.UseAuthorization(); -var scopeRequiredByApi = app.Configuration["AzureAd:Scopes"]; +var scopeRequiredByApi = app.Configuration["AzureAd:Scopes"] ?? ""; var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; #if (GenerateApi) -app.MapGet("/weatherforecast", (HttpContext httpContext, IDownstreamWebApi downstreamWebApi) => +app.MapGet("/weatherforecast", async (HttpContext httpContext, IDownstreamWebApi downstreamWebApi) => { httpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); @@ -97,13 +97,12 @@ .ToArray(); return forecast; -}) -#elseif (GenerateGraph) -app.MapGet("/weahterforecast", (HttpContext httpContext, GraphServiceClient graphServiceClient) => +#elif (GenerateGraph) +app.MapGet("/weatherforecast", async (HttpContext httpContext, Graph.GraphServiceClient graphServiceClient) => { httpContext.VerifyUserHasAnyAcceptedScope(scopeRequiredByApi); - var user = await _graphServiceClient.Me.Request().GetAsync(); + var user = await graphServiceClient.Me.Request().GetAsync(); var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast @@ -115,7 +114,6 @@ .ToArray(); return forecast; -}) #else app.MapGet("/weatherforecast", (HttpContext httpContext) => { @@ -145,4 +143,4 @@ record WeatherForecast(DateTime Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); -} \ No newline at end of file +} diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.WindowsOrNoAuth.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.WindowsOrNoAuth.cs index 8b8e2b81afd0..77eb04f48ac8 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.WindowsOrNoAuth.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/WebApi-CSharp/Program.MinimalAPIs.WindowsOrNoAuth.cs @@ -37,6 +37,7 @@ #endif #if (WindowsAuth) app.UseAuthentication(); +app.UseAuthorization(); #endif var summaries = new[] diff --git a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/Program.Main.cs b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/Program.Main.cs index d69747f38d51..10fac97af3ec 100644 --- a/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/Program.Main.cs +++ b/src/ProjectTemplates/Web.ProjectTemplates/content/Worker-CSharp/Program.Main.cs @@ -1,6 +1,4 @@ -using Company.Application1; - -namespace Company.WebApplication1; +namespace Company.Application1; public class Program { diff --git a/src/ProjectTemplates/scripts/Run-Angular-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Angular-Locally.ps1 index 95de6568eab1..30e5329e5efa 100644 --- a/src/ProjectTemplates/scripts/Run-Angular-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-Angular-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "angular" "angular" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "angular" "angular" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-AngularProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-AngularProgramMain-Locally.ps1 index 93127bb08be0..1db52862a374 100644 --- a/src/ProjectTemplates/scripts/Run-AngularProgramMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-AngularProgramMain-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "angular" "angular --use-program-main" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "angular" "angular --use-program-main" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-Blazor-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Blazor-Locally.ps1 index 7a510a7a9a45..1b0a84aaae62 100644 --- a/src/ProjectTemplates/scripts/Run-Blazor-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-Blazor-Locally.ps1 @@ -10,4 +10,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "blazorserver" "blazorserver" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "blazorserver" "blazorserver" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-BlazorProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-BlazorProgramMain-Locally.ps1 index 3d9fdd64a70d..c06d9f83aa80 100644 --- a/src/ProjectTemplates/scripts/Run-BlazorProgramMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-BlazorProgramMain-Locally.ps1 @@ -10,4 +10,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "blazorserver" "blazorserver --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "blazorserver" "blazorserver --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-BlazorWasm-Locally.ps1 b/src/ProjectTemplates/scripts/Run-BlazorWasm-Locally.ps1 index 50f70bb1b04d..5945a9fd00bd 100644 --- a/src/ProjectTemplates/scripts/Run-BlazorWasm-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-BlazorWasm-Locally.ps1 @@ -10,4 +10,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "blazorwasm" "blazorwasm --hosted --auth Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $true +Test-Template "blazorwasm" "blazorwasm --hosted --auth Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $true diff --git a/src/ProjectTemplates/scripts/Run-BlazorWasmProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-BlazorWasmProgramMain-Locally.ps1 index 7c8755a8bba3..d7541fe5505f 100644 --- a/src/ProjectTemplates/scripts/Run-BlazorWasmProgramMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-BlazorWasmProgramMain-Locally.ps1 @@ -10,4 +10,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "blazorwasm" "blazorwasm --use-program-main --hosted --auth Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $true +Test-Template "blazorwasm" "blazorwasm --use-program-main --hosted --auth Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $true diff --git a/src/ProjectTemplates/scripts/Run-EmptyWeb-Locally.ps1 b/src/ProjectTemplates/scripts/Run-EmptyWeb-Locally.ps1 index 10e743504b58..ad97e7ed4e3e 100644 --- a/src/ProjectTemplates/scripts/Run-EmptyWeb-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-EmptyWeb-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "web" "web" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "web" "web" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-EmptyWebProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-EmptyWebProgramMain-Locally.ps1 index 7453063baf21..d2a4b5f55bf1 100644 --- a/src/ProjectTemplates/scripts/Run-EmptyWebProgramMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-EmptyWebProgramMain-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "web" "web --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "web" "web --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-Razor-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Razor-Locally.ps1 index 6f16590be8fe..8d562fe763c2 100644 --- a/src/ProjectTemplates/scripts/Run-Razor-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-Razor-Locally.ps1 @@ -6,4 +6,4 @@ param() . $PSScriptRoot\Test-Template.ps1 -Test-Template "webapp" "webapp -au Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "webapp" "webapp -au Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-RazorProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-RazorProgramMain-Locally.ps1 index 4224cf985dd2..bfc7748ccdd4 100644 --- a/src/ProjectTemplates/scripts/Run-RazorProgramMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-RazorProgramMain-Locally.ps1 @@ -6,4 +6,4 @@ param() . $PSScriptRoot\Test-Template.ps1 -Test-Template "webapp" "webapp -au Individual --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "webapp" "webapp -au Individual --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-React-Locally.ps1 b/src/ProjectTemplates/scripts/Run-React-Locally.ps1 index a4baf0c40568..c09e8c1fc624 100644 --- a/src/ProjectTemplates/scripts/Run-React-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-React-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "react" "react" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "react" "react" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-ReactProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-ReactProgramMain-Locally.ps1 index df61a5a11740..b05f556fc17f 100644 --- a/src/ProjectTemplates/scripts/Run-ReactProgramMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-ReactProgramMain-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "react" "react --use-program-main" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "react" "react --use-program-main" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-ReactRedux-Locally.ps1 b/src/ProjectTemplates/scripts/Run-ReactRedux-Locally.ps1 index aedd8ec88acb..df02bed62438 100644 --- a/src/ProjectTemplates/scripts/Run-ReactRedux-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-ReactRedux-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "reactredux" "reactredux" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "reactredux" "reactredux" "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-Starterweb-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Starterweb-Locally.ps1 index ab195cd016ef..6d6afa43a39c 100644 --- a/src/ProjectTemplates/scripts/Run-Starterweb-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-Starterweb-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "mvc" "mvc -au Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "mvc" "mvc -au Individual" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-StarterwebProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-StarterwebProgramMain-Locally.ps1 index 076106d3e861..4965829b3904 100644 --- a/src/ProjectTemplates/scripts/Run-StarterwebProgramMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-StarterwebProgramMain-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "mvc" "mvc -au Individual --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "mvc" "mvc -au Individual --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-WebApi-Locally.ps1 b/src/ProjectTemplates/scripts/Run-WebApi-Locally.ps1 index 467d6e57e76e..cd866e4e355e 100644 --- a/src/ProjectTemplates/scripts/Run-WebApi-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-WebApi-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "webapi" "webapi" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "webapi" "webapi" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-WebApiMinimal-Locally.ps1 b/src/ProjectTemplates/scripts/Run-WebApiMinimal-Locally.ps1 index 19325a7c7eca..5afe6c0d586f 100644 --- a/src/ProjectTemplates/scripts/Run-WebApiMinimal-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-WebApiMinimal-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "webapimin" "webapi -minimal" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "webapimin" "webapi -minimal" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-WebApiProgamMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-WebApiProgamMain-Locally.ps1 index 41f794b7eaaf..045d249e9aa3 100644 --- a/src/ProjectTemplates/scripts/Run-WebApiProgamMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-WebApiProgamMain-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "webapi" "webapi --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "webapi" "webapi --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-WebApiProgamMainMinimal-Locally.ps1 b/src/ProjectTemplates/scripts/Run-WebApiProgamMainMinimal-Locally.ps1 new file mode 100644 index 000000000000..c8b66bdcc0ad --- /dev/null +++ b/src/ProjectTemplates/scripts/Run-WebApiProgamMainMinimal-Locally.ps1 @@ -0,0 +1,12 @@ +#!/usr/bin/env pwsh +#requires -version 4 + +[CmdletBinding(PositionalBinding = $false)] +param() + +Set-StrictMode -Version 2 +$ErrorActionPreference = 'Stop' + +. $PSScriptRoot\Test-Template.ps1 + +Test-Template "webapi" "webapi --use-program-main --use-minimal-apis" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-Worker-Locally.ps1 b/src/ProjectTemplates/scripts/Run-Worker-Locally.ps1 index 9f3c0272096d..c201a4fa2e73 100644 --- a/src/ProjectTemplates/scripts/Run-Worker-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-Worker-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "worker" "worker" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "worker" "worker" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-WorkerProgramMain-Locally.ps1 b/src/ProjectTemplates/scripts/Run-WorkerProgramMain-Locally.ps1 index 9e0aa3d4607b..2852882312c5 100644 --- a/src/ProjectTemplates/scripts/Run-WorkerProgramMain-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-WorkerProgramMain-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "worker" "worker --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "worker" "worker --use-program-main" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/scripts/Run-gRPC-Locally.ps1 b/src/ProjectTemplates/scripts/Run-gRPC-Locally.ps1 index 2803a34d9c9c..21950ac78920 100644 --- a/src/ProjectTemplates/scripts/Run-gRPC-Locally.ps1 +++ b/src/ProjectTemplates/scripts/Run-gRPC-Locally.ps1 @@ -9,4 +9,4 @@ $ErrorActionPreference = 'Stop' . $PSScriptRoot\Test-Template.ps1 -Test-Template "grpc" "grpc" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.0-dev.nupkg" $false +Test-Template "grpc" "grpc" "Microsoft.DotNet.Web.ProjectTemplates.6.0.6.0.6.nupkg" $false diff --git a/src/ProjectTemplates/test/ArgConstants.cs b/src/ProjectTemplates/test/ArgConstants.cs new file mode 100644 index 000000000000..94a3b2965cce --- /dev/null +++ b/src/ProjectTemplates/test/ArgConstants.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Templates.Test; + +internal static class ArgConstants +{ + public const string UseProgramMain = "--use-program-main"; + public const string UseMinimalApis = "--use-minimal-apis"; + public const string Hosted = "--hosted"; + public const string Pwa = "--pwa"; + public const string CallsGraph = "--calls-graph"; + public const string CalledApiUrl = "--called-api-url"; + public const string CalledApiUrlGraphMicrosoftCom = "--called-api-url \"https://graph.microsoft.com\""; + public const string CalledApiScopes = "--called-api-scopes"; + public const string CalledApiScopesUserReadWrite = $"{CalledApiScopes} user.readwrite"; + public const string NoOpenApi = "--no-openapi"; + public const string Auth = "-au"; + public const string ClientId = "--client-id"; + public const string Domain = "--domain"; + public const string DefaultScope = "--default-scope"; + public const string AppIdUri = "--app-id-uri"; + public const string AppIdClientId = "--api-client-id"; + public const string TenantId = "--tenant-id"; + public const string AadB2cInstance = "--aad-b2c-instance"; + public const string UseLocalDb = "-uld"; +} diff --git a/src/ProjectTemplates/test/AssemblyInfo.AssemblyFixtures.cs b/src/ProjectTemplates/test/AssemblyInfo.AssemblyFixtures.cs index 7e531ec57ea6..3422eecd8c53 100644 --- a/src/ProjectTemplates/test/AssemblyInfo.AssemblyFixtures.cs +++ b/src/ProjectTemplates/test/AssemblyInfo.AssemblyFixtures.cs @@ -3,5 +3,7 @@ using Microsoft.AspNetCore.Testing; using Templates.Test.Helpers; +using Xunit; [assembly: AssemblyFixture(typeof(ProjectFactoryFixture))] +[assembly: CollectionBehavior(DisableTestParallelization = true)] \ No newline at end of file diff --git a/src/ProjectTemplates/test/BaselineTest.cs b/src/ProjectTemplates/test/BaselineTest.cs index 786b7d36dc6b..f98f69b4f0d7 100644 --- a/src/ProjectTemplates/test/BaselineTest.cs +++ b/src/ProjectTemplates/test/BaselineTest.cs @@ -67,11 +67,12 @@ public ITestOutputHelper Output } // This test should generally not be quarantined as it only is checking that the expected files are on disk + // and that the namespace declarations in the generated .cs files start with the project name [Theory] [MemberData(nameof(TemplateBaselines))] public async Task Template_Produces_The_Right_Set_Of_FilesAsync(string arguments, string[] expectedFiles) { - Project = await ProjectFactory.GetOrCreateProject(CreateProjectKey(arguments), Output); + Project = await ProjectFactory.CreateProject(Output); var createResult = await Project.RunDotNetNewRawAsync(arguments); Assert.True(createResult.ExitCode == 0, createResult.GetFormattedOutput()); @@ -100,66 +101,22 @@ public async Task Template_Produces_The_Right_Set_Of_FilesAsync(string arguments continue; } Assert.Contains(relativePath, expectedFiles); - } - } - - private static ConcurrentDictionary _projectKeys = new(); - - private string CreateProjectKey(string arguments) - { - var text = "baseline"; - - // Turn string like "new templatename -minimal -au SingleOrg --another-option OptionValue" - // into array like [ "new templatename", "minimal", "au SingleOrg", "another-option OptionValue" ] - var argumentsArray = arguments - .Split(new[] { " --", " -" }, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries) - .ToArray(); - // Add template name, value has form of "new name" - text += argumentsArray[0].Substring("new ".Length); - - // Sort arguments to ensure definitions that differ only by arguments order are caught - Array.Sort(argumentsArray, StringComparer.Ordinal); - - foreach (var argValue in argumentsArray) - { - var argSegments = argValue.Split(' ', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); - - if (argSegments.Length == 0) - { - continue; - } - else if (argSegments.Length == 1) - { - text += argSegments[0] switch - { - "ho" => "hosted", - "p" => "pwa", - _ => argSegments[0].Replace("-","") - }; - } - else + // Commented out to see if it impacts the Helix test failures + if (relativePath.EndsWith(".cs", StringComparison.Ordinal)) { - text += argSegments[0] switch + var namespaceDeclarationPrefix = "namespace "; + var namespaceDeclaration = File.ReadLines(file) + .SingleOrDefault(line => line.StartsWith(namespaceDeclarationPrefix, StringComparison.Ordinal)) + ?.Substring(namespaceDeclarationPrefix.Length); + + // nullable because Program.cs with top-level statements doesn't have a namespace declaration + if (namespaceDeclaration is not null) { - "au" => argSegments[1], - "uld" => "uld", - "language" => argSegments[1].Replace("#", "Sharp"), - "support-pages-and-views" when argSegments[1] == "true" => "supportpagesandviewstrue", - _ => "" - }; + Assert.StartsWith(Project.ProjectName, namespaceDeclaration, StringComparison.Ordinal); + } } } - - if (!_projectKeys.TryAdd(text, null)) - { - throw new InvalidOperationException( - $"Project key for template with args '{arguments}' already exists. " + - $"Check that the metadata specified in {BaselineDefinitionFileResourceName} is correct and that " + - $"the {nameof(CreateProjectKey)} method is considering enough template arguments to ensure uniqueness."); - } - - return text; } private void AssertFileExists(string basePath, string path, bool shouldExist) diff --git a/src/ProjectTemplates/test/BlazorServerTemplateTest.cs b/src/ProjectTemplates/test/BlazorServerTemplateTest.cs index 104f2304020d..6f277abd3a74 100644 --- a/src/ProjectTemplates/test/BlazorServerTemplateTest.cs +++ b/src/ProjectTemplates/test/BlazorServerTemplateTest.cs @@ -24,30 +24,37 @@ public BlazorServerTemplateTest(ProjectFactoryFixture projectFactory) public override string ProjectType { get; } = "blazorserver"; [Fact] - public Task BlazorServerTemplateWorks_NoAuth() => CreateBuildPublishAsync("blazorservernoauth"); + public Task BlazorServerTemplateWorks_NoAuth() => CreateBuildPublishAsync(); [Fact] - public Task BlazorServerTemplateWorks_ProgamMainNoAuth() => CreateBuildPublishAsync("blazorservernoauth", args: new [] { "--use-program-main" }); + public Task BlazorServerTemplateWorks_ProgamMainNoAuth() => CreateBuildPublishAsync(args: new [] { ArgConstants.UseProgramMain }); - [Theory] - [InlineData(true, null)] - [InlineData(true, new string[] { "--use-program-main" })] - [InlineData(false, null)] - [InlineData(false, new string[] { "--use-program-main" })] + [ConditionalTheory] + [InlineData("Individual", null)] + [InlineData("Individual", new string[] { ArgConstants.UseProgramMain })] [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/30825", Queues = "All.OSX")] - public Task BlazorServerTemplateWorks_IndividualAuth(bool useLocalDB, string[] args) => CreateBuildPublishAsync("blazorserverindividual" + (useLocalDB ? "uld" : "", args: args)); + public Task BlazorServerTemplateWorks_IndividualAuth(string auth, string[] args) => CreateBuildPublishAsync(auth, args: args); + + [ConditionalTheory] + [InlineData("Individual", new string[] { ArgConstants.UseLocalDb })] + [InlineData("Individual", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseLocalDb })] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "No LocalDb on non-Windows")] + public Task BlazorServerTemplateWorks_IndividualAuth_LocalDb(string auth, string[] args) => CreateBuildPublishAsync(auth, args: args); [Theory] [InlineData("IndividualB2C", null)] - [InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("IndividualB2C", new string[] { "--use-program-main", "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("SingleOrg", null)] - [InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("SingleOrg", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("SingleOrg", new string[] { "--calls-graph" })] - [InlineData("SingleOrg", new string[] { "--use-program-main --calls-graph" })] - public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish(string auth, string[] args) - => CreateBuildPublishAsync("blazorserveridweb" + Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant(), auth, args); + [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain })] + [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish_IndividualB2C(string auth, string[] args) => CreateBuildPublishAsync(auth, args); + [Theory] + [InlineData("SingleOrg", null)] + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain })] + [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph })] + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })] + public Task BlazorServerTemplate_IdentityWeb_BuildAndPublish_SingleOrg(string auth, string[] args) => CreateBuildPublishAsync(auth, args); } } diff --git a/src/ProjectTemplates/test/BlazorTemplateTest.cs b/src/ProjectTemplates/test/BlazorTemplateTest.cs index 792e9fd24750..b6096de7ce63 100644 --- a/src/ProjectTemplates/test/BlazorTemplateTest.cs +++ b/src/ProjectTemplates/test/BlazorTemplateTest.cs @@ -41,12 +41,12 @@ public ITestOutputHelper Output public abstract string ProjectType { get; } - protected async Task CreateBuildPublishAsync(string projectName, string auth = null, string[] args = null, string targetFramework = null, bool serverProject = false, bool onlyCreate = false) + protected async Task CreateBuildPublishAsync(string auth = null, string[] args = null, string targetFramework = null, bool serverProject = false, bool onlyCreate = false) { // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - var project = await ProjectFactory.GetOrCreateProject(projectName, Output); + var project = await ProjectFactory.CreateProject(Output); if (targetFramework != null) { project.TargetFramework = targetFramework; diff --git a/src/ProjectTemplates/test/BlazorWasmTemplateTest.cs b/src/ProjectTemplates/test/BlazorWasmTemplateTest.cs index dae6148d213a..fc9bfa5f8e4a 100644 --- a/src/ProjectTemplates/test/BlazorWasmTemplateTest.cs +++ b/src/ProjectTemplates/test/BlazorWasmTemplateTest.cs @@ -27,7 +27,7 @@ public BlazorWasmTemplateTest(ProjectFactoryFixture projectFactory) [Fact] public async Task BlazorWasmStandaloneTemplateCanCreateBuildPublish() { - var project = await CreateBuildPublishAsync("blazorstandalone"); + var project = await CreateBuildPublishAsync(); // The service worker assets manifest isn't generated for non-PWA projects var publishDir = Path.Combine(project.TemplatePublishDir, "wwwroot"); @@ -35,18 +35,18 @@ public async Task BlazorWasmStandaloneTemplateCanCreateBuildPublish() } [Fact] - public Task BlazorWasmHostedTemplateCanCreateBuildPublish() => CreateBuildPublishAsync("blazorhosted", args: new[] { "--hosted" }, serverProject: true); + public Task BlazorWasmHostedTemplateCanCreateBuildPublish() => CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted }, serverProject: true); [Fact] - public Task BlazorWasmHostedTemplateWithProgamMainCanCreateBuildPublish() => CreateBuildPublishAsync("blazorhosted", args: new[] { "--use-program-main", "--hosted" }, serverProject: true); + public Task BlazorWasmHostedTemplateWithProgamMainCanCreateBuildPublish() => CreateBuildPublishAsync(args: new[] { ArgConstants.UseProgramMain, ArgConstants.Hosted }, serverProject: true); [Fact] - public Task BlazorWasmStandalonePwaTemplateCanCreateBuildPublish() => CreateBuildPublishAsync("blazorstandalonepwa", args: new[] { "--pwa" }); + public Task BlazorWasmStandalonePwaTemplateCanCreateBuildPublish() => CreateBuildPublishAsync(args: new[] { ArgConstants.Pwa }); [Fact] public async Task BlazorWasmHostedPwaTemplateCanCreateBuildPublish() { - var project = await CreateBuildPublishAsync("blazorhostedpwa", args: new[] { "--hosted", "--pwa" }, serverProject: true); + var project = await CreateBuildPublishAsync(args: new[] { ArgConstants.Hosted, ArgConstants.Pwa }, serverProject: true); var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); @@ -81,20 +81,32 @@ private void ValidatePublishedServiceWorker(Project project) // LocalDB doesn't work on non Windows platforms [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB() - => BlazorWasmHostedTemplate_IndividualAuth_Works(true); + => BlazorWasmHostedTemplate_IndividualAuth_Works(true, false); [ConditionalFact] [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")] public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB() - => BlazorWasmHostedTemplate_IndividualAuth_Works(false); + => BlazorWasmHostedTemplate_IndividualAuth_Works(false, false); - private async Task CreateBuildPublishIndividualAuthProject(bool useLocalDb) + [ConditionalFact] + [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")] + // LocalDB doesn't work on non Windows platforms + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX)] + public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithLocalDB_ProgramMain() + => BlazorWasmHostedTemplate_IndividualAuth_Works(true, true); + + [ConditionalFact] + [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/34554", Queues = "Windows.10.Arm64v8.Open")] + public Task BlazorWasmHostedTemplate_IndividualAuth_Works_WithOutLocalDB_ProgramMain() + => BlazorWasmHostedTemplate_IndividualAuth_Works(false, true); + + private async Task CreateBuildPublishIndividualAuthProject(bool useLocalDb, bool useProgramMain = false) { // Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278 Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true"); - var project = await CreateBuildPublishAsync("blazorhostedindividual" + (useLocalDb ? "uld" : ""), - args: new[] { "--hosted", "-au", "Individual", useLocalDb ? "-uld" : "" }); + var project = await CreateBuildPublishAsync("Individual", + args: new[] { ArgConstants.Hosted, useLocalDb ? ArgConstants.UseLocalDb : "", useProgramMain ? ArgConstants.UseProgramMain : "" }); var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); @@ -128,9 +140,9 @@ private async Task CreateBuildPublishIndividualAuthProject(bool useLoca return project; } - private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(bool useLocalDb) + private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(bool useLocalDb, bool useProgramMain) { - var project = await CreateBuildPublishIndividualAuthProject(useLocalDb: useLocalDb); + var project = await CreateBuildPublishIndividualAuthProject(useLocalDb: useLocalDb, useProgramMain: useProgramMain); var serverProject = GetSubProject(project, "Server", $"{project.ProjectName}.Server"); } @@ -138,71 +150,136 @@ private async Task BlazorWasmHostedTemplate_IndividualAuth_Works(bool useLocalDb [Fact] public async Task BlazorWasmStandaloneTemplate_IndividualAuth_CreateBuildPublish() { - var project = await CreateBuildPublishAsync("blazorstandaloneindividual", args: new[] { - "-au", - "Individual", + var project = await CreateBuildPublishAsync("Individual", args: new[] { "--authority", "https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration", - "--client-id", + ArgConstants.ClientId, "sample-client-id" }); } - public static TheoryData TemplateData => new TheoryData + public static TheoryData TemplateDataIndividualB2C => new TheoryData { new TemplateInstance( "blazorwasmhostedaadb2c", "-ho", - "-au", "IndividualB2C", - "--aad-b2c-instance", "example.b2clogin.com", + ArgConstants.Auth, "IndividualB2C", + ArgConstants.AadB2cInstance, "example.b2clogin.com", "-ssp", "b2c_1_siupin", - "--client-id", "clientId", - "--domain", "my-domain", - "--default-scope", "full", - "--app-id-uri", "ApiUri", - "--api-client-id", "1234123413241324"), + ArgConstants.ClientId, "clientId", + ArgConstants.Domain, "my-domain", + ArgConstants.DefaultScope, "full", + ArgConstants.AppIdUri, "ApiUri", + ArgConstants.AppIdClientId, "1234123413241324"), + new TemplateInstance( + "blazorwasmhostedaadb2c_program_main", "-ho", + ArgConstants.Auth, "IndividualB2C", + ArgConstants.AadB2cInstance, "example.b2clogin.com", + "-ssp", "b2c_1_siupin", + ArgConstants.ClientId, "clientId", + ArgConstants.Domain, "my-domain", + ArgConstants.DefaultScope, "full", + ArgConstants.AppIdUri, "ApiUri", + ArgConstants.AppIdClientId, "1234123413241324", + ArgConstants.UseProgramMain), + new TemplateInstance( + "blazorwasmstandaloneaadb2c", + ArgConstants.Auth, "IndividualB2C", + ArgConstants.AadB2cInstance, "example.b2clogin.com", + "-ssp", "b2c_1_siupin", + ArgConstants.ClientId, "clientId", + ArgConstants.Domain, "my-domain"), + new TemplateInstance( + "blazorwasmstandaloneaadb2c_program_main", + ArgConstants.Auth, "IndividualB2C", + ArgConstants.AadB2cInstance, "example.b2clogin.com", + "-ssp", "b2c_1_siupin", + ArgConstants.ClientId, "clientId", + ArgConstants.Domain, "my-domain", + ArgConstants.UseProgramMain), + }; + + public static TheoryData TemplateDataSingleOrg => new TheoryData + { new TemplateInstance( "blazorwasmhostedaad", "-ho", - "-au", "SingleOrg", - "--domain", "my-domain", - "--tenant-id", "tenantId", - "--client-id", "clientId", - "--default-scope", "full", - "--app-id-uri", "ApiUri", - "--api-client-id", "1234123413241324"), + ArgConstants.Auth, "SingleOrg", + ArgConstants.Domain, "my-domain", + ArgConstants.TenantId, "tenantId", + ArgConstants.ClientId, "clientId", + ArgConstants.DefaultScope, "full", + ArgConstants.AppIdUri, "ApiUri", + ArgConstants.AppIdClientId, "1234123413241324"), new TemplateInstance( "blazorwasmhostedaadgraph", "-ho", - "-au", "SingleOrg", - "--calls-graph", - "--domain", "my-domain", - "--tenant-id", "tenantId", - "--client-id", "clientId", - "--default-scope", "full", - "--app-id-uri", "ApiUri", - "--api-client-id", "1234123413241324"), + ArgConstants.Auth, "SingleOrg", + ArgConstants.CallsGraph, + ArgConstants.Domain, "my-domain", + ArgConstants.TenantId, "tenantId", + ArgConstants.ClientId, "clientId", + ArgConstants.DefaultScope, "full", + ArgConstants.AppIdUri, "ApiUri", + ArgConstants.AppIdClientId, "1234123413241324"), new TemplateInstance( "blazorwasmhostedaadapi", "-ho", - "-au", "SingleOrg", - "--called-api-url", "\"https://graph.microsoft.com\"", - "--called-api-scopes", "user.readwrite", - "--domain", "my-domain", - "--tenant-id", "tenantId", - "--client-id", "clientId", - "--default-scope", "full", - "--app-id-uri", "ApiUri", - "--api-client-id", "1234123413241324"), - new TemplateInstance( - "blazorwasmstandaloneaadb2c", - "-au", "IndividualB2C", - "--aad-b2c-instance", "example.b2clogin.com", - "-ssp", "b2c_1_siupin", - "--client-id", "clientId", - "--domain", "my-domain"), + ArgConstants.Auth, "SingleOrg", + ArgConstants.CalledApiUrl, "\"https://graph.microsoft.com\"", + ArgConstants.CalledApiScopes, "user.readwrite", + ArgConstants.Domain, "my-domain", + ArgConstants.TenantId, "tenantId", + ArgConstants.ClientId, "clientId", + ArgConstants.DefaultScope, "full", + ArgConstants.AppIdUri, "ApiUri", + ArgConstants.AppIdClientId, "1234123413241324"), new TemplateInstance( "blazorwasmstandaloneaad", - "-au", "SingleOrg", - "--domain", "my-domain", - "--tenant-id", "tenantId", - "--client-id", "clientId"), + ArgConstants.Auth, "SingleOrg", + ArgConstants.Domain, "my-domain", + ArgConstants.TenantId, "tenantId", + ArgConstants.ClientId, "clientId"), + }; + + public static TheoryData TemplateDataSingleOrgProgramMain => new TheoryData + { + new TemplateInstance( + "blazorwasmhostedaad_program_main", "-ho", + ArgConstants.Auth, "SingleOrg", + ArgConstants.Domain, "my-domain", + ArgConstants.TenantId, "tenantId", + ArgConstants.ClientId, "clientId", + ArgConstants.DefaultScope, "full", + ArgConstants.AppIdUri, "ApiUri", + ArgConstants.AppIdClientId, "1234123413241324", + ArgConstants.UseProgramMain), + new TemplateInstance( + "blazorwasmhostedaadgraph_program_main", "-ho", + ArgConstants.Auth, "SingleOrg", + ArgConstants.CallsGraph, + ArgConstants.Domain, "my-domain", + ArgConstants.TenantId, "tenantId", + ArgConstants.ClientId, "clientId", + ArgConstants.DefaultScope, "full", + ArgConstants.AppIdUri, "ApiUri", + ArgConstants.AppIdClientId, "1234123413241324", + ArgConstants.UseProgramMain), + new TemplateInstance( + "blazorwasmhostedaadapi_program_main", "-ho", + ArgConstants.Auth, "SingleOrg", + ArgConstants.CalledApiUrl, "\"https://graph.microsoft.com\"", + ArgConstants.CalledApiScopes, "user.readwrite", + ArgConstants.Domain, "my-domain", + ArgConstants.TenantId, "tenantId", + ArgConstants.ClientId, "clientId", + ArgConstants.DefaultScope, "full", + ArgConstants.AppIdUri, "ApiUri", + ArgConstants.AppIdClientId, "1234123413241324", + ArgConstants.UseProgramMain), + new TemplateInstance( + "blazorwasmstandaloneaad_program_main", + ArgConstants.Auth, "SingleOrg", + ArgConstants.Domain, "my-domain", + ArgConstants.TenantId, "tenantId", + ArgConstants.ClientId, "clientId", + ArgConstants.UseProgramMain), }; public class TemplateInstance @@ -218,9 +295,19 @@ public TemplateInstance(string name, params string[] arguments) } [Theory] - [MemberData(nameof(TemplateData))] - public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_Works(TemplateInstance instance) - => CreateBuildPublishAsync(instance.Name, args: instance.Arguments, targetFramework: "netstandard2.1"); + [MemberData(nameof(TemplateDataIndividualB2C))] + public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_IndividualB2C_Works(TemplateInstance instance) + => CreateBuildPublishAsync(args: instance.Arguments, targetFramework: "netstandard2.1"); + + [Theory] + [MemberData(nameof(TemplateDataSingleOrg))] + public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_Works(TemplateInstance instance) + => CreateBuildPublishAsync(args: instance.Arguments, targetFramework: "netstandard2.1"); + + [Theory] + [MemberData(nameof(TemplateDataSingleOrgProgramMain))] + public Task BlazorWasmHostedTemplate_AzureActiveDirectoryTemplate_SingleOrg_ProgramMain_Works(TemplateInstance instance) + => CreateBuildPublishAsync(args: instance.Arguments, targetFramework: "netstandard2.1"); private string ReadFile(string basePath, string path) { diff --git a/src/ProjectTemplates/test/EmptyWebTemplateTest.cs b/src/ProjectTemplates/test/EmptyWebTemplateTest.cs index 7468a587b41e..e350f65cb583 100644 --- a/src/ProjectTemplates/test/EmptyWebTemplateTest.cs +++ b/src/ProjectTemplates/test/EmptyWebTemplateTest.cs @@ -42,7 +42,7 @@ public async Task EmptyWebTemplateCSharp() [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] public async Task EmptyWebTemplateProgramMainCSharp() { - await EmtpyTemplateCore(languageOverride: null, args: new [] { "--use-program-main" }); + await EmtpyTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain }); } [Fact] @@ -53,7 +53,7 @@ public async Task EmptyWebTemplateFSharp() private async Task EmtpyTemplateCore(string languageOverride, string[] args = null) { - var project = await ProjectFactory.GetOrCreateProject("empty" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output); + var project = await ProjectFactory.CreateProject(Output); var createResult = await project.RunDotNetNewAsync("web", args: args, language: languageOverride); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); diff --git a/src/ProjectTemplates/test/GrpcTemplateTest.cs b/src/ProjectTemplates/test/GrpcTemplateTest.cs index d26d0f8623a6..128ca22f4251 100644 --- a/src/ProjectTemplates/test/GrpcTemplateTest.cs +++ b/src/ProjectTemplates/test/GrpcTemplateTest.cs @@ -41,9 +41,9 @@ public ITestOutputHelper Output [InlineData(false)] public async Task GrpcTemplate(bool useProgramMain) { - var project = await ProjectFactory.GetOrCreateProject("grpc", Output); + var project = await ProjectFactory.CreateProject(Output); - var args = useProgramMain ? new [] { "--use-program-main" } : null; + var args = useProgramMain ? new [] { ArgConstants.UseProgramMain } : null; var createResult = await project.RunDotNetNewAsync("grpc", args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); diff --git a/src/ProjectTemplates/test/IdentityUIPackageTest.cs b/src/ProjectTemplates/test/IdentityUIPackageTest.cs index aa73178833a8..75b94f804a26 100644 --- a/src/ProjectTemplates/test/IdentityUIPackageTest.cs +++ b/src/ProjectTemplates/test/IdentityUIPackageTest.cs @@ -102,7 +102,7 @@ public ITestOutputHelper Output public async Task IdentityUIPackage_WorksWithDifferentOptions() { var packageOptions = new Dictionary(); - var project = await ProjectFactory.GetOrCreateProject("identityuipackage" + string.Concat(packageOptions.Values), Output); + var project = await ProjectFactory.CreateProject(Output); var useLocalDB = false; var createResult = await project.RunDotNetNewAsync("razor", auth: "Individual", useLocalDB: useLocalDB, environmentVariables: packageOptions); diff --git a/src/ProjectTemplates/test/ItemTemplateTests/BlazorServerTests.cs b/src/ProjectTemplates/test/ItemTemplateTests/BlazorServerTests.cs index 3b97cfdaff78..af904522a756 100644 --- a/src/ProjectTemplates/test/ItemTemplateTests/BlazorServerTests.cs +++ b/src/ProjectTemplates/test/ItemTemplateTests/BlazorServerTests.cs @@ -25,7 +25,7 @@ public BlazorServerTest(ProjectFactoryFixture projectFactory, ITestOutputHelper [Fact] public async Task BlazorServerItemTemplate() { - Project = await ProjectFactory.GetOrCreateProject("razorcomponentitem", Output); + Project = await ProjectFactory.CreateProject(Output); var createResult = await Project.RunDotNetNewAsync("razorcomponent --name Different"); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create", Project, createResult)); diff --git a/src/ProjectTemplates/test/MvcTemplateTest.cs b/src/ProjectTemplates/test/MvcTemplateTest.cs index a5d6bec38066..8984c7bbd7bd 100644 --- a/src/ProjectTemplates/test/MvcTemplateTest.cs +++ b/src/ProjectTemplates/test/MvcTemplateTest.cs @@ -45,11 +45,11 @@ public ITestOutputHelper Output [ConditionalFact] [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] - public async Task MvcTemplate_ProgramMainNoAuthCSharp() => await MvcTemplateCore(languageOverride: null, new [] { "--use-program-main" }); + public async Task MvcTemplate_ProgramMainNoAuthCSharp() => await MvcTemplateCore(languageOverride: null, new [] { ArgConstants.UseProgramMain }); private async Task MvcTemplateCore(string languageOverride, string[] args = null) { - var project = await ProjectFactory.GetOrCreateProject("mvcnoauth" + (languageOverride == "F#" ? "fsharp" : "csharp"), Output); + var project = await ProjectFactory.CreateProject(Output); var createResult = await project.RunDotNetNewAsync("mvc", language: languageOverride, args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); @@ -120,16 +120,23 @@ private async Task MvcTemplateCore(string languageOverride, string[] args = null } [ConditionalTheory] - [InlineData(true, false)] - [InlineData(true, true)] - [InlineData(false, false)] - [InlineData(false, true)] + [InlineData(false)] + [InlineData(true)] [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] - public async Task MvcTemplate_IndividualAuth(bool useLocalDB, bool useProgramMain) + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "No LocalDb on non-Windows")] + public Task MvcTemplate_IndividualAuth_LocalDb(bool useProgramMain) => MvcTemplate_IndividualAuth_Core(useLocalDB: true, useProgramMain); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)] + public Task MvcTemplate_IndividualAuth(bool useProgramMain) => MvcTemplate_IndividualAuth_Core(useLocalDB: false, useProgramMain); + + private async Task MvcTemplate_IndividualAuth_Core(bool useLocalDB, bool useProgramMain) { - var project = await ProjectFactory.GetOrCreateProject("mvcindividual" + (useLocalDB ? "uld" : ""), Output); + var project = await ProjectFactory.CreateProject(Output); - var args = useProgramMain ? new [] { "--use-program-main" } : null; + var args = useProgramMain ? new [] { ArgConstants.UseProgramMain } : null; var createResult = await project.RunDotNetNewAsync("mvc", auth: "Individual", useLocalDB: useLocalDB, args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); @@ -249,13 +256,13 @@ public async Task MvcTemplate_SingleFileExe() // This test verifies publishing an MVC app as a single file exe works. We'll limit testing // this to a few operating systems to make our lives easier. var runtimeIdentifer = "win-x64"; - var project = await ProjectFactory.GetOrCreateProject("mvcsinglefileexe", Output); + var project = await ProjectFactory.CreateProject(Output); project.RuntimeIdentifier = runtimeIdentifer; var createResult = await project.RunDotNetNewAsync("mvc"); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); - var publishResult = await project.RunDotNetPublishAsync(additionalArgs: $"/p:PublishSingleFile=true -r {runtimeIdentifer}", noRestore: false); + var publishResult = await project.RunDotNetPublishAsync(additionalArgs: $"/p:PublishSingleFile=true -r {runtimeIdentifer} --self-contained", noRestore: false); Assert.True(0 == publishResult.ExitCode, ErrorMessages.GetFailedProcessMessage("publish", project, publishResult)); var menuLinks = new[] @@ -292,15 +299,24 @@ public async Task MvcTemplate_SingleFileExe() [ConditionalTheory] [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] [InlineData("IndividualB2C", null)] - [InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] + [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain })] + [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + public Task MvcTemplate_IdentityWeb_IndividualB2C_BuildsAndPublishes(string auth, string[] args) => MvcTemplateBuildsAndPublishes(auth: auth, args: args); + + [ConditionalTheory] + [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] [InlineData("SingleOrg", null)] - [InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("SingleOrg", new string[] { "--calls-graph" })] - public Task MvcTemplate_IdentityWeb_BuildsAndPublishes(string auth, string[] args) => MvcTemplateBuildsAndPublishes(auth: auth, args: args); + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain })] + [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph })] + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })] + public Task MvcTemplate_IdentityWeb_SingleOrg_BuildsAndPublishes(string auth, string[] args) => MvcTemplateBuildsAndPublishes(auth: auth, args: args); private async Task MvcTemplateBuildsAndPublishes(string auth, string[] args) { - var project = await ProjectFactory.GetOrCreateProject("mvc" + Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant(), Output); + var project = await ProjectFactory.CreateProject(Output); var createResult = await project.RunDotNetNewAsync("mvc", auth: auth, args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); diff --git a/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj b/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj index 8a9e1fa661cd..9f3a2986b389 100644 --- a/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj +++ b/src/ProjectTemplates/test/ProjectTemplates.Tests.csproj @@ -19,6 +19,9 @@ TestTemplates\ $([MSBuild]::EnsureTrailingSlash('$(RepoRoot)'))obj\template-restore\ true + + $(HelixQueueArmDebian11); + diff --git a/src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs b/src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs index 4a547b2377fe..5c84f9135dec 100644 --- a/src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs +++ b/src/ProjectTemplates/test/RazorClassLibraryTemplateTest.cs @@ -33,7 +33,7 @@ public ITestOutputHelper Output [Fact] public async Task RazorClassLibraryTemplate_WithViews_Async() { - var project = await ProjectFactory.GetOrCreateProject("razorclasslibwithviews", Output); + var project = await ProjectFactory.CreateProject(Output); var createResult = await project.RunDotNetNewAsync("razorclasslib", args: new[] { "--support-pages-and-views", "true" }); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); @@ -53,7 +53,7 @@ public async Task RazorClassLibraryTemplate_WithViews_Async() [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] public async Task RazorClassLibraryTemplateAsync() { - var project = await ProjectFactory.GetOrCreateProject("razorclasslib", Output); + var project = await ProjectFactory.CreateProject(Output); var createResult = await project.RunDotNetNewAsync("razorclasslib"); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); diff --git a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs index 3412233f5dd5..ca1affdcadf1 100644 --- a/src/ProjectTemplates/test/RazorPagesTemplateTest.cs +++ b/src/ProjectTemplates/test/RazorPagesTemplateTest.cs @@ -40,9 +40,9 @@ public ITestOutputHelper Output [InlineData(false)] public async Task RazorPagesTemplate_NoAuth(bool useProgramMain) { - var project = await ProjectFactory.GetOrCreateProject("razorpagesnoauth", Output); + var project = await ProjectFactory.CreateProject(Output); - var args = useProgramMain ? new [] { "--use-program-main" } : null; + var args = useProgramMain ? new [] { ArgConstants.UseProgramMain } : null; var createResult = await project.RunDotNetNewAsync("razor", args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("razor", project, createResult)); @@ -107,16 +107,23 @@ public async Task RazorPagesTemplate_NoAuth(bool useProgramMain) } [ConditionalTheory] - [InlineData(false, false)] - [InlineData(false, true)] - [InlineData(true, false)] - [InlineData(true, true)] - [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] - public async Task RazorPagesTemplate_IndividualAuth(bool useLocalDB, bool useProgramMain) + [InlineData(false)] + [InlineData(true)] + [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)] + [OSSkipCondition(OperatingSystems.Linux | OperatingSystems.MacOSX, SkipReason = "No LocalDb on non-Windows")] + public Task RazorPagesTemplate_IndividualAuth_LocalDb(bool useProgramMain) => RazorPagesTemplate_IndividualAuth_Core(useLocalDB: true, useProgramMain); + + [ConditionalTheory] + [InlineData(false)] + [InlineData(true)] + [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64 + HelixConstants.DebianAmd64)] + public Task RazorPagesTemplate_IndividualAuth(bool useProgramMain) => RazorPagesTemplate_IndividualAuth_Core(useLocalDB: false, useProgramMain); + + private async Task RazorPagesTemplate_IndividualAuth_Core(bool useLocalDB, bool useProgramMain) { - var project = await ProjectFactory.GetOrCreateProject("razorpagesindividual" + (useLocalDB ? "uld" : ""), Output); + var project = await ProjectFactory.CreateProject(Output); - var args = useProgramMain ? new [] { "--use-program-main" } : null; + var args = useProgramMain ? new [] { ArgConstants.UseProgramMain } : null; var createResult = await project.RunDotNetNewAsync("razor", auth: "Individual", useLocalDB: useLocalDB, args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); @@ -231,21 +238,27 @@ public async Task RazorPagesTemplate_IndividualAuth(bool useLocalDB, bool usePro [ConditionalTheory] [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] [InlineData("IndividualB2C", null)] - [InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("IndividualB2C", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] + [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain })] + [InlineData("IndividualB2C", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("IndividualB2C", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + public Task RazorPagesTemplate_IdentityWeb_IndividualB2C_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args); + + [ConditionalTheory] + [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] [InlineData("SingleOrg", null)] - [InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("SingleOrg", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - public Task RazorPagesTemplate_IdentityWeb_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args); + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain })] + [InlineData("SingleOrg", new[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + public Task RazorPagesTemplate_IdentityWeb_SingleOrg_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args); [ConditionalTheory] - [InlineData("SingleOrg", new string[] { "--calls-graph" })] - [InlineData("SingleOrg", new string[] { "--use-program-main --calls-graph" })] - public Task RazorPagesTemplate_IdentityWeb_BuildsAndPublishes_WithSingleOrg(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args); + [InlineData("SingleOrg", new[] { ArgConstants.CallsGraph })] + [InlineData("SingleOrg", new[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })] + public Task RazorPagesTemplate_IdentityWeb_SingleOrg_CallsGraph_BuildsAndPublishes(string auth, string[] args) => BuildAndPublishRazorPagesTemplate(auth: auth, args: args); private async Task BuildAndPublishRazorPagesTemplate(string auth, string[] args) { - var project = await ProjectFactory.GetOrCreateProject("razorpages" + Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant(), Output); + var project = await ProjectFactory.CreateProject(Output); var createResult = await project.RunDotNetNewAsync("razor", auth: auth, args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); diff --git a/src/ProjectTemplates/test/SpaTemplatesTest.cs b/src/ProjectTemplates/test/SpaTemplatesTest.cs index c5271f7f7ebd..a990217047d5 100644 --- a/src/ProjectTemplates/test/SpaTemplatesTest.cs +++ b/src/ProjectTemplates/test/SpaTemplatesTest.cs @@ -35,13 +35,13 @@ public ITestOutputHelper Output } [Theory] - [InlineData("angularind", "angular", "Individual")] - [InlineData("reactind", "react", "Individual")] - [InlineData("angularnoauth", "angular", null)] - [InlineData("reactnoauth", "react", null)] - public async Task SpaTemplates_BuildAndPublish(string projectKey, string template, string auth) + [InlineData("angular", "Individual")] + [InlineData("react", "Individual")] + [InlineData("angular", null)] + [InlineData("react", null)] + public async Task SpaTemplates_BuildAndPublish(string template, string auth) { - var project = await ProjectFactory.GetOrCreateProject(projectKey, Output); + var project = await ProjectFactory.CreateProject(Output); var args = new[] { "--NoSpaFrontEnd", "true" }; var createResult = await project.RunDotNetNewAsync(template, auth: auth, args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage(template, project, createResult)); diff --git a/src/ProjectTemplates/test/WebApiTemplateTest.cs b/src/ProjectTemplates/test/WebApiTemplateTest.cs index f727abcc92c0..95b483311466 100644 --- a/src/ProjectTemplates/test/WebApiTemplateTest.cs +++ b/src/ProjectTemplates/test/WebApiTemplateTest.cs @@ -35,16 +35,40 @@ public ITestOutputHelper Output [ConditionalTheory] [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] [InlineData("IndividualB2C", null)] - [InlineData("IndividualB2C", new string[] { "--use-program-main" })] - [InlineData("IndividualB2C", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("IndividualB2C", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] + [InlineData("IndividualB2C", new string[] { ArgConstants.UseMinimalApis })] + [InlineData("IndividualB2C", new string[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("IndividualB2C", new string[] { ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + public Task WebApiTemplateCSharp_IdentityWeb_IndividualB2C_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args); + + [ConditionalTheory] + [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] + [InlineData("IndividualB2C", null)] + [InlineData("IndividualB2C", new string[] { ArgConstants.UseProgramMain })] + [InlineData("IndividualB2C", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis })] + [InlineData("IndividualB2C", new string[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("IndividualB2C", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + public Task WebApiTemplateCSharp_IdentityWeb_IndividualB2C_ProgramMain_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args); + + [ConditionalTheory] + [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] + [InlineData("SingleOrg", null)] + [InlineData("SingleOrg", new string[] { ArgConstants.UseMinimalApis })] + [InlineData("SingleOrg", new string[] { ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new string[] { ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new string[] { ArgConstants.CallsGraph })] + [InlineData("SingleOrg", new string[] { ArgConstants.UseMinimalApis, ArgConstants.CallsGraph })] + public Task WebApiTemplateCSharp_IdentityWeb_SingleOrg_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args); + + [ConditionalTheory] + [SkipOnHelix("https://github.com/dotnet/aspnetcore/issues/28090", Queues = HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] [InlineData("SingleOrg", null)] - [InlineData("SingleOrg", new string[] { "--use-program-main" })] - [InlineData("SingleOrg", new string[] { "--called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("SingleOrg", new string[] { "--use-program-main --called-api-url \"https://graph.microsoft.com\"", "--called-api-scopes user.readwrite" })] - [InlineData("SingleOrg", new string[] { "--calls-graph" })] - [InlineData("SingleOrg", new string[] { "--use-program-main --calls-graph" })] - public Task WebApiTemplateCSharp_IdentityWeb_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args); + [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain })] + [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis })] + [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CalledApiUrlGraphMicrosoftCom, ArgConstants.CalledApiScopesUserReadWrite })] + [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.CallsGraph })] + [InlineData("SingleOrg", new string[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.CallsGraph })] + public Task WebApiTemplateCSharp_IdentityWeb_SingleOrg_ProgramMain_BuildsAndPublishes(string auth, string[] args) => PublishAndBuildWebApiTemplate(languageOverride: null, auth: auth, args: args); [Fact] public Task WebApiTemplateFSharp() => WebApiTemplateCore(languageOverride: "F#"); @@ -55,17 +79,33 @@ public ITestOutputHelper Output [ConditionalFact] [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] - public Task WebApiTemplateProgramMainCSharp() => WebApiTemplateCore(languageOverride: null, args: new [] { "--use-program-main" }); + public Task WebApiTemplateProgramMainCSharp() => WebApiTemplateCore(languageOverride: null, args: new [] { ArgConstants.UseProgramMain }); + + [ConditionalFact] + [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] + public Task WebApiTemplateMinimalApisCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseMinimalApis }); + + [ConditionalFact] + [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] + public Task WebApiTemplateProgramMainMinimalApisCSharp() => WebApiTemplateCore(languageOverride: null, args: new[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis }); [ConditionalTheory] - [InlineData(false)] - [InlineData(true)] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, true)] + [InlineData(true, false)] [SkipOnHelix("Cert failure, https://github.com/dotnet/aspnetcore/issues/28090", Queues = "All.OSX;" + HelixConstants.Windows10Arm64 + HelixConstants.DebianArm64)] - public async Task WebApiTemplateCSharp_WithoutOpenAPI(bool useProgramMain) + public async Task WebApiTemplateCSharp_WithoutOpenAPI(bool useProgramMain, bool useMinimalApis) { - var project = await FactoryFixture.GetOrCreateProject("webapinoopenapi", Output); - - var args = useProgramMain ? new[] { "--use-program-main --no-openapi" } : new[] { "--no-openapi" }; + var project = await FactoryFixture.CreateProject(Output); + + var args = useProgramMain + ? useMinimalApis + ? new[] { ArgConstants.UseProgramMain, ArgConstants.UseMinimalApis, ArgConstants.NoOpenApi } + : new[] { ArgConstants.UseProgramMain, ArgConstants.NoOpenApi } + : useMinimalApis + ? new[] { ArgConstants.UseMinimalApis, ArgConstants.NoOpenApi } + : new[] { ArgConstants.NoOpenApi }; var createResult = await project.RunDotNetNewAsync("webapi", args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); diff --git a/src/ProjectTemplates/test/WorkerTemplateTest.cs b/src/ProjectTemplates/test/WorkerTemplateTest.cs index ba82b5073c0e..60b3f519040b 100644 --- a/src/ProjectTemplates/test/WorkerTemplateTest.cs +++ b/src/ProjectTemplates/test/WorkerTemplateTest.cs @@ -33,14 +33,12 @@ public ITestOutputHelper Output [ConditionalTheory] [OSSkipCondition(OperatingSystems.Linux, SkipReason = "https://github.com/dotnet/sdk/issues/12831")] [InlineData("C#", null)] - [InlineData("C#", new string[] { "--use-program-main" })] + [InlineData("C#", new string[] { ArgConstants.UseProgramMain })] [InlineData("F#", null)] [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/25404")] public async Task WorkerTemplateAsync(string language, string[] args) { - var project = await ProjectFactory.GetOrCreateProject( - $"worker-{ language.ToLowerInvariant()[0] }sharp", - Output); + var project = await ProjectFactory.CreateProject(Output); var createResult = await project.RunDotNetNewAsync("worker", language: language, args: args); Assert.True(0 == createResult.ExitCode, ErrorMessages.GetFailedProcessMessage("create/restore", project, createResult)); diff --git a/src/ProjectTemplates/test/template-baselines.json b/src/ProjectTemplates/test/template-baselines.json index b707dcd78819..4c364f3b9298 100644 --- a/src/ProjectTemplates/test/template-baselines.json +++ b/src/ProjectTemplates/test/template-baselines.json @@ -541,6 +541,19 @@ ], "AuthOption": "IndividualB2C" }, + "IndividualB2CProgramMain": { + "Template": "webapi", + "Arguments": "new webapi -au IndividualB2C --use-program-main --aad-b2c-instance https://login.microsoftonline.com/tfp/ --domain fake-b2c-domain.onmicrosoft.com --client-id 64f31f76-2750-49e4-aab9-f5de105b5172 -ssp B2C_1_SiUpIn", + "Files": [ + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "WeatherForecast.cs", + "Controllers/WeatherForecastController.cs", + "Properties/launchSettings.json" + ], + "AuthOption": "IndividualB2C" + }, "SingleOrg": { "Template": "webapi", "Arguments": "new webapi -au SingleOrg --aad-instance https://login.microsoftonline.com/ --domain fake-aad-domain.onmicrosoft.com --client-id db33c356-12cc-4953-9167-00ad56c2e8b2 --tenant-id 7e511586-66ec-4108-bc9c-a68dee0dc2aa", @@ -554,6 +567,19 @@ ], "AuthOption": "SingleOrg" }, + "SingleOrgProgramMain": { + "Template": "webapi", + "Arguments": "new webapi -au SingleOrg --use-program-main --aad-instance https://login.microsoftonline.com/ --domain fake-aad-domain.onmicrosoft.com --client-id db33c356-12cc-4953-9167-00ad56c2e8b2 --tenant-id 7e511586-66ec-4108-bc9c-a68dee0dc2aa", + "Files": [ + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "WeatherForecast.cs", + "Controllers/WeatherForecastController.cs", + "Properties/launchSettings.json" + ], + "AuthOption": "SingleOrg" + }, "None": { "Template": "webapi", "Arguments": "new webapi -au None", @@ -567,6 +593,19 @@ ], "AuthOption": "None" }, + "NoneProgramMain": { + "Template": "webapi", + "Arguments": "new webapi -au None --use-program-main", + "Files": [ + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "WeatherForecast.cs", + "Controllers/WeatherForecastController.cs", + "Properties/launchSettings.json" + ], + "AuthOption": "None" + }, "MinimalIndividualB2C": { "Template": "webapi", "Arguments": "new webapi -minimal -au IndividualB2C --aad-b2c-instance https://login.microsoftonline.com/tfp/ --domain fake-b2c-domain.onmicrosoft.com --client-id 64f31f76-2750-49e4-aab9-f5de105b5172 -ssp B2C_1_SiUpIn", @@ -578,6 +617,18 @@ ], "AuthOption": "IndividualB2C" }, + "MinimalIndividualB2CProgramMain": { + "Template": "webapi", + "Arguments": "new webapi -minimal --use-program-main -au IndividualB2C --aad-b2c-instance https://login.microsoftonline.com/tfp/ --domain fake-b2c-domain.onmicrosoft.com --client-id 64f31f76-2750-49e4-aab9-f5de105b5172 -ssp B2C_1_SiUpIn", + "Files": [ + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "WeatherForecast.cs", + "Properties/launchSettings.json" + ], + "AuthOption": "IndividualB2C" + }, "MinimalSingleOrg": { "Template": "webapi", "Arguments": "new webapi -minimal -au SingleOrg --aad-instance https://login.microsoftonline.com/ --domain fake-aad-domain.onmicrosoft.com --client-id db33c356-12cc-4953-9167-00ad56c2e8b2 --tenant-id 7e511586-66ec-4108-bc9c-a68dee0dc2aa", @@ -589,6 +640,18 @@ ], "AuthOption": "SingleOrg" }, + "MinimalSingleOrgProgramMain": { + "Template": "webapi", + "Arguments": "new webapi -minimal --use-program-main -au SingleOrg --aad-instance https://login.microsoftonline.com/ --domain fake-aad-domain.onmicrosoft.com --client-id db33c356-12cc-4953-9167-00ad56c2e8b2 --tenant-id 7e511586-66ec-4108-bc9c-a68dee0dc2aa", + "Files": [ + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "WeatherForecast.cs", + "Properties/launchSettings.json" + ], + "AuthOption": "SingleOrg" + }, "Minimal": { "Template": "webapi", "Arguments": "new webapi -minimal -au None", @@ -600,6 +663,18 @@ ], "AuthOption": "None" }, + "MinimalProgramMain": { + "Template": "webapi", + "Arguments": "new webapi -minimal --use-program-main -au None", + "Files": [ + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "WeatherForecast.cs", + "Properties/launchSettings.json" + ], + "AuthOption": "None" + }, "Windows": { "Template": "webapi", "Arguments": "new webapi -au Windows", @@ -613,6 +688,19 @@ ], "AuthOption": "Windows" }, + "WindowsProgramMain": { + "Template": "webapi", + "Arguments": "new webapi -au Windows --use-program-main", + "Files": [ + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "WeatherForecast.cs", + "Controllers/WeatherForecastController.cs", + "Properties/launchSettings.json" + ], + "AuthOption": "Windows" + }, "MinimalWindows": { "Template": "webapi", "Arguments": "new webapi -minimal -au Windows", @@ -624,6 +712,18 @@ ], "AuthOption": "Windows" }, + "MinimalWindowsProgramMain": { + "Template": "webapi", + "Arguments": "new webapi -minimal --use-program-main -au Windows", + "Files": [ + "appsettings.Development.json", + "appsettings.json", + "Program.cs", + "WeatherForecast.cs", + "Properties/launchSettings.json" + ], + "AuthOption": "Windows" + }, "FSharp": { "Template": "webapi", "Arguments": "new webapi --language F#", @@ -1569,6 +1669,7 @@ "ClientApp/src/custom.css", "ClientApp/src/App.js", "ClientApp/src/App.test.js", + "ClientApp/src/AppRoutes.js", "ClientApp/src/index.js", "ClientApp/src/reportWebVitals.js", "ClientApp/src/serviceWorkerRegistration.js", diff --git a/src/ProjectTemplates/test/yarn.lock b/src/ProjectTemplates/test/yarn.lock index 136eccbe7f67..8eb6533d4475 100644 --- a/src/ProjectTemplates/test/yarn.lock +++ b/src/ProjectTemplates/test/yarn.lock @@ -254,9 +254,9 @@ mimic-response@^3.1.0: integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mkdirp@^1.0.4: version "1.0.4" diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs index b4de879f24ba..d1971c53b026 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestStream.cs @@ -102,11 +102,11 @@ private void ValidateReadBuffer(byte[] buffer, int offset, int size) { throw new ArgumentNullException(nameof(buffer)); } - if (offset < 0 || offset > buffer.Length) + if ((uint)offset > (uint)buffer.Length) { throw new ArgumentOutOfRangeException(nameof(offset), offset, string.Empty); } - if (size <= 0 || size > buffer.Length - offset) + if ((uint)size > (uint)(buffer.Length - offset)) { throw new ArgumentOutOfRangeException(nameof(size), size, string.Empty); } @@ -163,7 +163,14 @@ public override unsafe int Read([In, Out] byte[] buffer, int offset, int size) dataRead += extraDataRead; } - if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) + + // Zero-byte reads + if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA && size == 0) + { + // extraDataRead returns 1 to let us know there's data available. Don't count it against the request body size yet. + dataRead = 0; + } + else if (statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { Exception exception = new IOException(string.Empty, new HttpSysException((int)statusCode)); Log.ErrorWhileRead(Logger, exception); @@ -183,7 +190,8 @@ public override unsafe int Read([In, Out] byte[] buffer, int offset, int size) internal void UpdateAfterRead(uint statusCode, uint dataRead) { - if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF || dataRead == 0) + if (statusCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF + || statusCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA && dataRead == 0) { Dispose(); } @@ -244,7 +252,7 @@ public override unsafe Task ReadAsync(byte[] buffer, int offset, int size, cancellationRegistration = RequestContext.RegisterForCancellation(cancellationToken); } - asyncResult = new RequestStreamAsyncResult(this, null, null, buffer, offset, dataRead, cancellationRegistration); + asyncResult = new RequestStreamAsyncResult(this, null, null, buffer, offset, size, dataRead, cancellationRegistration); uint bytesReturned; try diff --git a/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs b/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs index e5fbc69ba72d..e51128e9989a 100644 --- a/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs +++ b/src/Servers/HttpSys/src/RequestProcessing/RequestStreamAsyncResult.cs @@ -17,38 +17,24 @@ internal unsafe class RequestStreamAsyncResult : IAsyncResult, IDisposable private readonly SafeNativeOverlapped? _overlapped; private readonly IntPtr _pinnedBuffer; + private readonly int _size; private readonly uint _dataAlreadyRead; private readonly TaskCompletionSource _tcs; private readonly RequestStream _requestStream; private readonly AsyncCallback? _callback; private readonly CancellationTokenRegistration _cancellationRegistration; - internal RequestStreamAsyncResult(RequestStream requestStream, object? userState, AsyncCallback? callback) + internal RequestStreamAsyncResult(RequestStream requestStream, object? userState, AsyncCallback? callback, byte[] buffer, int offset, int size, uint dataAlreadyRead, CancellationTokenRegistration cancellationRegistration) { _requestStream = requestStream; _tcs = new TaskCompletionSource(userState); _callback = callback; - } - - internal RequestStreamAsyncResult(RequestStream requestStream, object? userState, AsyncCallback? callback, uint dataAlreadyRead) - : this(requestStream, userState, callback) - { - _dataAlreadyRead = dataAlreadyRead; - } - - internal RequestStreamAsyncResult(RequestStream requestStream, object? userState, AsyncCallback? callback, byte[] buffer, int offset, uint dataAlreadyRead) - : this(requestStream, userState, callback, buffer, offset, dataAlreadyRead, new CancellationTokenRegistration()) - { - } - - internal RequestStreamAsyncResult(RequestStream requestStream, object? userState, AsyncCallback? callback, byte[] buffer, int offset, uint dataAlreadyRead, CancellationTokenRegistration cancellationRegistration) - : this(requestStream, userState, callback) - { _dataAlreadyRead = dataAlreadyRead; var boundHandle = requestStream.RequestContext.Server.RequestQueue.BoundHandle; _overlapped = new SafeNativeOverlapped(boundHandle, boundHandle.AllocateNativeOverlapped(IOCallback, this, buffer)); _pinnedBuffer = (Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset)); + _size = size; _cancellationRegistration = cancellationRegistration; } @@ -87,7 +73,13 @@ private static void IOCompleted(RequestStreamAsyncResult asyncResult, uint error { try { - if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) + // Zero-byte reads + if (errorCode == UnsafeNclNativeMethods.ErrorCodes.ERROR_MORE_DATA && asyncResult._size == 0) + { + // numBytes returns 1 to let us know there's data available. Don't count it against the request body size yet. + asyncResult.Complete(0, errorCode); + } + else if (errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_SUCCESS && errorCode != UnsafeNclNativeMethods.ErrorCodes.ERROR_HANDLE_EOF) { asyncResult.Fail(new IOException(string.Empty, new HttpSysException((int)errorCode))); } diff --git a/src/Servers/HttpSys/test/FunctionalTests/RequestBodyTests.cs b/src/Servers/HttpSys/test/FunctionalTests/RequestBodyTests.cs index f9817310f4cb..ba1bb65da674 100644 --- a/src/Servers/HttpSys/test/FunctionalTests/RequestBodyTests.cs +++ b/src/Servers/HttpSys/test/FunctionalTests/RequestBodyTests.cs @@ -39,6 +39,28 @@ public async Task RequestBody_ReadSync_Success() } } + [ConditionalFact] + public async Task RequestBody_Read0ByteSync_Success() + { + string address; + using (Utilities.CreateHttpServer(out address, httpContext => + { + Assert.True(httpContext.Request.CanHaveBody()); + byte[] input = new byte[100]; + httpContext.Features.Get().AllowSynchronousIO = true; + int read = httpContext.Request.Body.Read(input, 0, 0); + Assert.Equal(0, read); + read = httpContext.Request.Body.Read(input, 0, input.Length); + httpContext.Response.ContentLength = read; + httpContext.Response.Body.Write(input, 0, read); + return Task.FromResult(0); + })) + { + string response = await SendRequestAsync(address, "Hello World"); + Assert.Equal("Hello World", response); + } + } + [ConditionalFact] public async Task RequestBody_ReadAsync_Success() { @@ -57,6 +79,29 @@ public async Task RequestBody_ReadAsync_Success() } } + [ConditionalFact] + public async Task RequestBody_Read0ByteAsync_Success() + { + string address; + using (Utilities.CreateHttpServer(out address, async httpContext => + { + Assert.True(httpContext.Request.CanHaveBody()); + byte[] input = new byte[100]; + await Task.Delay(1000); + int read = await httpContext.Request.Body.ReadAsync(input, 0, 1); + Assert.Equal(1, read); + read = await httpContext.Request.Body.ReadAsync(input, 0, 0); + Assert.Equal(0, read); + read = await httpContext.Request.Body.ReadAsync(input, 1, input.Length - 1); + httpContext.Response.ContentLength = read + 1; + await httpContext.Response.Body.WriteAsync(input, 0, read + 1); + })) + { + string response = await SendRequestAsync(address, "Hello World"); + Assert.Equal("Hello World", response); + } + } + [ConditionalFact] public async Task RequestBody_ReadBeginEnd_Success() { @@ -87,7 +132,6 @@ public async Task RequestBody_InvalidBuffer_ArgumentException() Assert.Throws("offset", () => httpContext.Request.Body.Read(input, -1, 1)); Assert.Throws("offset", () => httpContext.Request.Body.Read(input, input.Length + 1, 1)); Assert.Throws("size", () => httpContext.Request.Body.Read(input, 10, -1)); - Assert.Throws("size", () => httpContext.Request.Body.Read(input, 0, 0)); Assert.Throws("size", () => httpContext.Request.Body.Read(input, 1, input.Length)); Assert.Throws("size", () => httpContext.Request.Body.Read(input, 0, input.Length + 1)); return Task.FromResult(0); diff --git a/src/Servers/IIS/IIS/samples/NativeIISSample/NativeIISSample.csproj b/src/Servers/IIS/IIS/samples/NativeIISSample/NativeIISSample.csproj index fa97a75dd5b0..ecde817e707f 100644 --- a/src/Servers/IIS/IIS/samples/NativeIISSample/NativeIISSample.csproj +++ b/src/Servers/IIS/IIS/samples/NativeIISSample/NativeIISSample.csproj @@ -14,6 +14,7 @@ + diff --git a/src/Servers/IIS/IIS/src/Core/IISHttpContext.IO.cs b/src/Servers/IIS/IIS/src/Core/IISHttpContext.IO.cs index e8ca5be68676..9a456258161e 100644 --- a/src/Servers/IIS/IIS/src/Core/IISHttpContext.IO.cs +++ b/src/Servers/IIS/IIS/src/Core/IISHttpContext.IO.cs @@ -162,6 +162,11 @@ private async Task WriteBody(bool flush = false) var buffer = result.Buffer; try { + if (_bodyOutput.Aborted) + { + break; + } + if (!buffer.IsEmpty) { await AsyncIO!.WriteAsync(buffer); diff --git a/src/Servers/IIS/IIS/src/Core/OutputProducer.cs b/src/Servers/IIS/IIS/src/Core/OutputProducer.cs index 8bf887d1b86a..3b52dc8e284d 100644 --- a/src/Servers/IIS/IIS/src/Core/OutputProducer.cs +++ b/src/Servers/IIS/IIS/src/Core/OutputProducer.cs @@ -13,9 +13,10 @@ namespace Microsoft.AspNetCore.Server.IIS.Core { internal class OutputProducer { - // This locks access to _completed. + // This locks access to _completed and _aborted. private readonly object _contextLock = new object(); private bool _completed; + private volatile bool _aborted; private readonly Pipe _pipe; @@ -32,6 +33,8 @@ public OutputProducer(Pipe pipe) } public PipeReader Reader => _pipe.Reader; + + public bool Aborted => _aborted; public Task FlushAsync(CancellationToken cancellationToken) { @@ -44,7 +47,7 @@ public void Complete() { lock (_contextLock) { - if (_completed) + if (_completed || _aborted) { return; } @@ -58,12 +61,12 @@ public void Abort(Exception error) { lock (_contextLock) { - if (_completed) + if (_completed || _aborted) { return; } - _completed = true; + _aborted = true; _pipe.Reader.CancelPendingRead(); _pipe.Writer.Complete(); @@ -74,7 +77,7 @@ public Task WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellati { lock (_contextLock) { - if (_completed) + if (_completed || _aborted) { return Task.CompletedTask; } diff --git a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/ShadowCopyTests.cs b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/ShadowCopyTests.cs index 375bf607eaed..cdf629494059 100644 --- a/src/Servers/IIS/IIS/test/IIS.FunctionalTests/ShadowCopyTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.FunctionalTests/ShadowCopyTests.cs @@ -199,7 +199,7 @@ public async Task ShadowCopyE2EWorksWithOldFoldersPresent() // Depending on timing, this could result in a shutdown failure, but sometimes it succeeds, handle both situations if (!response.IsSuccessStatusCode) { - Assert.Equal("Application Shutting Down", response.ReasonPhrase); + Assert.True(response.ReasonPhrase == "Application Shutting Down" || response.ReasonPhrase == "Server has been shutdown"); } // This shutdown should trigger a copy to the next highest directory, which will be 2 diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj b/src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj index 1b54904dee94..befdb53d48fa 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj +++ b/src/Servers/IIS/IIS/test/IIS.Tests/IIS.Tests.csproj @@ -19,6 +19,7 @@ + diff --git a/src/Servers/IIS/IIS/test/IIS.Tests/ResponseAbortTests.cs b/src/Servers/IIS/IIS/test/IIS.Tests/ResponseAbortTests.cs index 1cec8d64149f..9f8c1dbb6f9f 100644 --- a/src/Servers/IIS/IIS/test/IIS.Tests/ResponseAbortTests.cs +++ b/src/Servers/IIS/IIS/test/IIS.Tests/ResponseAbortTests.cs @@ -33,7 +33,6 @@ public async Task ClosesWithoutSendingAnything() } [ConditionalFact] - [QuarantinedTest("https://github.com/dotnet/aspnetcore/issues/31404")] public async Task ClosesAfterDataSent() { var bodyReceived = CreateTaskCompletionSource(); diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessNewShimWebSite/InProcessNewShimWebSite.csproj b/src/Servers/IIS/IIS/test/testassets/InProcessNewShimWebSite/InProcessNewShimWebSite.csproj index 1292ac7a3aa4..11fd5774a2f5 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessNewShimWebSite/InProcessNewShimWebSite.csproj +++ b/src/Servers/IIS/IIS/test/testassets/InProcessNewShimWebSite/InProcessNewShimWebSite.csproj @@ -56,6 +56,9 @@ true + + true + true diff --git a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj index 4f4c84730d4b..2b92d81eaa09 100644 --- a/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj +++ b/src/Servers/IIS/IIS/test/testassets/InProcessWebSite/InProcessWebSite.csproj @@ -29,6 +29,7 @@ + diff --git a/src/Servers/IIS/IntegrationTesting.IIS/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj b/src/Servers/IIS/IntegrationTesting.IIS/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj index c97ee26fab2e..ed716f84d993 100644 --- a/src/Servers/IIS/IntegrationTesting.IIS/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj +++ b/src/Servers/IIS/IntegrationTesting.IIS/src/Microsoft.AspNetCore.Server.IntegrationTesting.IIS.csproj @@ -61,6 +61,7 @@ + diff --git a/src/Servers/Kestrel/Core/src/Internal/SniOptionsSelector.cs b/src/Servers/Kestrel/Core/src/Internal/SniOptionsSelector.cs index 85cd5fa1bd70..aa62f1e6d3e4 100644 --- a/src/Servers/Kestrel/Core/src/Internal/SniOptionsSelector.cs +++ b/src/Servers/Kestrel/Core/src/Internal/SniOptionsSelector.cs @@ -225,7 +225,17 @@ private LongestStringFirstComparer() public int Compare(string? x, string? y) { // Flip x and y to put the longest instead of the shortest string first in the SortedList. - return y!.Length.CompareTo(x!.Length); + // SortedList does not support duplicate entries, so fall back to + // StringComparison.OrdinalIgnoreCase behavior for equal length strings. + var lengthResult = y!.Length.CompareTo(x!.Length); + if (lengthResult != 0) + { + return lengthResult; + } + else + { + return string.Compare(x, y, StringComparison.OrdinalIgnoreCase); + } } } } diff --git a/src/Servers/Kestrel/Core/test/SniOptionsSelectorTests.cs b/src/Servers/Kestrel/Core/test/SniOptionsSelectorTests.cs index 6c6cc977b8ee..203d5972bae6 100644 --- a/src/Servers/Kestrel/Core/test/SniOptionsSelectorTests.cs +++ b/src/Servers/Kestrel/Core/test/SniOptionsSelectorTests.cs @@ -186,6 +186,91 @@ public void ServerNameMatchingIsCaseInsensitive() Assert.Equal("WildcardPrefix", pathDictionary[aSubdomainOptions.ServerCertificate]); } + [Fact] + public void MultipleWildcardPrefixServerNamesOfSameLengthAreAllowed() + { + var sniDictionary = new Dictionary + { + { + "*.a.example.org", + new SniConfig + { + Certificate = new CertificateConfig + { + Path = "a" + } + } + }, + { + "*.b.example.org", + new SniConfig + { + Certificate = new CertificateConfig + { + Path = "b" + } + } + } + }; + + var mockCertificateConfigLoader = new MockCertificateConfigLoader(); + var pathDictionary = mockCertificateConfigLoader.CertToPathDictionary; + + var sniOptionsSelector = new SniOptionsSelector( + "TestEndpointName", + sniDictionary, + mockCertificateConfigLoader, + fallbackHttpsOptions: new HttpsConnectionAdapterOptions(), + fallbackHttpProtocols: HttpProtocols.Http1AndHttp2, + logger: Mock.Of>()); + + var (aSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "c.a.example.org"); + Assert.Equal("a", pathDictionary[aSubdomainOptions.ServerCertificate]); + + var (bSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "c.b.example.org"); + Assert.Equal("b", pathDictionary[bSubdomainOptions.ServerCertificate]); + } + + [Fact] + public void DuplicateWildcardPrefixServerNamesThrowsArgumentException() + { + var sniDictionary = new Dictionary + { + { + "*.example.org", + new SniConfig + { + Certificate = new CertificateConfig + { + Path = "a" + } + } + }, + { + "*.EXAMPLE.org", + new SniConfig + { + Certificate = new CertificateConfig + { + Path = "b" + } + } + } + }; + + var mockCertificateConfigLoader = new MockCertificateConfigLoader(); + var pathDictionary = mockCertificateConfigLoader.CertToPathDictionary; + + var exception = Assert.Throws(() => new SniOptionsSelector( + "TestEndpointName", + sniDictionary, + mockCertificateConfigLoader, + fallbackHttpsOptions: new HttpsConnectionAdapterOptions(), + fallbackHttpProtocols: HttpProtocols.Http1AndHttp2, + logger: Mock.Of>())); + Assert.Equal("An item with the same key has already been added. Key: .EXAMPLE.org (Parameter 'key')", exception.Message); + } + [Fact] public void GetOptionsThrowsAnAuthenticationExceptionIfThereIsNoMatchingSniSection() { diff --git a/src/Shared/CertificateGeneration/CertificateManager.cs b/src/Shared/CertificateGeneration/CertificateManager.cs index 99f8e446a7d3..987db2bba031 100644 --- a/src/Shared/CertificateGeneration/CertificateManager.cs +++ b/src/Shared/CertificateGeneration/CertificateManager.cs @@ -493,11 +493,11 @@ internal void ExportCertificate(X509Certificate2 certificate, string path, bool // Export the key first to an encrypted PEM to avoid issues with System.Security.Cryptography.Cng indicating that the operation is not supported. // This is likely by design to avoid exporting the key by mistake. // To bypass it, we export the certificate to pem temporarily and then we import it and export it as unprotected PEM. - keyBytes = key.ExportEncryptedPkcs8PrivateKey("", new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 1)); + keyBytes = key.ExportEncryptedPkcs8PrivateKey((ReadOnlySpan)"", new PbeParameters(PbeEncryptionAlgorithm.Aes256Cbc, HashAlgorithmName.SHA256, 1)); pem = PemEncoding.Write("ENCRYPTED PRIVATE KEY", keyBytes); key.Dispose(); key = RSA.Create(); - key.ImportFromEncryptedPem(pem, ""); + key.ImportFromEncryptedPem(pem, (ReadOnlySpan)""); Array.Clear(keyBytes, 0, keyBytes.Length); Array.Clear(pem, 0, pem.Length); keyBytes = key.ExportPkcs8PrivateKey(); diff --git a/src/SignalR/clients/ts/FunctionalTests/yarn.lock b/src/SignalR/clients/ts/FunctionalTests/yarn.lock index 83ff9d122a99..99e147bdf2b8 100644 --- a/src/SignalR/clients/ts/FunctionalTests/yarn.lock +++ b/src/SignalR/clients/ts/FunctionalTests/yarn.lock @@ -265,9 +265,9 @@ ajv@^6.12.3, ajv@^6.12.5: uri-js "^4.2.2" ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.1.tgz#123d6479e92ad45ad897d4054e3c7ca7db4944e1" + integrity "sha1-Ej1keekq1FrYl9QFTjx8p9tJROE= sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==" ansi-styles@^3.2.1: version "3.2.1" @@ -341,9 +341,9 @@ async-limiter@~1.0.0: integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== async@^2.0.0, async@^2.1.2, async@^2.6.2, async@^2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + version "2.6.4" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" + integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== dependencies: lodash "^4.17.14" @@ -1530,9 +1530,9 @@ minimatch@^3.0.2, minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== minimist@~0.0.1: version "0.0.10" diff --git a/src/SignalR/clients/ts/common/yarn.lock b/src/SignalR/clients/ts/common/yarn.lock index 5f7e6ed679e1..833e599afde7 100644 --- a/src/SignalR/clients/ts/common/yarn.lock +++ b/src/SignalR/clients/ts/common/yarn.lock @@ -3366,9 +3366,9 @@ minimatch@^3.0.4: brace-expansion "^1.1.7" minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + version "1.2.6" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" + integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== mixin-deep@^1.2.0: version "1.3.2" @@ -4415,9 +4415,9 @@ throat@^5.0.0: integrity sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA== tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= + version "1.0.5" + resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" + integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== to-fast-properties@^2.0.0: version "2.0.0" diff --git a/src/Testing/src/xunit/HelixConstants.cs b/src/Testing/src/xunit/HelixConstants.cs index 8ec6a086a7a0..a78e9e2b411b 100644 --- a/src/Testing/src/xunit/HelixConstants.cs +++ b/src/Testing/src/xunit/HelixConstants.cs @@ -6,6 +6,7 @@ namespace Microsoft.AspNetCore.Testing public static class HelixConstants { public const string Windows10Arm64 = "Windows.10.Arm64v8.Open;"; + public const string DebianAmd64 = "Debian.11.Amd64.Open;"; public const string DebianArm64 = "Debian.11.Arm64.Open;"; public const string RedhatAmd64 = "Redhat.7.Amd64.Open;"; } diff --git a/src/Tools/Microsoft.dotnet-openapi/src/Microsoft.dotnet-openapi.csproj b/src/Tools/Microsoft.dotnet-openapi/src/Microsoft.dotnet-openapi.csproj index 1248c3830880..58f97791a998 100644 --- a/src/Tools/Microsoft.dotnet-openapi/src/Microsoft.dotnet-openapi.csproj +++ b/src/Tools/Microsoft.dotnet-openapi/src/Microsoft.dotnet-openapi.csproj @@ -17,6 +17,7 @@ + diff --git a/src/Tools/dotnet-sql-cache/src/dotnet-sql-cache.csproj b/src/Tools/dotnet-sql-cache/src/dotnet-sql-cache.csproj index 24f2823009cf..ca9fb454c381 100644 --- a/src/Tools/dotnet-sql-cache/src/dotnet-sql-cache.csproj +++ b/src/Tools/dotnet-sql-cache/src/dotnet-sql-cache.csproj @@ -15,6 +15,7 @@ + diff --git a/src/submodules/googletest b/src/submodules/googletest index af29db7ec28d..8ded48c37be0 160000 --- a/src/submodules/googletest +++ b/src/submodules/googletest @@ -1 +1 @@ -Subproject commit af29db7ec28d6df1c7f0f745186884091e602e07 +Subproject commit 8ded48c37be09d8cc3665af1b414c5d53c0862e7 diff --git a/src/submodules/spa-templates b/src/submodules/spa-templates index 08a4e743ccec..74e05a19dc83 160000 --- a/src/submodules/spa-templates +++ b/src/submodules/spa-templates @@ -1 +1 @@ -Subproject commit 08a4e743ccec97c24aec8b7a719655f08e827d8e +Subproject commit 74e05a19dc83b736877cb2c5120456948d7799f1