TemplatePackageInstaller.cs 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. using System;
  4. using System.Diagnostics;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Reflection;
  8. using System.Threading.Tasks;
  9. using Microsoft.AspNetCore.Internal;
  10. using Microsoft.Extensions.CommandLineUtils;
  11. using Xunit;
  12. using Xunit.Abstractions;
  13. namespace Templates.Test.Helpers;
  14. internal static class TemplatePackageInstaller
  15. {
  16. private static bool _haveReinstalledTemplatePackages;
  17. private static readonly string[] _templatePackages = new[]
  18. {
  19. "Microsoft.DotNet.Common.ItemTemplates",
  20. "Microsoft.DotNet.Common.ProjectTemplates.2.1",
  21. "Microsoft.DotNet.Test.ProjectTemplates.2.1",
  22. "Microsoft.DotNet.Web.Client.ItemTemplates",
  23. "Microsoft.DotNet.Web.ItemTemplates",
  24. "Microsoft.DotNet.Web.ProjectTemplates.1.x",
  25. "Microsoft.DotNet.Web.ProjectTemplates.2.0",
  26. "Microsoft.DotNet.Web.ProjectTemplates.2.1",
  27. "Microsoft.DotNet.Web.ProjectTemplates.2.2",
  28. "Microsoft.DotNet.Web.ProjectTemplates.3.0",
  29. "Microsoft.DotNet.Web.ProjectTemplates.3.1",
  30. "Microsoft.DotNet.Web.ProjectTemplates.5.0",
  31. "Microsoft.DotNet.Web.ProjectTemplates.6.0",
  32. "Microsoft.DotNet.Web.ProjectTemplates.7.0",
  33. "Microsoft.DotNet.Web.ProjectTemplates.8.0",
  34. "Microsoft.DotNet.Web.Spa.ProjectTemplates.2.1",
  35. "Microsoft.DotNet.Web.Spa.ProjectTemplates.2.2",
  36. "Microsoft.DotNet.Web.Spa.ProjectTemplates.3.0",
  37. "Microsoft.DotNet.Web.Spa.ProjectTemplates.3.1",
  38. "Microsoft.DotNet.Web.Spa.ProjectTemplates.5.0",
  39. "Microsoft.DotNet.Web.Spa.ProjectTemplates.6.0",
  40. "Microsoft.DotNet.Web.Spa.ProjectTemplates.7.0",
  41. "Microsoft.DotNet.Web.Spa.ProjectTemplates.8.0",
  42. "Microsoft.DotNet.Web.Spa.ProjectTemplates",
  43. "Microsoft.AspNetCore.Blazor.Templates",
  44. };
  45. public static string CustomHivePath { get; } = Path.GetFullPath((string.IsNullOrEmpty(Environment.GetEnvironmentVariable("helix")))
  46. ? typeof(TemplatePackageInstaller)
  47. .Assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
  48. .Single(s => s.Key == "CustomTemplateHivePath").Value
  49. : Path.Combine("Hives", ".templateEngine"));
  50. public static async Task EnsureTemplatingEngineInitializedAsync(ITestOutputHelper output)
  51. {
  52. if (!_haveReinstalledTemplatePackages)
  53. {
  54. if (Directory.Exists(CustomHivePath))
  55. {
  56. Directory.Delete(CustomHivePath, recursive: true);
  57. }
  58. await InstallTemplatePackages(output);
  59. _haveReinstalledTemplatePackages = true;
  60. }
  61. }
  62. public static async Task<ProcessEx> RunDotNetNew(ITestOutputHelper output, string arguments)
  63. {
  64. var proc = ProcessEx.Run(
  65. output,
  66. AppContext.BaseDirectory,
  67. DotNetMuxer.MuxerPathOrDefault(),
  68. //--debug:disable-sdk-templates means, don't include C:\Program Files\dotnet\templates, aka. what comes with SDK, so we don't need to uninstall
  69. //--debug:custom-hive means, don't install templates on CI/developer machine, instead create new temporary instance
  70. $"new {arguments} --debug:disable-sdk-templates --debug:custom-hive \"{CustomHivePath}\"");
  71. await proc.Exited;
  72. return proc;
  73. }
  74. private static async Task InstallTemplatePackages(ITestOutputHelper output)
  75. {
  76. string packagesDir;
  77. if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("helix")))
  78. {
  79. packagesDir = ".";
  80. }
  81. else
  82. {
  83. packagesDir = typeof(TemplatePackageInstaller).Assembly
  84. .GetCustomAttributes<AssemblyMetadataAttribute>()
  85. .Single(a => a.Key == "ArtifactsShippingPackagesDir").Value;
  86. }
  87. var builtPackages = Directory.EnumerateFiles(packagesDir, "*Templates*.nupkg")
  88. .Where(p => _templatePackages.Any(t => Path.GetFileName(p).StartsWith(t, StringComparison.OrdinalIgnoreCase)))
  89. .ToArray();
  90. if (builtPackages.Length == 0)
  91. {
  92. throw new InvalidOperationException($"Failed to find required templates in {packagesDir}. Please ensure the *Templates*.nupkg have been built.");
  93. }
  94. Assert.Equal(4, builtPackages.Length);
  95. await VerifyCannotFindTemplateAsync(output, "web");
  96. await VerifyCannotFindTemplateAsync(output, "webapp");
  97. await VerifyCannotFindTemplateAsync(output, "webapi");
  98. await VerifyCannotFindTemplateAsync(output, "mvc");
  99. await VerifyCannotFindTemplateAsync(output, "react");
  100. await VerifyCannotFindTemplateAsync(output, "reactredux");
  101. await VerifyCannotFindTemplateAsync(output, "angular");
  102. foreach (var packagePath in builtPackages)
  103. {
  104. output.WriteLine($"Installing templates package {packagePath}...");
  105. var result = await RunDotNetNew(output, $"install \"{packagePath}\"");
  106. Assert.True(result.ExitCode == 0, result.GetFormattedOutput());
  107. }
  108. await VerifyCanFindTemplate(output, "webapp");
  109. await VerifyCanFindTemplate(output, "web");
  110. await VerifyCanFindTemplate(output, "webapi");
  111. await VerifyCanFindTemplate(output, "react");
  112. }
  113. private static async Task VerifyCanFindTemplate(ITestOutputHelper output, string templateName)
  114. {
  115. var proc = await RunDotNetNew(output, $"--list");
  116. if (!(proc.Output.Contains($" {templateName} ") || proc.Output.Contains($",{templateName}") || proc.Output.Contains($"{templateName},")))
  117. {
  118. throw new InvalidOperationException($"Couldn't find {templateName} as an option in {proc.Output}.");
  119. }
  120. }
  121. private static async Task VerifyCannotFindTemplateAsync(ITestOutputHelper output, string templateName)
  122. {
  123. // Verify we really did remove the previous templates
  124. var tempDir = Path.Combine(AppContext.BaseDirectory, Path.GetRandomFileName(), Guid.NewGuid().ToString("D"));
  125. Directory.CreateDirectory(tempDir);
  126. try
  127. {
  128. var proc = await RunDotNetNew(output, $"\"{templateName}\"");
  129. if (!proc.Error.Contains("No templates or subcommands found matching:"))
  130. {
  131. throw new InvalidOperationException($"Failed to uninstall previous templates. The template '{templateName}' could still be found.");
  132. }
  133. }
  134. finally
  135. {
  136. Directory.Delete(tempDir, recursive: true);
  137. }
  138. }
  139. }