Skip to content

Commit 3418843

Browse files
Fix templates sourceName & port of template fixes from main (#41536)
* Fix templates sourceName * Support minimal APIs & program/main together in Web API template - Updated test cases to cover more options combinations - Fixed route pattern typo in minimal APIs case - Added missing UseAuthorization call in minimal APIs + Windows Auth case - Fail template tests if a compiler warning is output on build or publish - Fix Web API template when call graph option is true - Add arg constants & fix formatting Fixes #41491 * Clean-up from template fix port - Update template scripts to use correct package version when using locally - Update template-baselines.json to cover new template options * Update for VS compiler changes to nullability & u8 string literals * Template baselines test fix * Make template baseline test check namespaces match project name * Comment for clarity * Port extra template tests from main * Port template test change that fails if warnings present on build or publish * Use original file name in template baseline test * Fix single file exe test Now that we fail if "warning" appears in the command output, we have to be sure to now issue and dotnet CLI commands that result in SDK warnings (in this case the change in net6.0 that -r must be accompanied with --self-contained or --no-self-contained). * Comment out tenmplate baseline test namespace declaration * Comment out template warning checks * Apply test fixes from failures investigation * Set project name * Bump Helix test runner timeout to 60 mins * Collect test host dumps on Helix test runner crashes * Print message when test job times out Bump helix timeout to match main (45 mins) * Make helix runner print timestamps with console logs * Ported more test changes from main * Revert failing project test if new/build/publish emit restore errors or other warnings. * Fix typos * Port TestRunner.cs changes
1 parent fac970d commit 3418843

File tree

60 files changed

+830
-478
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+830
-478
lines changed

eng/helix/content/RunTests/ProcessUtil.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System;
66
using System.Collections.Generic;
77
using System.Diagnostics;
8+
using System.Globalization;
89
using System.IO;
910
using System.Runtime.InteropServices;
1011
using System.Text;
@@ -80,7 +81,7 @@ public static async Task<ProcessResult> RunAsync(
8081
Action<int>? onStart = null,
8182
CancellationToken cancellationToken = default)
8283
{
83-
Console.WriteLine($"Running '{filename} {arguments}'");
84+
PrintMessage($"Running '{filename} {arguments}'");
8485
using var process = new Process()
8586
{
8687
StartInfo =
@@ -153,7 +154,7 @@ public static async Task<ProcessResult> RunAsync(
153154

154155
process.Exited += (_, e) =>
155156
{
156-
Console.WriteLine($"'{process.StartInfo.FileName} {process.StartInfo.Arguments}' completed with exit code '{process.ExitCode}'");
157+
PrintMessage($"'{process.StartInfo.FileName} {process.StartInfo.Arguments}' completed with exit code '{process.ExitCode}'");
157158
if (throwOnError && process.ExitCode != 0)
158159
{
159160
processLifetimeTask.TrySetException(new InvalidOperationException($"Command {filename} {arguments} returned exit code {process.ExitCode} output: {outputBuilder.ToString()}"));
@@ -208,5 +209,8 @@ public static async Task<ProcessResult> RunAsync(
208209

209210
return await processLifetimeTask.Task;
210211
}
212+
213+
public static void PrintMessage(string message) => Console.WriteLine($"{DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)} {message}");
214+
public static void PrintErrorMessage(string message) => Console.Error.WriteLine($"{DateTime.UtcNow.ToString("O", CultureInfo.InvariantCulture)} {message}");
211215
}
212216
}

eng/helix/content/RunTests/Program.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ static async Task Main(string[] args)
2525
keepGoing = await runner.InstallPlaywrightAsync();
2626
}
2727
#else
28-
Console.WriteLine("Playwright install skipped.");
28+
ProcessUtil.PrintMessage("Playwright install skipped.");
2929
#endif
3030

3131
runner.DisplayContents();
@@ -34,23 +34,23 @@ static async Task Main(string[] args)
3434
{
3535
if (!await runner.CheckTestDiscoveryAsync())
3636
{
37-
Console.WriteLine("RunTest stopping due to test discovery failure.");
37+
ProcessUtil.PrintMessage("RunTest stopping due to test discovery failure.");
3838
Environment.Exit(1);
3939
return;
4040
}
4141

4242
var exitCode = await runner.RunTestsAsync();
4343
runner.UploadResults();
44-
Console.WriteLine($"Completed Helix job with exit code '{exitCode}'");
44+
ProcessUtil.PrintMessage($"Completed Helix job with exit code '{exitCode}'");
4545
Environment.Exit(exitCode);
4646
}
4747

48-
Console.WriteLine("Tests were not run due to previous failures. Exit code=1");
48+
ProcessUtil.PrintMessage("Tests were not run due to previous failures. Exit code=1");
4949
Environment.Exit(1);
5050
}
5151
catch (Exception e)
5252
{
53-
Console.WriteLine($"RunTests uncaught exception: {e.ToString()}");
53+
ProcessUtil.PrintMessage($"RunTests uncaught exception: {e.ToString()}");
5454
Environment.Exit(1);
5555
}
5656
}

eng/helix/content/RunTests/TestRunner.cs

Lines changed: 62 additions & 52 deletions
Large diffs are not rendered by default.

eng/targets/Helix.props

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111

1212
<PropertyGroup>
1313
<CreateHelixPayload>true</CreateHelixPayload>
14-
<HelixTimeout>00:30:00</HelixTimeout>
15-
<HelixTimeout Condition="$(HelixTargetQueue.StartsWith('Windows.10.Amd64'))">00:40:00</HelixTimeout>
14+
<HelixTimeout>00:45:00</HelixTimeout>
1615
<RunQuarantinedTests>false</RunQuarantinedTests>
1716
<HelixTestName>$(MSBuildProjectName)--$(TargetFramework)</HelixTestName>
1817
<LoggingTestingDisableFileLogging Condition="'$(IsHelixJob)' == 'true'">false</LoggingTestingDisableFileLogging>

src/Hosting/Hosting/src/Internal/WebHost.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,12 @@ public WebHost(
8181
// There's no way to to register multiple service types per definition. See https://github.com/aspnet/DependencyInjection/issues/360
8282
#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.
8383
_applicationServiceCollection.AddSingleton(services
84-
=> services.GetService<ApplicationLifetime>() as IHostApplicationLifetime);
84+
=> services.GetService<ApplicationLifetime>()! as IHostApplicationLifetime);
8585
#pragma warning disable CS0618 // Type or member is obsolete
8686
_applicationServiceCollection.AddSingleton(services
87-
=> services.GetService<ApplicationLifetime>() as AspNetCore.Hosting.IApplicationLifetime);
87+
=> services.GetService<ApplicationLifetime>()! as AspNetCore.Hosting.IApplicationLifetime);
8888
_applicationServiceCollection.AddSingleton(services
89-
=> services.GetService<ApplicationLifetime>() as Extensions.Hosting.IApplicationLifetime);
89+
=> services.GetService<ApplicationLifetime>()! as Extensions.Hosting.IApplicationLifetime);
9090
#pragma warning restore CS0618 // Type or member is obsolete
9191
#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.
9292
_applicationServiceCollection.AddSingleton<HostedServiceExecutor>();

