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

(#2500) List parameters in templates #2507

Merged
merged 1 commit into from
Aug 13, 2022
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 @@ -26,6 +26,7 @@ namespace chocolatey.tests.infrastructure.app.services
using chocolatey.infrastructure.app.services;
using chocolatey.infrastructure.app.templates;
using chocolatey.infrastructure.filesystem;
using chocolatey.infrastructure.services;
using Moq;
using NUnit.Framework;
using Should;
Expand All @@ -36,12 +37,14 @@ public abstract class TemplateServiceSpecsBase : TinySpec
{
protected TemplateService service;
protected Mock<IFileSystem> fileSystem = new Mock<IFileSystem>();
protected Mock<IXmlService> xmlService = new Mock<IXmlService>();

public override void Context()
{
fileSystem.ResetCalls();
xmlService.ResetCalls();

service = new TemplateService(fileSystem.Object);
service = new TemplateService(fileSystem.Object, xmlService.Object);
}
}

Expand Down
63 changes: 61 additions & 2 deletions src/chocolatey/infrastructure.app/services/TemplateService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace chocolatey.infrastructure.app.services
using System.Reflection;
using System.Text;
using configuration;
using infrastructure.services;
using logging;
using templates;
using tokens;
Expand All @@ -35,6 +36,7 @@ public class TemplateService : ITemplateService
private readonly UTF8Encoding utf8WithoutBOM = new UTF8Encoding(false);
private readonly IFileSystem _fileSystem;
private readonly ILogger _nugetLogger;
private readonly IXmlService _xmlService;

private readonly IList<string> _templateBinaryExtensions = new List<string> {
".exe", ".msi", ".msu", ".msp", ".mst",
Expand All @@ -45,10 +47,12 @@ public class TemplateService : ITemplateService

private readonly string _builtInTemplateOverrideName = "default";
private readonly string _builtInTemplateName = "built-in";
private readonly string _templateParameterCacheFilename = ".parameters";

public TemplateService(IFileSystem fileSystem)
public TemplateService(IFileSystem fileSystem, IXmlService xmlService)
{
_fileSystem = fileSystem;
_xmlService = xmlService;
}

public void generate_noop(ChocolateyConfiguration configuration)
Expand Down Expand Up @@ -148,6 +152,7 @@ public void generate(ChocolateyConfiguration configuration)
configuration.NewCommand.TemplateName = string.IsNullOrWhiteSpace(configuration.NewCommand.TemplateName) ? "default" : configuration.NewCommand.TemplateName;

var templatePath = _fileSystem.combine_paths(ApplicationParameters.TemplatesLocation, configuration.NewCommand.TemplateName);
var templateParameterCachePath = _fileSystem.combine_paths(templatePath, _templateParameterCacheFilename);
if (!_fileSystem.directory_exists(templatePath)) throw new ApplicationException("Unable to find path to requested template '{0}'. Path should be '{1}'".format_with(configuration.NewCommand.TemplateName, templatePath));

this.Log().Info(configuration.QuietOutput ? logger : ChocolateyLoggers.Important, "Generating package from custom template at '{0}'.".format_with(templatePath));
Expand All @@ -174,6 +179,10 @@ public void generate(ChocolateyConfiguration configuration)
this.Log().Debug(" Treating template file ('{0}') as binary instead of replacing templated values.".format_with(_fileSystem.get_file_name(file)));
_fileSystem.copy_file(file, packageFileLocation, overwriteExisting:true);
}
else if (templateParameterCachePath.is_equal_to(file))
{
this.Log().Debug("{0} is the parameter cache file, ignoring".format_with(file));
}
else
{
generate_file_from_template(configuration, tokens, _fileSystem.read_file(file), packageFileLocation, Encoding.UTF8);
Expand Down Expand Up @@ -280,6 +289,7 @@ protected void list_custom_template_info(ChocolateyConfiguration configuration,
.combine_paths(ApplicationParameters.TemplatesLocation, configuration.TemplateCommand.Name), "*", SearchOption.AllDirectories)));
var isOverridingBuiltIn = configuration.TemplateCommand.Name == _builtInTemplateOverrideName;
var isDefault = string.IsNullOrWhiteSpace(configuration.DefaultTemplateName) ? isOverridingBuiltIn : (configuration.DefaultTemplateName == configuration.TemplateCommand.Name);
var templateParams = " {0}".format_with(string.Join("{0} ".format_with(Environment.NewLine), get_template_parameters(configuration, templateInstalledViaPackage)));

