Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Component-based template validation PoC #5838

Merged
merged 4 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Microsoft.TemplateEngine.Abstractions
/// <summary>
/// Represents a localization file for a template.
/// </summary>
public interface ILocalizationLocator
public interface ILocalizationLocator : IValidationInfo
{
/// <summary>
/// Gets the locale of the localizations.
Expand Down
5 changes: 5 additions & 0 deletions src/Microsoft.TemplateEngine.Abstractions/IValidationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ namespace Microsoft.TemplateEngine.Abstractions
{
public interface IValidationInfo
{
/// <summary>
/// <see langword="true"/> when the template is valid to be used.
/// </summary>
bool IsValid { get; }

/// <summary>
/// Gets the results of template validation.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
Microsoft.TemplateEngine.Abstractions.IExtendedTemplateLocator
Microsoft.TemplateEngine.Abstractions.ITemplate.TemplateSourceRoot.get -> Microsoft.TemplateEngine.Abstractions.Mount.IDirectory!
Microsoft.TemplateEngine.Abstractions.IValidationInfo.IsValid.get -> bool
Microsoft.TemplateEngine.Abstractions.IVariableCollection
Microsoft.TemplateEngine.Abstractions.IVariableCollection.Parent.get -> Microsoft.TemplateEngine.Abstractions.IVariableCollection?
Microsoft.TemplateEngine.Abstractions.IVariableCollection.Parent.set -> void
Expand Down
72 changes: 72 additions & 0 deletions src/Microsoft.TemplateEngine.Edge/LocalizableStrings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions src/Microsoft.TemplateEngine.Edge/LocalizableStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,38 @@ Details: {1}.</value>
<data name="Scanner_Error_TemplatePackageLocationIsNotSupported" xml:space="preserve">
<value>Template package location {0} is not supported, or doesn't exist.</value>
</data>
<data name="Scanner_Validation_Error_Header" xml:space="preserve">
<value>The template {0} has the following validation errors:</value>
<comment>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity)</comment>
</data>
<data name="Scanner_Validation_Info_Header" xml:space="preserve">
<value>The template {0} has the following validation messages:</value>
<comment>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity)</comment>
</data>
<data name="Scanner_Validation_InvalidTemplate" xml:space="preserve">
<value>Failed to install the template {0}: the template is not valid.</value>
<comment>{0} template info in format 'template name' (template.identity)</comment>
</data>
<data name="Scanner_Validation_InvalidTemplateLoc" xml:space="preserve">
<value>Failed to install the '{0}' localization the template {1}: the localization file is not valid. The localization will be skipped.</value>
<comment>{1} template info in format 'template name' (template.identity), {0} is localization locale in "en-US" format</comment>
</data>
<data name="Scanner_Validation_LocError_Header" xml:space="preserve">
<value>The template {0} has the following validation errors in '{1}' localization:</value>
<comment>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity), {1} is localization locale in "en-US" format</comment>
</data>
<data name="Scanner_Validation_LocInfo_Header" xml:space="preserve">
<value>The template {0} has the following validation messages in '{1}' localization:</value>
<comment>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity), {1} is localization locale in "en-US" format</comment>
</data>
<data name="Scanner_Validation_LocWarning_Header" xml:space="preserve">
<value>The template {0} has the following validation warnings in '{1}' localization:</value>
<comment>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity), {1} is localization locale in "en-US" format</comment>
</data>
<data name="Scanner_Validation_Warning_Header" xml:space="preserve">
<value>The template {0} has the following validation warnings:</value>
<comment>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity)</comment>
</data>
<data name="SdkConstraint_Error_InvalidVersion" xml:space="preserve">
<value>'{0}' is not a valid semver version.</value>
<comment>{0} is the provided version</comment>
Expand Down
102 changes: 96 additions & 6 deletions src/Microsoft.TemplateEngine.Edge/Settings/Scanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
#if !NETFULL
Expand Down Expand Up @@ -214,16 +215,15 @@ private async Task<ScanResult> ScanMountPointForTemplatesAsync(MountPointScanSou
foreach (IGenerator generator in _environmentSettings.Components.OfType<IGenerator>())
{
IReadOnlyList<IScanTemplateInfo> templateList = await generator.GetTemplatesFromMountPointAsync(source.MountPoint, cancellationToken).ConfigureAwait(false);
foreach (IScanTemplateInfo template in templateList)
{
templates.Add(template);
}
LogScanningResults(source, templateList, generator);

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

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

Expand Down Expand Up @@ -281,6 +281,96 @@ private IEnumerable<KeyValuePair<string, Assembly>> LoadAllFromPath(
return loaded;
}

private void LogScanningResults(MountPointScanSource source, IReadOnlyList<IScanTemplateInfo> foundTemplates, IGenerator generator)
{
ILogger logger = _environmentSettings.Host.Logger;
logger.LogDebug("Scanning mount point '{0}' by generator '{1}': found {2} templates", source.MountPoint.MountPointUri, generator.Id, foundTemplates.Count);
foreach (IScanTemplateInfo template in foundTemplates)
{
string templateDisplayName = GetTemplateDisplayName(template);
logger.LogDebug("Found template {0}", templateDisplayName);

LogValidationEntries(
logger,
string.Format(LocalizableStrings.Scanner_Validation_Error_Header, templateDisplayName),
template.ValidationErrors,
IValidationEntry.SeverityLevel.Error);
LogValidationEntries(
logger,
string.Format(LocalizableStrings.Scanner_Validation_Warning_Header, templateDisplayName),
template.ValidationErrors,
IValidationEntry.SeverityLevel.Warning);
LogValidationEntries(
logger,
string.Format(LocalizableStrings.Scanner_Validation_Info_Header, templateDisplayName),
template.ValidationErrors,
IValidationEntry.SeverityLevel.Info);

foreach (KeyValuePair<string, ILocalizationLocator> locator in template.Localizations)
{
ILocalizationLocator localizationInfo = locator.Value;

LogValidationEntries(
logger,
string.Format(LocalizableStrings.Scanner_Validation_LocError_Header, templateDisplayName, localizationInfo.Locale),
localizationInfo.ValidationErrors,
IValidationEntry.SeverityLevel.Error);
LogValidationEntries(
logger,
string.Format(LocalizableStrings.Scanner_Validation_LocWarning_Header, templateDisplayName, localizationInfo.Locale),
localizationInfo.ValidationErrors,
IValidationEntry.SeverityLevel.Warning);
LogValidationEntries(
logger,
string.Format(LocalizableStrings.Scanner_Validation_LocInfo_Header, templateDisplayName, localizationInfo.Locale),
localizationInfo.ValidationErrors,
IValidationEntry.SeverityLevel.Info);
}

if (!template.IsValid)
{
logger.LogError(LocalizableStrings.Scanner_Validation_InvalidTemplate, templateDisplayName);
}
foreach (ILocalizationLocator invalidLoc in template.Localizations.Values.Where(li => !li.IsValid))
{
logger.LogWarning(LocalizableStrings.Scanner_Validation_InvalidTemplateLoc, invalidLoc.Locale, templateDisplayName);
}
}

static string GetTemplateDisplayName(IScanTemplateInfo template)
{
string templateName = string.IsNullOrEmpty(template.Name) ? "<no name>" : template.Name;
return $"'{templateName}' ({template.Identity})";
}

static string PrintError(IValidationEntry error) => $" [{error.Severity}][{error.Code}] {error.ErrorMessage}";

static void LogValidationEntries(ILogger logger, string header, IReadOnlyList<IValidationEntry> errors, IValidationEntry.SeverityLevel severity)
{
Action<string> log = severity switch
{
IValidationEntry.SeverityLevel.None => (string s) => throw new NotSupportedException($"{IValidationEntry.SeverityLevel.None} severity is not supported."),
IValidationEntry.SeverityLevel.Info => (string s) => logger.LogDebug(s),
IValidationEntry.SeverityLevel.Warning => (string s) => logger.LogWarning(s),
IValidationEntry.SeverityLevel.Error => (string s) => logger.LogError(s),
_ => throw new InvalidOperationException($"{severity} is not expected value for {nameof(IValidationEntry.SeverityLevel)}."),
};

if (!errors.Any(e => e.Severity == severity))
{
return;
}

StringBuilder sb = new();
sb.AppendLine(header);
foreach (IValidationEntry error in errors.Where(e => e.Severity == severity))
{
sb.AppendLine(PrintError(error));
}
log(sb.ToString());
}
}

private class MountPointScanSource
{
public MountPointScanSource(string location, IMountPoint mountPoint, bool shouldStayInOriginalLocation, bool foundComponents, bool foundTemplates)
Expand Down
40 changes: 40 additions & 0 deletions src/Microsoft.TemplateEngine.Edge/xlf/LocalizableStrings.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,46 @@ Podrobnosti: {1}.</target>
<target state="translated">Umístění balíčku šablony {0} se buď nepodporuje, nebo neexistuje.</target>
<note />
</trans-unit>
<trans-unit id="Scanner_Validation_Error_Header">
<source>The template {0} has the following validation errors:</source>
<target state="new">The template {0} has the following validation errors:</target>
<note>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity)</note>
</trans-unit>
<trans-unit id="Scanner_Validation_Info_Header">
<source>The template {0} has the following validation messages:</source>
<target state="new">The template {0} has the following validation messages:</target>
<note>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity)</note>
</trans-unit>
<trans-unit id="Scanner_Validation_InvalidTemplate">
<source>Failed to install the template {0}: the template is not valid.</source>
<target state="new">Failed to install the template {0}: the template is not valid.</target>
<note>{0} template info in format 'template name' (template.identity)</note>
</trans-unit>
<trans-unit id="Scanner_Validation_InvalidTemplateLoc">
<source>Failed to install the '{0}' localization the template {1}: the localization file is not valid. The localization will be skipped.</source>
<target state="new">Failed to install the '{0}' localization the template {1}: the localization file is not valid. The localization will be skipped.</target>
<note>{1} template info in format 'template name' (template.identity), {0} is localization locale in "en-US" format</note>
</trans-unit>
<trans-unit id="Scanner_Validation_LocError_Header">
<source>The template {0} has the following validation errors in '{1}' localization:</source>
<target state="new">The template {0} has the following validation errors in '{1}' localization:</target>
<note>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity), {1} is localization locale in "en-US" format</note>
</trans-unit>
<trans-unit id="Scanner_Validation_LocInfo_Header">
<source>The template {0} has the following validation messages in '{1}' localization:</source>
<target state="new">The template {0} has the following validation messages in '{1}' localization:</target>
<note>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity), {1} is localization locale in "en-US" format</note>
</trans-unit>
<trans-unit id="Scanner_Validation_LocWarning_Header">
<source>The template {0} has the following validation warnings in '{1}' localization:</source>
<target state="new">The template {0} has the following validation warnings in '{1}' localization:</target>
<note>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity), {1} is localization locale in "en-US" format</note>
</trans-unit>
<trans-unit id="Scanner_Validation_Warning_Header">
<source>The template {0} has the following validation warnings:</source>
<target state="new">The template {0} has the following validation warnings:</target>
<note>This is a header for the list of issues found for the template, and followed by the list of issues (from new line). {0} template info in format 'template name' (template.identity)</note>
</trans-unit>
<trans-unit id="SdkConstraint_Error_InvalidVersion">
<source>'{0}' is not a valid semver version.</source>
<target state="translated">{0} není platná verze semver.</target>
Expand Down
Loading