src/ProjectTemplates/BlazorTemplates.Tests/BlazorTemplateTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ protected async Task<Project> CreateBuildPublishAsync(string projectName, string
2929
// Additional arguments are needed. See: https://github.com/dotnet/aspnetcore/issues/24278
3030
Environment.SetEnvironmentVariable("EnableDefaultScopedCssItems", "true");
3131

32-
var project = await ProjectFactory.GetOrCreateProject(projectName, Output);
32+
var project = await ProjectFactory.CreateProject(Output);
3333
if (targetFramework != null)
3434
{
3535
project.TargetFramework = targetFramework;

src/ProjectTemplates/Shared/ProcessResult.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public ProcessResult(ProcessEx process)
1717

1818
public string Process { get; }
1919

20-
public int ExitCode { get; }
20+
public int ExitCode { get; set; }
2121

2222
public string Error { get; }
2323

src/ProjectTemplates/Shared/Project.cs

Lines changed: 35 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ internal async Task<ProcessResult> RunDotNetNewAsync(
5454
string language = null,
5555
bool useLocalDB = false,
5656
bool noHttps = false,
57+
bool errorOnRestoreError = true,
5758
string[] args = null,
5859
// Used to set special options in MSBuild
5960
IDictionary<string, string> environmentVariables = null)
@@ -95,29 +96,15 @@ internal async Task<ProcessResult> RunDotNetNewAsync(
9596

9697
argString += $" -o {TemplateOutputDir}";
9798

98-
// Only run one instance of 'dotnet new' at once, as a workaround for
99-
// https://github.com/aspnet/templating/issues/63
100-
101-
await DotNetNewLock.WaitAsync();
102-
try
103-
{
104-
Output.WriteLine("Acquired DotNetNewLock");
105-
106-
if (Directory.Exists(TemplateOutputDir))
107-
{
108-
Output.WriteLine($"Template directory already exists, deleting contents of {TemplateOutputDir}");
109-
Directory.Delete(TemplateOutputDir, recursive: true);
110-
}
111-
112-
using var execution = ProcessEx.Run(Output, AppContext.BaseDirectory, DotNetMuxer.MuxerPathOrDefault(), argString, environmentVariables);
113-
await execution.Exited;
114-
return new ProcessResult(execution);
115-
}
116-
finally
99+
if (Directory.Exists(TemplateOutputDir))
117100
{
118-
DotNetNewLock.Release();
119-
Output.WriteLine("Released DotNetNewLock");
101+
Output.WriteLine($"Template directory already exists, deleting contents of {TemplateOutputDir}");
102+
Directory.Delete(TemplateOutputDir, recursive: true);
120103
}
104+
105+
using var execution = ProcessEx.Run(Output, AppContext.BaseDirectory, DotNetMuxer.MuxerPathOrDefault(), argString, environmentVariables);
106+
await execution.Exited;
107+
return new ProcessResult(execution);
121108
}
122109

123110
internal async Task<ProcessResult> RunDotNetPublishAsync(IDictionary<string, string> packageOptions = null, string additionalArgs = null, bool noRestore = true)
@@ -183,31 +170,19 @@ internal async Task<ProcessResult> RunDotNetEfCreateMigrationAsync(string migrat
183170
{
184171
var args = $"--verbose --no-build migrations add {migrationName}";
185172

186-
// Only run one instance of 'dotnet new' at once, as a workaround for
187-
// https://github.com/aspnet/templating/issues/63
188-
await DotNetNewLock.WaitAsync();
189-
try
173+
var command = DotNetMuxer.MuxerPathOrDefault();
174+
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath")))
190175
{
191-
Output.WriteLine("Acquired DotNetNewLock");
192-
var command = DotNetMuxer.MuxerPathOrDefault();
193-
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath")))
194-
{
195-
args = $"\"{DotNetEfFullPath}\" " + args;
196-
}
197-
else
198-
{
199-
command = "dotnet-ef";
200-
}
201-
202-
using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args);
203-
await result.Exited;
204-
return new ProcessResult(result);
176+
args = $"\"{DotNetEfFullPath}\" " + args;
205177
}
206-
finally
178+
else
207179
{
208-
DotNetNewLock.Release();
209-
Output.WriteLine("Released DotNetNewLock");
180+
command = "dotnet-ef";
210181
}
182+
183+
using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args);
184+
await result.Exited;
185+
return new ProcessResult(result);
211186
}
212187

213188
internal async Task<ProcessResult> RunDotNetEfUpdateDatabaseAsync()
@@ -216,31 +191,19 @@ internal async Task<ProcessResult> RunDotNetEfUpdateDatabaseAsync()
216191

217192
var args = "--verbose --no-build database update";
218193

219-
// Only run one instance of 'dotnet new' at once, as a workaround for
220-
// https://github.com/aspnet/templating/issues/63
221-
await DotNetNewLock.WaitAsync();
222-
try
194+
var command = DotNetMuxer.MuxerPathOrDefault();
195+
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath")))
223196
{
224-
Output.WriteLine("Acquired DotNetNewLock");
225-
var command = DotNetMuxer.MuxerPathOrDefault();
226-
if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("DotNetEfFullPath")))
227-
{
228-
args = $"\"{DotNetEfFullPath}\" " + args;
229-
}
230-
else
231-
{
232-
command = "dotnet-ef";
233-
}
234-
235-
using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args);
236-
await result.Exited;
237-
return new ProcessResult(result);
197+
args = $"\"{DotNetEfFullPath}\" " + args;
238198
}
239-
finally
199+
else
240200
{
241-
DotNetNewLock.Release();
242-
Output.WriteLine("Released DotNetNewLock");
201+
command = "dotnet-ef";
243202
}
203+
204+
using var result = ProcessEx.Run(Output, TemplateOutputDir, command, args);
205+
await result.Exited;
206+
return new ProcessResult(result);
244207
}
245208

