Skip to content

Commit

Permalink
MSBuild validate task PoC
Browse files Browse the repository at this point in the history
  • Loading branch information
vlada-shubina committed Mar 15, 2023
1 parent 348b46c commit ec25b81
Show file tree
Hide file tree
Showing 13 changed files with 372 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/Microsoft.TemplateEngine.Edge/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Microsoft.TemplateEngine.Edge.DefaultEnvironment.DefaultEnvironment(System.Collections.Generic.IReadOnlyDictionary<string!, string!>! environmentVariables) -> void
Microsoft.TemplateEngine.Edge.Settings.Scanner.ScanAsync(string! mountPointUri, bool logValidationResults = true, bool returnInvalidTemplates = false, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Threading.Tasks.Task<Microsoft.TemplateEngine.Edge.Settings.ScanResult!>!
Microsoft.TemplateEngine.Edge.Settings.Scanner.ScanAsync(string! mountPointUri, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task<Microsoft.TemplateEngine.Edge.Settings.ScanResult!>!
Microsoft.TemplateEngine.Edge.Settings.ScanResult.Templates.get -> System.Collections.Generic.IReadOnlyList<Microsoft.TemplateEngine.Abstractions.IScanTemplateInfo!>!
Microsoft.TemplateEngine.Edge.Settings.ITemplateInfoHostJsonCache.HostData.get -> string?
Expand Down
38 changes: 33 additions & 5 deletions src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,28 @@ public Task<ScanResult> ScanAsync(string mountPointUri, CancellationToken cancel
}
MountPointScanSource source = GetOrCreateMountPointScanInfoForInstallSource(mountPointUri);
cancellationToken.ThrowIfCancellationRequested();
return ScanMountPointForTemplatesAsync(source, cancellationToken);
return ScanMountPointForTemplatesAsync(source, cancellationToken: cancellationToken);
}

/// <summary>
/// Scans mount point for templates.
/// </summary>
/// <remarks>
/// The mount point will not be disposed by the <see cref="Scanner"/>. Use <see cref="ScanResult.Dispose"/> to dispose mount point.
/// </remarks>
public Task<ScanResult> ScanAsync(
string mountPointUri,
bool logValidationResults = true,
bool returnInvalidTemplates = false,
CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(mountPointUri))
{
throw new ArgumentException($"{nameof(mountPointUri)} should not be null or empty");
}
MountPointScanSource source = GetOrCreateMountPointScanInfoForInstallSource(mountPointUri);
cancellationToken.ThrowIfCancellationRequested();
return ScanMountPointForTemplatesAsync(source, logValidationResults, returnInvalidTemplates, cancellationToken);
}

private MountPointScanSource GetOrCreateMountPointScanInfoForInstallSource(string sourceLocation)
Expand Down Expand Up @@ -207,23 +228,30 @@ private bool TryCopyForNonFileSystemBasedMountPoints(IMountPoint mountPoint, str
return true;
}

