File: tests\Shared\WorkloadTesting\TemplateCustomHive.cs
Web Access
Project: src\tests\Aspire.EndToEnd.Tests\Aspire.EndToEnd.Tests.csproj (Aspire.EndToEnd.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text.RegularExpressions;
 
namespace Aspire.Workload.Tests;
 
public class TemplatesCustomHive
{
    private static readonly string s_tmpDirSuffix = Guid.NewGuid().ToString()[..8];
 
    public static TemplatesCustomHive TemplatesHive { get; } = new([TemplatePackageIdNames.AspireProjectTemplates], "templates");
 
    private readonly string _stampFilePath;
    private readonly string _customHiveDirectory;
 
    public string[] TemplatePackageIds { get; init; }
    public string CustomHiveDirectory => File.Exists(_stampFilePath)
                                            ? _customHiveDirectory
                                            : throw new InvalidOperationException($"TemplatesCustomHive has not been installed yet in {_customHiveDirectory}");
 
    public TemplatesCustomHive(string[] templatePackageIds, string customHiveDirName)
    {
        TemplatePackageIds = templatePackageIds ?? throw new ArgumentNullException(nameof(templatePackageIds));
 
        ArgumentException.ThrowIfNullOrEmpty(customHiveDirName, nameof(customHiveDirName));
 
        var customHiveBaseDirectory = BuildEnvironment.IsRunningOnCI
                                        ? Path.Combine(Path.GetTempPath(), $"templates-${s_tmpDirSuffix}")
                                        : Path.Combine(AppContext.BaseDirectory, "templates");
        _customHiveDirectory = Path.Combine(customHiveBaseDirectory, customHiveDirName);
        _stampFilePath = Path.Combine(_customHiveDirectory, ".stamp-installed");
    }
 
    public async Task EnsureInstalledAsync(BuildEnvironment buildEnvironment)
    {
        if (BuildEnvironment.IsRunningOnCI && File.Exists(_stampFilePath))
        {
            // On CI, don't check if the packages need to be updated
            Console.WriteLine($"** Custom hive exists, skipping installation: {_customHiveDirectory}");
            return;
        }
 
        // For local runs, we can skip the installation if nothing has changed
        var packageIdAndPaths =
                TemplatePackageIds
                    .Select(id => GetPackagePath(buildEnvironment.BuiltNuGetsPath, id))
                    .Zip(TemplatePackageIds, (path, id) => (path, id));
 
        var installTemplates = true;
        if (File.Exists(_stampFilePath))
        {
            // Installation exists, but check if any of the packages have been updated since
            var dirWriteTime = Directory.GetLastWriteTimeUtc(_customHiveDirectory);
            installTemplates = packageIdAndPaths.Where(t => new FileInfo(t.path).LastWriteTimeUtc > dirWriteTime).Any();
        }
 
        if (!installTemplates)
        {
            Console.WriteLine($"** Custom hive exists, skipping installation: {_customHiveDirectory}");
            return;
        }
 
        if (Directory.Exists(_customHiveDirectory))
        {
            Directory.Delete(_customHiveDirectory, recursive: true);
        }
 
        Console.WriteLine($"*** Creating templates custom hive: {_customHiveDirectory}");
        Directory.CreateDirectory(_customHiveDirectory);
 
        foreach (var (packagePath, templatePackageId) in packageIdAndPaths)
        {
            await InstallTemplatesAsync(
                    packagePath,
                    customHiveDirectory: _customHiveDirectory,
                    dotnet: buildEnvironment.DotNet);
        }
 
        File.WriteAllText(_stampFilePath, "");
    }
 
    public static string GetPackagePath(string builtNuGetsPath, string templatePackageId)
    {
        var packageNameRegex = new Regex($@"{templatePackageId}\.\d+\.\d+\.\d+(-[A-z\.\d]*\.*\d*)?\.nupkg");
        var packages = Directory.EnumerateFiles(builtNuGetsPath, $"{templatePackageId}*.nupkg")
                        .Where(p => packageNameRegex.IsMatch(Path.GetFileName(p)));
 
        if (!packages.Any())
        {
            throw new ArgumentException($"Cannot find {templatePackageId}*.nupkg in {builtNuGetsPath}. Packages found in {builtNuGetsPath}: {string.Join(", ", Directory.EnumerateFiles(builtNuGetsPath).Select(Path.GetFileName))}");
        }
        if (packages.Count() > 1)
        {
            throw new ArgumentException($"Found more than one {templatePackageId}*.nupkg in {builtNuGetsPath}: {string.Join(", ", packages.Select(Path.GetFileName))}");
        }
        return packages.Single();
    }
 
    public static async Task<CommandResult> InstallTemplatesAsync(string packagePath, string customHiveDirectory, string dotnet)
    {
        var installCmd = $"new install --debug:custom-hive {customHiveDirectory} {packagePath}";
        using var cmd = new ToolCommand(dotnet,
                                        new TestOutputWrapper(forceShowBuildOutput: true),
                                        label: "template install")
                            .WithWorkingDirectory(BuildEnvironment.IsRunningOnCI
                                ? Path.GetTempPath()
                                : Path.Combine(BuildEnvironment.TempDir, "templates", "working")); // avoid running from the repo
 
        var res = await cmd.ExecuteAsync(installCmd);
        res.EnsureSuccessful();
        return res;
    }
}