246209
// If this fails, you should generate new migrations via migrations/updateMigrations.cmd
@@ -294,25 +257,15 @@ public string ReadFile(string path)
294257

295258
internal async Task<ProcessEx> RunDotNetNewRawAsync(string arguments)
296259
{
297-
await DotNetNewLock.WaitAsync();
298-
try
299-
{
300-
Output.WriteLine("Acquired DotNetNewLock");
301-
var result = ProcessEx.Run(
302-
Output,
303-
AppContext.BaseDirectory,
304-
DotNetMuxer.MuxerPathOrDefault(),
305-
arguments +
306-
$" --debug:disable-sdk-templates --debug:custom-hive \"{TemplatePackageInstaller.CustomHivePath}\"" +
307-
$" -o {TemplateOutputDir}");
308-
await result.Exited;
309-
return result;
310-
}
311-
finally
312-
{
313-
DotNetNewLock.Release();
314-
Output.WriteLine("Released DotNetNewLock");
315-
}
260+
var result = ProcessEx.Run(
261+
Output,
262+
AppContext.BaseDirectory,
263+
DotNetMuxer.MuxerPathOrDefault(),
264+
arguments +
265+
$" --debug:disable-sdk-templates --debug:custom-hive \"{TemplatePackageInstaller.CustomHivePath}\"" +
266+
$" -o {TemplateOutputDir}");
267+
await result.Exited;
268+
return result;
316269
}
317270

