diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index eea25ebe2d..1301daa138 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -113,6 +113,8 @@ Properties\SolutionVersion.cs + + diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyTemplateCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyTemplateCommand.cs new file mode 100644 index 0000000000..110c5bf10b --- /dev/null +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyTemplateCommand.cs @@ -0,0 +1,157 @@ +// Copyright © 2017 - 2021 Chocolatey Software, Inc +// Copyright © 2011 - 2017 RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.commands +{ + using System; + using System.Collections.Generic; + using System.Linq; + using attributes; + using commandline; + using configuration; + using domain; + using infrastructure.commands; + using logging; + using services; + using templates; + + [CommandFor("template", "get information about installed templates")] + [CommandFor("templates", "get information about installed templates (alias for template)")] + public class ChocolateyTemplateCommand : ICommand + { + private readonly ITemplateService _templateService; + + public ChocolateyTemplateCommand(ITemplateService templateService) + { + _templateService = templateService; + } + + public void configure_argument_parser(OptionSet optionSet, ChocolateyConfiguration configuration) + { + optionSet + .Add("n=|name=", + "The name of the template to get information about.", + option => configuration.TemplateCommand.Name = option.remove_surrounding_quotes().ToLower()); + // TODO Allow for templates from external path? If PR 1477 is merged + } + + public virtual void handle_additional_argument_parsing(IList unparsedArguments, ChocolateyConfiguration configuration) + { + // don't set configuration.Input or it will be passed to list + + if (unparsedArguments.Count > 1) + { + throw new ApplicationException("A single template command must be listed. Please see the help menu for those commands"); + } + + var command = TemplateCommandType.unknown; + string unparsedCommand = unparsedArguments.DefaultIfEmpty(string.Empty).FirstOrDefault(); + Enum.TryParse(unparsedCommand, true, out command); + + if (command == TemplateCommandType.unknown) + { + if (!string.IsNullOrWhiteSpace(unparsedCommand)) this.Log().Warn("Unknown command {0}. Setting to list.".format_with(unparsedCommand)); + command = TemplateCommandType.list; + } + + configuration.TemplateCommand.Command = command; + } + + public virtual void handle_validation(ChocolateyConfiguration configuration) + { + if (configuration.TemplateCommand.Command != TemplateCommandType.list && string.IsNullOrWhiteSpace(configuration.TemplateCommand.Name)) + { + throw new ApplicationException("When specifying the subcommand '{0}', you must also specify --name.".format_with(configuration.TemplateCommand.Command.to_string())); + } + } + + public virtual void help_message(ChocolateyConfiguration configuration) + { + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Template Command"); + "chocolatey".Log().Info(@" +List information installed templates. + +Both manually installed templates and templates installed via + .template packages are displayed."); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Usage"); + "chocolatey".Log().Info(@" + choco pin [list]|info []"); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Examples"); + "chocolatey".Log().Info(@" + choco template + choco templates + choco template list + choco template info --name msi + choco template list --reduce-output + choco template list --verbose + +NOTE: See scripting in the command reference (`choco -?`) for how to + write proper scripts and integrations. + +"); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Exit Codes"); + "chocolatey".Log().Info(@" +Exit codes that normally result from running this command. + +Normal: + - 0: operation was successful, no issues detected + - -1 or 1: an error has occurred + +If you find other exit codes that we have not yet documented, please + file a ticket so we can document it at + https://github.com/chocolatey/choco/issues/new/choose. + +"); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Options and Switches"); + } + + public virtual void noop(ChocolateyConfiguration configuration) + { + switch (configuration.TemplateCommand.Command) + { + case TemplateCommandType.list: + _templateService.list_noop(configuration); + break; + case TemplateCommandType.info: + _templateService.list_noop(configuration); + break; + } + } + + public virtual void run(ChocolateyConfiguration configuration) + { + switch (configuration.TemplateCommand.Command) + { + case TemplateCommandType.list: + _templateService.list(configuration); + break; + case TemplateCommandType.info: + configuration.Verbose = true; + _templateService.list(configuration); + break; + } + } + + public virtual bool may_require_admin_access() + { + return false; + } + } +} \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index c4ec782330..f597e7b1fe 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -51,6 +51,7 @@ public ChocolateyConfiguration() OutdatedCommand = new OutdatedCommandConfiguration(); Proxy = new ProxyConfiguration(); ExportCommand = new ExportCommandConfiguration(); + TemplateCommand = new TemplateCommandConfiguration(); #if DEBUG AllowUnofficialBuild = true; #endif @@ -345,6 +346,14 @@ private void append_output(StringBuilder propertyValues, string append) /// On .NET 4.0, get error CS0200 when private set - see http://stackoverflow.com/a/23809226/18475 /// public ProxyConfiguration Proxy { get; set; } + + /// + /// Configuration related specifically to Template command + /// + /// + /// On .NET 4.0, get error CS0200 when private set - see http://stackoverflow.com/a/23809226/18475 + /// + public TemplateCommandConfiguration TemplateCommand { get; set; } } [Serializable] @@ -557,4 +566,11 @@ public sealed class ExportCommandConfiguration public string OutputFilePath { get; set; } } + + [Serializable] + public sealed class TemplateCommandConfiguration + { + public TemplateCommandType Command { get; set; } + public string Name { get; set; } + } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/domain/TemplateCommandType.cs b/src/chocolatey/infrastructure.app/domain/TemplateCommandType.cs new file mode 100644 index 0000000000..8f4cb5bcc1 --- /dev/null +++ b/src/chocolatey/infrastructure.app/domain/TemplateCommandType.cs @@ -0,0 +1,25 @@ +// Copyright © 2017 - 2021 Chocolatey Software, Inc +// Copyright © 2011 - 2017 RealDimensions Software, LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace chocolatey.infrastructure.app.domain +{ + public enum TemplateCommandType + { + unknown, + list, + info + } +} diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index a849a379ee..8e94690ee9 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -97,7 +97,8 @@ public void RegisterComponents(Container container) new ChocolateyUnpackSelfCommand(container.GetInstance()), new ChocolateyVersionCommand(container.GetInstance()), new ChocolateyUpdateCommand(container.GetInstance()), - new ChocolateyExportCommand(container.GetInstance(), container.GetInstance()) + new ChocolateyExportCommand(container.GetInstance(), container.GetInstance()), + new ChocolateyTemplateCommand(container.GetInstance()) }; return list.AsReadOnly(); }, Lifestyle.Singleton); diff --git a/src/chocolatey/infrastructure.app/services/ITemplateService.cs b/src/chocolatey/infrastructure.app/services/ITemplateService.cs index abfe00dfa0..ec757dd50d 100644 --- a/src/chocolatey/infrastructure.app/services/ITemplateService.cs +++ b/src/chocolatey/infrastructure.app/services/ITemplateService.cs @@ -22,5 +22,7 @@ public interface ITemplateService { void generate_noop(ChocolateyConfiguration configuration); void generate(ChocolateyConfiguration configuration); + void list_noop(ChocolateyConfiguration configuration); + void list(ChocolateyConfiguration configuration); } } diff --git a/src/chocolatey/infrastructure.app/services/TemplateService.cs b/src/chocolatey/infrastructure.app/services/TemplateService.cs index 7a68b491fc..12e986ff2e 100644 --- a/src/chocolatey/infrastructure.app/services/TemplateService.cs +++ b/src/chocolatey/infrastructure.app/services/TemplateService.cs @@ -19,18 +19,23 @@ namespace chocolatey.infrastructure.app.services using System; using System.Collections.Generic; using System.IO; + using System.Linq; using System.Reflection; using System.Text; using configuration; - using filesystem; using logging; using templates; using tokens; + using NuGet; + using nuget; + using IFileSystem = filesystem.IFileSystem; public class TemplateService : ITemplateService { private readonly UTF8Encoding utf8WithoutBOM = new UTF8Encoding(false); private readonly IFileSystem _fileSystem; + private readonly ILogger _nugetLogger; + private readonly IList _templateBinaryExtensions = new List { ".exe", ".msi", ".msu", ".msp", ".mst", ".7z", ".zip", ".rar", ".gz", ".iso", ".tar", ".sfx", @@ -38,6 +43,9 @@ public class TemplateService : ITemplateService ".cer", ".crt", ".der", ".p7b", ".pfx", ".p12", ".pem" }; + private readonly string _builtInTemplateOverrideName = "default"; + private readonly string _builtInTemplateName = "built-in"; + public TemplateService(IFileSystem fileSystem) { _fileSystem = fileSystem; @@ -186,5 +194,162 @@ public void generate_file_from_template(ChocolateyConfiguration configuration, T _fileSystem.create_directory_if_not_exists(_fileSystem.get_directory_name(fileLocation)); _fileSystem.write_file(fileLocation, template, encoding); } + + public void list_noop(ChocolateyConfiguration configuration) + { + if (string.IsNullOrWhiteSpace(configuration.TemplateCommand.Name)) + { + this.Log().Info(() => "Would have listed templates in {0}\\templates".format_with(ApplicationParameters.InstallLocation)); + } + else + { + this.Log().Info(() => "Would have listed information about {0}".format_with(configuration.TemplateCommand.Name)); + } + } + + public void list(ChocolateyConfiguration configuration) + { + + var packageManager = NugetCommon.GetPackageManager(configuration, _nugetLogger, + new PackageDownloader(), + installSuccessAction: null, + uninstallSuccessAction: null, + addUninstallHandler: false); + + var templateDirList = _fileSystem.get_directories(ApplicationParameters.TemplatesLocation).ToList(); + var isBuiltInTemplateOverriden = templateDirList.Contains(_fileSystem.combine_paths(ApplicationParameters.TemplatesLocation, _builtInTemplateOverrideName)); + var isBuiltInOrDefaultTemplateDefault = string.IsNullOrWhiteSpace(configuration.DefaultTemplateName) || !templateDirList.Contains(_fileSystem.combine_paths(ApplicationParameters.TemplatesLocation, configuration.DefaultTemplateName)); + + if (string.IsNullOrWhiteSpace(configuration.TemplateCommand.Name)) + { + + if (templateDirList.Any()) + { + foreach (var templateDir in templateDirList) + { + configuration.TemplateCommand.Name = _fileSystem.get_file_name(templateDir); + list_custom_template_info(configuration, packageManager); + } + + this.Log().Info(configuration.RegularOutput ? "{0} Custom templates found at {1}{2}".format_with(templateDirList.Count(), ApplicationParameters.TemplatesLocation, Environment.NewLine) : string.Empty); + } + else + { + this.Log().Info(configuration.RegularOutput ? "No custom templates installed in {0}{2}".format_with(ApplicationParameters.TemplatesLocation, Environment.NewLine) : string.Empty); + } + + list_built_in_template_info(configuration, isBuiltInTemplateOverriden, isBuiltInOrDefaultTemplateDefault); + } + else + { + if (templateDirList.Contains(_fileSystem.combine_paths(ApplicationParameters.TemplatesLocation, configuration.TemplateCommand.Name))) + { + list_custom_template_info(configuration, packageManager); + if (configuration.TemplateCommand.Name == _builtInTemplateName || configuration.TemplateCommand.Name == _builtInTemplateOverrideName) + { + list_built_in_template_info(configuration, isBuiltInTemplateOverriden, isBuiltInOrDefaultTemplateDefault); + } + } + else + { + if (configuration.TemplateCommand.Name.ToLowerInvariant() == _builtInTemplateName || configuration.TemplateCommand.Name.ToLowerInvariant() == _builtInTemplateOverrideName) + { + // We know that the template is not overriden since the template directory was checked + list_built_in_template_info(configuration, isBuiltInTemplateOverriden, isBuiltInOrDefaultTemplateDefault); + } + else + { + throw new ApplicationException("Unable to find requested template '{0}'".format_with(configuration.TemplateCommand.Name)); + } + } + } + } + + protected void list_custom_template_info(ChocolateyConfiguration configuration, IPackageManager packageManager) + { + + var pkg = packageManager.LocalRepository.FindPackage("{0}.template".format_with(configuration.TemplateCommand.Name)); + var pkgVersion = (pkg == null) ? "0.0.0" : pkg.Version.to_string(); + var pkgTitle = (pkg == null) ? "{0} (Unmanaged)".format_with(configuration.TemplateCommand.Name) : pkg.Title; + var pkgSummary = (pkg == null) ? + string.Empty : (pkg.Summary != null && !string.IsNullOrWhiteSpace(pkg.Summary.to_string()) ? "{0}".format_with(pkg.Summary.escape_curly_braces().to_string()) : string.Empty); + var pkgDescription = (pkg == null) ? string.Empty : pkg.Description.escape_curly_braces().Replace("\n ", "\n").Replace("\n", "\n "); + var pkgFiles = " {0}".format_with(string.Join("{0} " + .format_with(Environment.NewLine), _fileSystem.get_files(_fileSystem + .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); + + if (configuration.RegularOutput) + { + if (configuration.Verbose) + { + // TODO list parameters, do later (save to file?) + this.Log().Info(@"Template name: {0} +Version: {1} +Default template: {2} +{3}Title: {4} +{5}{6} +List of files: +{7} +List of Parameters: + Will be implemented at a later time +".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)); + } + else + { + this.Log().Info("{0} {1} {2}".format_with((isDefault ? '*' : ' '), configuration.TemplateCommand.Name, pkgVersion)); + } + } + else + { + this.Log().Info("{0}|{1}".format_with(configuration.TemplateCommand.Name, pkgVersion)); + } + } + + protected void list_built_in_template_info(ChocolateyConfiguration configuration, bool isOverridden, bool isDefault) + { + if (configuration.RegularOutput) + { + if (isOverridden) + { + this.Log().Info("Built-in template overriden by 'default' template.{0}".format_with(Environment.NewLine)); + } + else + { + if (isDefault) + { + this.Log().Info("Built-in template is default.{0}".format_with(Environment.NewLine)); + } + else + { + this.Log().Info("Built-in template is not default, it can be specified if the --built-in parameter is used{0}".format_with(Environment.NewLine)); + } + + } + + if (configuration.Verbose) + { + this.Log().Info("Help about the built-in template can be found with 'choco new --help'{0}".format_with(Environment.NewLine)); + } + } + else + { + //If reduced output, only print out the built in template if it is not overriden + if (!isOverridden) + { + this.Log().Info("built-in|0.0.0"); + } + } + + } } }