private async Task<ScanResult> ScanMountPointForTemplatesAsync(MountPointScanSource source, CancellationToken cancellationToken)
private async Task<ScanResult> ScanMountPointForTemplatesAsync(
MountPointScanSource source,
bool logValidationResults = true,
bool returnInvalidTemplates = false,
CancellationToken cancellationToken = default)
{
_ = source ?? throw new ArgumentNullException(nameof(source));

var templates = new List<IScanTemplateInfo>();
foreach (IGenerator generator in _environmentSettings.Components.OfType<IGenerator>())
{
IReadOnlyList<IScanTemplateInfo> templateList = await generator.GetTemplatesFromMountPointAsync(source.MountPoint, cancellationToken).ConfigureAwait(false);
LogScanningResults(source, templateList, generator);
if (logValidationResults)
{
LogScanningResults(source, templateList, generator);
}

IEnumerable<IScanTemplateInfo> validTemplates = templateList.Where(t => t.IsValid);
IEnumerable<IScanTemplateInfo> validTemplates = templateList.Where(t => t.IsValid || returnInvalidTemplates);
templates.AddRange(validTemplates);
source.FoundTemplates |= validTemplates.Any();
}

//backward compatibility
var localizationLocators = templates.SelectMany(t => t.Localizations.Values.Where(li => li.IsValid)).ToList();
var localizationLocators = templates.SelectMany(t => t.Localizations.Values.Where(li => li.IsValid || returnInvalidTemplates)).ToList();
return new ScanResult(source.MountPoint, templates, localizationLocators, Array.Empty<(string, Type, IIdentifiedComponent)>());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<PackageType>Template</PackageType>
<PackageId>TemplatePackage</PackageId>
<Authors>Microsoft</Authors>
<Description>TemplatePackage</Description>
<IncludeContentInPack>true</IncludeContentInPack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
<NoWarn>$(NoWarn);NU5128</NoWarn>
<IsPackable>true</IsPackable>
<IsShipping>false</IsShipping>
<NoDefaultExcludes>true</NoDefaultExcludes>
<LocalizeTemplates>true</LocalizeTemplates>
</PropertyGroup>
<ItemGroup>
<Compile Remove="**\*" />
<Content Include="content\**\*" Exclude="content\**\bin\**;content\**\obj\**" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"author": "Test Asset",
"classifications": [ "Test Asset" ],
"description": "Test description",
"generatorVersions": "[1.0.0.0-*)",
"groupIdentity": "TestAssets.TemplateWithSourceName",
"precedence": "100",
"identity": "TestAssets.TemplateWithSourceName",
"sourceName": "bar"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TemplateWithSourceName
{
internal class Foo
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<PackageType>Template</PackageType>
<PackageId>TemplatePackage</PackageId>
<Authors>Microsoft</Authors>
<Description>TemplatePackage</Description>
<IncludeContentInPack>true</IncludeContentInPack>
<IncludeBuildOutput>false</IncludeBuildOutput>
<ContentTargetFolders>content</ContentTargetFolders>
<NoWarn>$(NoWarn);NU5128</NoWarn>
<IsPackable>true</IsPackable>
<IsShipping>false</IsShipping>
<NoDefaultExcludes>true</NoDefaultExcludes>
<LocalizeTemplates>true</LocalizeTemplates>
</PropertyGroup>
<ItemGroup>
<Compile Remove="**\*" />
<Content Include="content\**\*" Exclude="content\**\bin\**;content\**\obj\**" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "TemplateWithSourceName",
"description": "Test description",
"generatorVersions": "[1.0.0.0-*)",
"groupIdentity": "TestAssets.TemplateWithSourceName",
"precedence": "100",
"identity": "TestAssets.TemplateWithSourceName",
"shortName": "TestAssets.TemplateWithSourceName",
"sourceName": "bar"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace TemplateWithSourceName
{
internal class Foo
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using FluentAssertions;
using Microsoft.TemplateEngine.CommandUtils;
using Microsoft.TemplateEngine.TestHelper;
using Microsoft.TemplateEngine.Tests;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.TemplateEngine.Authoring.Tasks.IntegrationTests
{
public class ValidateTemplatesTests : TestBase
{
private readonly ITestOutputHelper _log;

public ValidateTemplatesTests(ITestOutputHelper log)
{
_log = log;
}

[Fact]
public void CanRunValidateTask_OnError()
{
string tmpDir = TestUtils.CreateTemporaryFolder();
TestUtils.DirectoryCopy("Resources/InvalidTemplatePackage_MissingName", tmpDir, true);
TestUtils.SetupNuGetConfigForPackagesLocation(tmpDir, ShippingPackagesLocation);

new DotnetCommand(_log, "add", "TemplatePackage.csproj", "package", "Microsoft.TemplateEngine.Authoring.Tasks", "--prerelease")
.WithoutTelemetry()
.WithWorkingDirectory(tmpDir)
.Execute()
.Should()
.Pass();

new DotnetCommand(_log, "build")
.WithoutTelemetry()
.WithWorkingDirectory(tmpDir)
.Execute()
.Should()
.Fail()
.And.HaveStdOutContaining("Template configuration error MV002: Missing 'name'.")
.And.HaveStdOutContaining("Template configuration error MV003: Missing 'shortName'.");
}

[Fact]
public void CanRunValidateTask_OnInfo()
{
string tmpDir = TestUtils.CreateTemporaryFolder();
TestUtils.DirectoryCopy("Resources/InvalidTemplatePackage_MissingOptionalData", tmpDir, true);
TestUtils.SetupNuGetConfigForPackagesLocation(tmpDir, ShippingPackagesLocation);

new DotnetCommand(_log, "add", "TemplatePackage.csproj", "package", "Microsoft.TemplateEngine.Authoring.Tasks", "--prerelease")
.WithoutTelemetry()
.WithWorkingDirectory(tmpDir)
.Execute()
.Should()
.Pass();

new DotnetCommand(_log, "build")
.WithoutTelemetry()
.WithWorkingDirectory(tmpDir)
.Execute()
.Should()
.Pass()
.And.HaveStdOutContaining("Template configuration message MV006: Missing 'author'.")
.And.HaveStdOutContaining("Template configuration message MV010: Missing 'classifications'.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
<PackageReference Include="Microsoft.Build.Utilities.Core" />
<PackageReference Update="@(PackageReference)" PrivateAssets="All" />
<ProjectReference Include="$(ToolsDir)\Microsoft.TemplateEngine.TemplateLocalizer.Core\Microsoft.TemplateEngine.TemplateLocalizer.Core.csproj" />
<ProjectReference Include="$(SrcDir)Microsoft.TemplateEngine.Edge\Microsoft.TemplateEngine.Edge.csproj" />
<ProjectReference Include="$(SrcDir)Microsoft.TemplateEngine.Orchestrator.RunnableProjects\Microsoft.TemplateEngine.Orchestrator.RunnableProjects.csproj" />
<ProjectReference Update="@(ProjectReference)" PrivateAssets="All" />
</ItemGroup>

Expand Down
Loading

0 comments on commit ec25b81

Please sign in to comment.