Skip to content

Commit

Permalink
Fix templates sourceName & port of template fixes from main (#41536)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
DamianEdwards authored May 16, 2022
1 parent fac970d commit 3418843
Show file tree
Hide file tree
Showing 60 changed files with 830 additions and 478 deletions.
8 changes: 6 additions & 2 deletions eng/helix/content/RunTests/ProcessUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -80,7 +81,7 @@ public static async Task<ProcessResult> RunAsync(
Action<int>? onStart = null,
CancellationToken cancellationToken = default)
{
Console.WriteLine($"Running '{filename} {arguments}'");
PrintMessage($"Running '{filename} {arguments}'");
using var process = new Process()
{
StartInfo =
Expand Down Expand Up @@ -153,7 +154,7 @@ public static async Task<ProcessResult> 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()}"));
Expand Down Expand Up @@ -208,5 +209,8 @@ public static async Task<ProcessResult> 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}");
}
}
10 changes: 5 additions & 5 deletions eng/helix/content/RunTests/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
}
}
Expand Down
114 changes: 62 additions & 52 deletions eng/helix/content/RunTests/TestRunner.cs

Large diffs are not rendered by default.

3 changes: 1 addition & 2 deletions eng/targets/Helix.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@

<PropertyGroup>
<CreateHelixPayload>true</CreateHelixPayload>
<HelixTimeout>00:30:00</HelixTimeout>
<HelixTimeout Condition="$(HelixTargetQueue.StartsWith('Windows.10.Amd64'))">00:40:00</HelixTimeout>
<HelixTimeout>00:45:00</HelixTimeout>
<RunQuarantinedTests>false</RunQuarantinedTests>
<HelixTestName>$(MSBuildProjectName)--$(TargetFramework)</HelixTestName>
<LoggingTestingDisableFileLogging Condition="'$(IsHelixJob)' == 'true'">false</LoggingTestingDisableFileLogging>
Expand Down
6 changes: 3 additions & 3 deletions src/Hosting/Hosting/src/Internal/WebHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ApplicationLifetime>() as IHostApplicationLifetime);
=> services.GetService<ApplicationLifetime>()! as IHostApplicationLifetime);
#pragma warning disable CS0618 // Type or member is obsolete
_applicationServiceCollection.AddSingleton(services
=> services.GetService<ApplicationLifetime>() as AspNetCore.Hosting.IApplicationLifetime);
=> services.GetService<ApplicationLifetime>()! as AspNetCore.Hosting.IApplicationLifetime);
_applicationServiceCollection.AddSingleton(services
=> services.GetService<ApplicationLifetime>() as Extensions.Hosting.IApplicationLifetime);
=> services.GetService<ApplicationLifetime>()! 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<HostedServiceExecutor>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ protected async Task<Project> 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;
Expand Down
2 changes: 1 addition & 1 deletion src/ProjectTemplates/Shared/ProcessResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
117 changes: 35 additions & 82 deletions src/ProjectTemplates/Shared/Project.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ internal async Task<ProcessResult> RunDotNetNewAsync(
string language = null,
bool useLocalDB = false,
bool noHttps = false,
bool errorOnRestoreError = true,
string[] args = null,
// Used to set special options in MSBuild
IDictionary<string, string> environmentVariables = null)
Expand Down Expand Up @@ -95,29 +96,15 @@ internal async Task<ProcessResult> 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<ProcessResult> RunDotNetPublishAsync(IDictionary<string, string> packageOptions = null, string additionalArgs = null, bool noRestore = true)
Expand Down Expand Up @@ -183,31 +170,19 @@ internal async Task<ProcessResult> 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<ProcessResult> RunDotNetEfUpdateDatabaseAsync()
Expand All @@ -216,31 +191,19 @@ internal async Task<ProcessResult> 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
Expand Down Expand Up @@ -294,25 +257,15 @@ public string ReadFile(string path)

internal async Task<ProcessEx> 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()
Expand Down
53 changes: 38 additions & 15 deletions src/ProjectTemplates/Shared/ProjectFactoryFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Templates.Test.Helpers
{
public class ProjectFactoryFixture : IDisposable
{
private const string LetterChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
private readonly ConcurrentDictionary<string, Project> _projects = new ConcurrentDictionary<string, Project>();

public IMessageSink DiagnosticsMessageSink { get; }
Expand All @@ -23,6 +24,21 @@ public ProjectFactoryFixture(IMessageSink diagnosticsMessageSink)
DiagnosticsMessageSink = diagnosticsMessageSink;
}

public async Task<Project> 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<Project> GetOrCreateProject(string projectKey, ITestOutputHelper output)
{
await TemplatePackageInstaller.EnsureTemplatingEngineInitializedAsync(output);
Expand All @@ -34,24 +50,31 @@ public async Task<Project> 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<AssemblyMetadataAttribute>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
#endif
using BlazorServerWeb_CSharp.Data;

namespace Company.WebApplication1;
namespace BlazorServerWeb_CSharp;

public class Program
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
using ComponentsWebAssembly_CSharp.Server.Models;
#endif

namespace Company.WebApplication1;
namespace ComponentsWebAssembly_CSharp;

public class Program
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using GrpcService_CSharp.Services;

namespace Company.WebApplication1;
namespace GrpcService_CSharp;

public class Program
{
Expand Down
Loading

0 comments on commit 3418843

Please sign in to comment.