From dc18d245e0432d48b341338c2354ca59368bbecf Mon Sep 17 00:00:00 2001 From: Gary Ewan Park Date: Mon, 13 May 2019 08:00:08 +0100 Subject: [PATCH] (GH-357) Add Export Command This will allow the creation of a packages.config file of all the currently installed packages on the machine. Usage of this command will be: choco export -o c:/temp/packages.config --include-version-numbers This command is particularly useful when re-building a machine. i.e. First export all packages currently installed on machine, and then replay this packages.config via choco install packages.config on new machine. Co-authored-by: mwallner --- .../helpers/ChocolateyTabExpansion.ps1 | 3 +- src/chocolatey/chocolatey.csproj | 1 + .../commands/ChocolateyExportCommand.cs | 145 ++++++++++++++++++ .../configuration/ChocolateyConfiguration.cs | 16 ++ .../PackagesConfigFilePackageSetting.cs | 48 ++++++ .../registration/ContainerBinding.cs | 3 +- 6 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 src/chocolatey/infrastructure.app/commands/ChocolateyExportCommand.cs diff --git a/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1 b/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1 index 980d7e7f0a..e2a74cdb7e 100644 --- a/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1 +++ b/src/chocolatey.resources/helpers/ChocolateyTabExpansion.ps1 @@ -33,7 +33,7 @@ function script:chocoCmdOperations($commands, $command, $filter, $currentArgumen where { $_ -like "$filter*" } } -$script:someCommands = @('-?','search','list','info','install','outdated','upgrade','uninstall','new','download','optimize','pack','push','sync','-h','--help','pin','source','config','feature','apikey') +$script:someCommands = @('-?','search','list','info','install','outdated','upgrade','uninstall','new','download','optimize','pack','push','sync','-h','--help','pin','source','config','feature','apikey', 'export') # ensure these all have a space to start, or they will cause issues $allcommands = " --debug --verbose --trace --noop --help --accept-license --confirm --limit-output --no-progress --log-file='' --execution-timeout='' --cache-location='' --proxy='' --proxy-user='' --proxy-password='' --proxy-bypass-list='' --proxy-bypass-on-local --force --no-color" @@ -62,6 +62,7 @@ $commandOptions = @{ download = "--internalize --internalize-all-urls --ignore-dependencies --installed-packages --ignore-unfound-packages --resources-location='' --download-location='' --outputdirectory='' --source='' --version='' --prerelease --user='' --password='' --cert='' --certpassword='' --append-use-original-location --recompile --disable-package-repository-optimizations -?" + $allcommands sync = "--output-directory='' --id='' --package-id='' -?" + $allcommands optimize = "--deflate-nupkg-only --id='' -?" + $allcommands + export = "--output-file-path='' --include-version-numbers -?" + $allcommands } try { diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index fd7a2ad802..ae212d68f5 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -101,6 +101,7 @@ Properties\SolutionVersion.cs + diff --git a/src/chocolatey/infrastructure.app/commands/ChocolateyExportCommand.cs b/src/chocolatey/infrastructure.app/commands/ChocolateyExportCommand.cs new file mode 100644 index 0000000000..6c29a26966 --- /dev/null +++ b/src/chocolatey/infrastructure.app/commands/ChocolateyExportCommand.cs @@ -0,0 +1,145 @@ +// Copyright © 2017 - 2018 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.Diagnostics; + using System.IO; + using System.Text; + using System.Xml; + using attributes; + using chocolatey.infrastructure.services; + using commandline; + using configuration; + using filesystem; + using infrastructure.commands; + using logging; + using services; + using tolerance; + + [CommandFor("export", "exports list of currently installed packages")] + public class ChocolateyExportCommand : ICommand + { + private readonly INugetService _nugetService; + private readonly IFileSystem _fileSystem; + private readonly IXmlService _xmlService; + + public ChocolateyExportCommand(INugetService nugetService, IFileSystem fileSystem, IXmlService xmlService) + { + _nugetService = nugetService; + _fileSystem = fileSystem; + _xmlService = xmlService; + } + + public void configure_argument_parser(OptionSet optionSet, ChocolateyConfiguration configuration) + { + optionSet + .Add("o=|output=|output-file-path=", + "Output File Path - the path to where the list of currently installed packages should be saved. Defaults to packages.config.", + option => configuration.ExportCommand.OutputFilePath = option.remove_surrounding_quotes()) + .Add("include-version|include-version-numbers", + "Include Version Numbers - controls whether or not version numbers for each package appear in generated file. Defaults to false.", + option => configuration.ExportCommand.IncludeVersionNumbers = option != null) + ; + } + + public void handle_additional_argument_parsing(IList unparsedArguments, ChocolateyConfiguration configuration) + { + // Cuurently, no additional argument parsing is required + } + + public void handle_validation(ChocolateyConfiguration configuration) + { + // Currently, no additional validation is required. + } + + public void help_message(ChocolateyConfiguration configuration) + { + this.Log().Info(ChocolateyLoggers.Important, "Export Command"); + this.Log().Info(@" +Export all currently installed packages to a file. + +This is especially helpful when re-building a machine that was created +using Chocolatey. Export all packages to a file, and then re-install +those packages onto a new machine using `choco install packages.config`. +"); + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Usage"); + "chocolatey".Log().Info(@" + choco export [] +"); + + "chocolatey".Log().Info(ChocolateyLoggers.Important, "Examples"); + "chocolatey".Log().Info(@" + choco export + choco export --include-version-numbers + choco export --output-file-path=""'c:\temp\packages.config'"" + choco export --output-file-path=""'c:\temp\packages.config'"" --include-version-numbers + +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 bool may_require_admin_access() + { + return false; + } + + public void noop(ChocolateyConfiguration configuration) + { + this.Log().Info("Export would have been with options: {0} Output File Path={1}{0} Include Version Numbers:{2}".format_with(Environment.NewLine, configuration.ExportCommand.OutputFilePath, configuration.ExportCommand.IncludeVersionNumbers)); + } + + public void run(ChocolateyConfiguration configuration) + { + var packageResults = _nugetService.get_all_installed_packages(configuration); + var packagesConfig = new PackagesConfigFileSettings(); + + packagesConfig.Packages = new HashSet(); + + foreach (var packageResult in packageResults) + { + var packagesConfigFile = new PackagesConfigFilePackageSetting(); + packagesConfigFile.Id = packageResult.Package.Id; + packagesConfigFile.Version = packageResult.Package.Version.ToString(); + packagesConfigFile.IncludeVersion = configuration.ExportCommand.IncludeVersionNumbers; + + packagesConfig.Packages.Add(packagesConfigFile); + } + + _xmlService.serialize(packagesConfig, configuration.ExportCommand.OutputFilePath); + } + } +} diff --git a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs index c5cc33f73f..52bf9c74d7 100644 --- a/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs +++ b/src/chocolatey/infrastructure.app/configuration/ChocolateyConfiguration.cs @@ -50,6 +50,7 @@ public ChocolateyConfiguration() PinCommand = new PinCommandConfiguration(); OutdatedCommand = new OutdatedCommandConfiguration(); Proxy = new ProxyConfiguration(); + ExportCommand = new ExportCommandConfiguration(); #if DEBUG AllowUnofficialBuild = true; #endif @@ -334,6 +335,8 @@ private void append_output(StringBuilder propertyValues, string append) /// public OutdatedCommandConfiguration OutdatedCommand { get; set; } + public ExportCommandConfiguration ExportCommand { get; set; } + /// /// Configuration related specifically to proxies. /// @@ -545,4 +548,17 @@ public sealed class ProxyConfiguration public string BypassList { get; set; } public bool BypassOnLocal { get; set; } } + + [Serializable] + public sealed class ExportCommandConfiguration + { + public ExportCommandConfiguration() + { + OutputFilePath = "packages.config"; + } + + public bool IncludeVersionNumbers { get; set; } + + public string OutputFilePath { get; set; } + } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/configuration/PackagesConfigFilePackageSetting.cs b/src/chocolatey/infrastructure.app/configuration/PackagesConfigFilePackageSetting.cs index ac0a002055..765c299fef 100644 --- a/src/chocolatey/infrastructure.app/configuration/PackagesConfigFilePackageSetting.cs +++ b/src/chocolatey/infrastructure.app/configuration/PackagesConfigFilePackageSetting.cs @@ -58,5 +58,53 @@ public sealed class PackagesConfigFilePackageSetting [XmlAttribute(AttributeName = "disabled")] public bool Disabled { get; set; } + + [XmlIgnore] + public bool IncludeVersion { get; set; } + + public bool ShouldSerializeVersion() + { + return IncludeVersion; + } + + public bool ShouldSerializeInstallArguments() + { + return false; + } + + public bool ShouldSerializePackageParameters() + { + return false; + } + + public bool ShouldSerializeApplyPackageParametersToDependencies() + { + return false; + } + + public bool ShouldSerializeApplyInstallArgumentsToDependencies() + { + return false; + } + + public bool ShouldSerializeForceX86() + { + return false; + } + + public bool ShouldSerializeAllowMultipleVersions() + { + return false; + } + + public bool ShouldSerializeIgnoreDependencies() + { + return false; + } + + public bool ShouldSerializeDisabled() + { + return false; + } } } diff --git a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs index 67ffc551b5..405be6193f 100644 --- a/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs +++ b/src/chocolatey/infrastructure.app/registration/ContainerBinding.cs @@ -95,7 +95,8 @@ public void RegisterComponents(Container container) new ChocolateyApiKeyCommand(container.GetInstance()), new ChocolateyUnpackSelfCommand(container.GetInstance()), new ChocolateyVersionCommand(container.GetInstance()), - new ChocolateyUpdateCommand(container.GetInstance()) + new ChocolateyUpdateCommand(container.GetInstance()), + new ChocolateyExportCommand(container.GetInstance(), container.GetInstance(), container.GetInstance()) }; return list.AsReadOnly(); }, Lifestyle.Singleton);