318271
public void Dispose()

src/ProjectTemplates/Shared/ProjectFactoryFixture.cs

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace Templates.Test.Helpers
1414
{
1515
public class ProjectFactoryFixture : IDisposable
1616
{
17+
private const string LetterChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
1718
private readonly ConcurrentDictionary<string, Project> _projects = new ConcurrentDictionary<string, Project>();
1819

1920
public IMessageSink DiagnosticsMessageSink { get; }
@@ -23,6 +24,21 @@ public ProjectFactoryFixture(IMessageSink diagnosticsMessageSink)
2324
DiagnosticsMessageSink = diagnosticsMessageSink;
2425
}
2526

27+
public async Task<Project> CreateProject(ITestOutputHelper output)
28+
{
29+
await TemplatePackageInstaller.EnsureTemplatingEngineInitializedAsync(output);
30+
31+
var project = CreateProjectImpl(output);
32+
33+
var projectKey = Guid.NewGuid().ToString().Substring(0, 10).ToLowerInvariant();
34+
if (!_projects.TryAdd(projectKey, project))
35+
{
36+
throw new InvalidOperationException($"Project key collision in {nameof(ProjectFactoryFixture)}.{nameof(CreateProject)}!");
37+
}
38+
39+
return project;
40+
}
41+
2642
public async Task<Project> GetOrCreateProject(string projectKey, ITestOutputHelper output)
2743
{
2844
await TemplatePackageInstaller.EnsureTemplatingEngineInitializedAsync(output);
@@ -34,24 +50,31 @@ public async Task<Project> GetOrCreateProject(string projectKey, ITestOutputHelp
3450
}
3551
return _projects.GetOrAdd(
3652
projectKey,
37-
(key, outputHelper) =>
38-
{
39-
var project = new Project
40-
{
41-
Output = outputHelper,
42-
DiagnosticsMessageSink = DiagnosticsMessageSink,
43-
ProjectGuid = Path.GetRandomFileName().Replace(".", string.Empty)
44-
};
45-
project.ProjectName = $"AspNet.{project.ProjectGuid}";
46-
47-
var assemblyPath = GetType().Assembly;
48-
var basePath = GetTemplateFolderBasePath(assemblyPath);
49-
project.TemplateOutputDir = Path.Combine(basePath, project.ProjectName);
50-
return project;
51-
},
53+
(_, outputHelper) => CreateProjectImpl(outputHelper),
5254
output);
5355
}
5456

57+
private Project CreateProjectImpl(ITestOutputHelper output)
58+
{
59+
var project = new Project
60+
{
61+
Output = output,
62+
DiagnosticsMessageSink = DiagnosticsMessageSink,
63+
// Ensure first character is a letter to avoid random insertions of '_' into template namespace
64+
// declarations (i.e. make it more stable for testing)
65+
ProjectGuid = GetRandomLetter() + Path.GetRandomFileName().Replace(".", string.Empty)
66+
};
67+
project.ProjectName = $"AspNetCore.{project.ProjectGuid}";
68+
69+
var assemblyPath = GetType().Assembly;
70+
var basePath = GetTemplateFolderBasePath(assemblyPath);
71+
project.TemplateOutputDir = Path.Combine(basePath, project.ProjectName);
72+
73+
return project;
74+
}
75+
76+
private static char GetRandomLetter() => LetterChars[Random.Shared.Next(LetterChars.Length - 1)];
77+
5578
private static string GetTemplateFolderBasePath(Assembly assembly) =>
5679
(string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HELIX_DIR")))
5780
? assembly.GetCustomAttributes<AssemblyMetadataAttribute>()

src/ProjectTemplates/Web.ProjectTemplates/content/BlazorServerWeb-CSharp/Program.Main.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
#endif
3838
using BlazorServerWeb_CSharp.Data;
3939

40-
namespace Company.WebApplication1;
40+
namespace BlazorServerWeb_CSharp;
4141

4242
public class Program
4343
{

0 commit comments

Comments
 (0)