if (configuration.RegularOutput)
{
Expand All @@ -292,14 +302,17 @@ protected void list_custom_template_info(ChocolateyConfiguration configuration,
{5}{6}
List of files:
{7}
List of Parameters:
{8}
".format_with(configuration.TemplateCommand.Name,
pkgVersion,
isDefault,
isOverridingBuiltIn ? "This template is overriding the built in template{0}".format_with(Environment.NewLine) : string.Empty,
pkgTitle,
string.IsNullOrEmpty(pkgSummary) ? "Template not installed as a package" : "Summary: {0}".format_with(pkgSummary),
string.IsNullOrEmpty(pkgDescription) ? string.Empty : "{0}Description:{0} {1}".format_with(Environment.NewLine, pkgDescription),
pkgFiles));
pkgFiles,
templateParams));
}
else
{
Expand Down Expand Up @@ -345,5 +358,51 @@ protected void list_built_in_template_info(ChocolateyConfiguration configuration
}
}
}

protected IEnumerable<string> get_template_parameters(ChocolateyConfiguration configuration, bool templateInstalledViaPackage)
{
// If the template was installed via package, the cache file gets removed on upgrade, so the cache file would be up to date if it exists
if (templateInstalledViaPackage)
{
var templateDirectory = _fileSystem.combine_paths(ApplicationParameters.TemplatesLocation, configuration.TemplateCommand.Name);
var cacheFilePath = _fileSystem.combine_paths(templateDirectory, _templateParameterCacheFilename);

if (!_fileSystem.file_exists(cacheFilePath))
{
_xmlService.serialize(get_template_parameters_from_files(configuration).ToList(), cacheFilePath);
}

return _xmlService.deserialize<List<string>>(cacheFilePath);
}
// If the template is not installed via a package, always read the parameters directly as the template may have been updated manually

return get_template_parameters_from_files(configuration).ToList();
}

protected HashSet<string> get_template_parameters_from_files(ChocolateyConfiguration configuration)
{
var filesList = _fileSystem.get_files(_fileSystem.combine_paths(ApplicationParameters.TemplatesLocation, configuration.TemplateCommand.Name), "*", SearchOption.AllDirectories);
var parametersList = new HashSet<string>();

foreach (var filePath in filesList)
{
if (_templateBinaryExtensions.Contains(_fileSystem.get_file_extension(filePath)))
{
this.Log().Debug("{0} is a binary file, not reading parameters".format_with(filePath));
continue;
}

if (_fileSystem.get_file_name(filePath) == _templateParameterCacheFilename)
{
this.Log().Debug("{0} is the parameter cache file, not reading parameters".format_with(filePath));
continue;
}

var fileContents = _fileSystem.read_file(filePath);
parametersList.UnionWith(TokenReplacer.get_tokens(fileContents, "[[", "]]"));
}

return parametersList;
}
}
}
11 changes: 11 additions & 0 deletions src/chocolatey/infrastructure/tokens/TokenReplacer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,16 @@ private static IDictionary<string, string> create_dictionary_from_configuration<

return propertyDictionary;
}

public static IEnumerable<string> get_tokens(string textWithTokens, string tokenPrefix = "[[", string tokenSuffix = "]]")
{
var regexMatches = Regex.Matches(textWithTokens, "{0}(?<key>\\w+){1}"
.format_with(Regex.Escape(tokenPrefix), Regex.Escape(tokenSuffix))
);
foreach (Match regexMatch in regexMatches)
{
yield return regexMatch.Groups["key"].to_string();
}
}
}
}