From dfabcc3481471fbd12abd6673a9520af73a4d88f Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 09:04:15 -0500 Subject: [PATCH 1/6] (GH-305) Clean up installer types - Add valid exit codes - Switch to return the same install args/uninstall args with a base class that defines the properties and command builders. - Override those custom builders for CustomInstaller, which should return nothing b/c it is unknown what it would return. --- src/chocolatey/chocolatey.csproj | 1 + .../domain/CustomInstaller.cs | 31 ++++------ .../infrastructure.app/domain/IInstaller.cs | 5 +- .../domain/InnoSetupInstaller.cs | 30 ++-------- .../domain/InstallShieldInstaller.cs | 31 ++-------- .../domain/InstallerBase.cs | 56 +++++++++++++++++++ .../infrastructure.app/domain/MsiInstaller.cs | 35 +++--------- .../domain/NsisInstaller.cs | 31 ++-------- 8 files changed, 96 insertions(+), 124 deletions(-) create mode 100644 src/chocolatey/infrastructure.app/domain/InstallerBase.cs diff --git a/src/chocolatey/chocolatey.csproj b/src/chocolatey/chocolatey.csproj index 2daec58295..d820e9b50b 100644 --- a/src/chocolatey/chocolatey.csproj +++ b/src/chocolatey/chocolatey.csproj @@ -95,6 +95,7 @@ + diff --git a/src/chocolatey/infrastructure.app/domain/CustomInstaller.cs b/src/chocolatey/infrastructure.app/domain/CustomInstaller.cs index e4b98242dc..fe991be4b1 100644 --- a/src/chocolatey/infrastructure.app/domain/CustomInstaller.cs +++ b/src/chocolatey/infrastructure.app/domain/CustomInstaller.cs @@ -15,10 +15,9 @@ namespace chocolatey.infrastructure.app.domain { - using System; using System.Collections.Generic; - public class CustomInstaller : IInstaller + public class CustomInstaller : InstallerBase { public CustomInstaller() { @@ -32,34 +31,26 @@ public CustomInstaller() UninstallExecutable = "\"{0}\"".format_with(InstallTokens.UNINSTALLER_LOCATION); SilentUninstall = "/S"; OtherUninstallOptions = ""; - ValidExitCodes = new List {0}; + ValidInstallExitCodes = new List { 0 }; + ValidUninstallExitCodes = new List { 0 }; } - public InstallerType InstallerType + public override InstallerType InstallerType { get { return InstallerType.Custom; } } - public string InstallExecutable { get; private set; } - public string SilentInstall { get; private set; } - public string NoReboot { get; private set; } - public string LogFile { get; private set; } - public string CustomInstallLocation { get; private set; } - public string Language { get; private set; } - public string OtherInstallOptions { get; private set; } - public string UninstallExecutable { get; private set; } - public string SilentUninstall { get; private set; } - public string OtherUninstallOptions { get; private set; } - public IEnumerable ValidExitCodes { get; private set; } - - public string build_install_command_arguments() + public override string build_install_command_arguments(bool customInstallLocation, bool languageRequested) { - throw new NotImplementedException(); + if (customInstallLocation) this.Log().Warn("CustomInstaller doesn't support custom install locations."); + if (languageRequested) this.Log().Warn("CustomInstaller doesn't support custom language options."); + + return string.Empty; } - public string build_uninstall_command_arguments() + public override string build_uninstall_command_arguments() { - return "{0} {1}".format_with(SilentUninstall, OtherInstallOptions); + return string.Empty; } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/domain/IInstaller.cs b/src/chocolatey/infrastructure.app/domain/IInstaller.cs index 273a88a852..84e3363cad 100644 --- a/src/chocolatey/infrastructure.app/domain/IInstaller.cs +++ b/src/chocolatey/infrastructure.app/domain/IInstaller.cs @@ -30,9 +30,10 @@ public interface IInstaller string UninstallExecutable { get; } string SilentUninstall { get; } string OtherUninstallOptions { get; } - IEnumerable ValidExitCodes { get; } + IEnumerable ValidInstallExitCodes { get; } + IEnumerable ValidUninstallExitCodes { get; } - string build_install_command_arguments(); + string build_install_command_arguments(bool customInstallLocation, bool languageRequested); string build_uninstall_command_arguments(); } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/domain/InnoSetupInstaller.cs b/src/chocolatey/infrastructure.app/domain/InnoSetupInstaller.cs index 1a5ec4d1e8..7428c3debf 100644 --- a/src/chocolatey/infrastructure.app/domain/InnoSetupInstaller.cs +++ b/src/chocolatey/infrastructure.app/domain/InnoSetupInstaller.cs @@ -24,7 +24,7 @@ namespace chocolatey.infrastructure.app.domain /// /// http://www.jrsoftware.org/ishelp/index.php?topic=setupcmdline /// - public class InnoSetupInstaller : IInstaller + public class InnoSetupInstaller : InstallerBase { public InnoSetupInstaller() { @@ -38,34 +38,14 @@ public InnoSetupInstaller() UninstallExecutable = "\"{0}\"".format_with(InstallTokens.UNINSTALLER_LOCATION); SilentUninstall = "/VERYSILENT"; OtherUninstallOptions = "/SUPPRESSMSGBOXES"; - ValidExitCodes = new List {0}; + // http://www.jrsoftware.org/ishelp/index.php?topic=setupexitcodes + ValidInstallExitCodes = new List { 0 }; + ValidUninstallExitCodes = new List { 0 }; } - public InstallerType InstallerType + public override InstallerType InstallerType { get { return InstallerType.InnoSetup; } } - - public string InstallExecutable { get; private set; } - public string SilentInstall { get; private set; } - public string NoReboot { get; private set; } - public string LogFile { get; private set; } - public string CustomInstallLocation { get; private set; } - public string Language { get; private set; } - public string OtherInstallOptions { get; private set; } - public string UninstallExecutable { get; private set; } - public string SilentUninstall { get; private set; } - public string OtherUninstallOptions { get; private set; } - public IEnumerable ValidExitCodes { get; private set; } - - public string build_install_command_arguments() - { - throw new NotImplementedException(); - } - - public string build_uninstall_command_arguments() - { - return "{0} {1} {2}".format_with(SilentUninstall, NoReboot, OtherUninstallOptions); - } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/domain/InstallShieldInstaller.cs b/src/chocolatey/infrastructure.app/domain/InstallShieldInstaller.cs index 8b009073b3..f8be43d96d 100644 --- a/src/chocolatey/infrastructure.app/domain/InstallShieldInstaller.cs +++ b/src/chocolatey/infrastructure.app/domain/InstallShieldInstaller.cs @@ -17,6 +17,7 @@ namespace chocolatey.infrastructure.app.domain { using System; using System.Collections.Generic; + using System.Text; /// /// InstallShield Installer Options @@ -24,7 +25,7 @@ namespace chocolatey.infrastructure.app.domain /// /// http://helpnet.installshield.com/installshield18helplib/ihelpsetup_execmdline.htm /// - public class InstallShieldInstaller : IInstaller + public class InstallShieldInstaller : InstallerBase { public InstallShieldInstaller() { @@ -38,34 +39,14 @@ public InstallShieldInstaller() UninstallExecutable = "\"{0}\"".format_with(InstallTokens.UNINSTALLER_LOCATION); SilentUninstall = "/uninst /s"; OtherUninstallOptions = "/sms"; - ValidExitCodes = new List {0}; + // http://helpnet.installshield.com/installshield18helplib/IHelpSetup_EXEErrors.htm + ValidInstallExitCodes = new List { 0, 1641, 3010 }; + ValidUninstallExitCodes = new List { 0, 1641, 3010 }; } - public InstallerType InstallerType + public override InstallerType InstallerType { get { return InstallerType.InstallShield; } } - - public string InstallExecutable { get; private set; } - public string SilentInstall { get; private set; } - public string NoReboot { get; private set; } - public string LogFile { get; private set; } - public string CustomInstallLocation { get; private set; } - public string Language { get; private set; } - public string OtherInstallOptions { get; private set; } - public string UninstallExecutable { get; private set; } - public string SilentUninstall { get; private set; } - public string OtherUninstallOptions { get; private set; } - public IEnumerable ValidExitCodes { get; private set; } - - public string build_install_command_arguments() - { - throw new NotImplementedException(); - } - - public string build_uninstall_command_arguments() - { - return "{0} {1} {2}".format_with(SilentUninstall, NoReboot, OtherUninstallOptions); - } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/domain/InstallerBase.cs b/src/chocolatey/infrastructure.app/domain/InstallerBase.cs new file mode 100644 index 0000000000..1fa8333102 --- /dev/null +++ b/src/chocolatey/infrastructure.app/domain/InstallerBase.cs @@ -0,0 +1,56 @@ +// Copyright © 2011 - Present 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 +{ + using System.Collections.Generic; + using System.Text; + + public abstract class InstallerBase : IInstaller + { + public abstract InstallerType InstallerType { get; } + + public string InstallExecutable { get; protected set; } + public string SilentInstall { get; protected set; } + public string NoReboot { get; protected set; } + public string LogFile { get; protected set; } + public string OtherInstallOptions { get; protected set; } + public string CustomInstallLocation { get; protected set; } + public string Language { get; protected set; } + public string UninstallExecutable { get; protected set; } + public string SilentUninstall { get; protected set; } + public string OtherUninstallOptions { get; protected set; } + public IEnumerable ValidInstallExitCodes { get; protected set; } + public IEnumerable ValidUninstallExitCodes { get; protected set; } + + public virtual string build_install_command_arguments(bool customInstallLocation, bool languageRequested) + { + var args = new StringBuilder(); + args.AppendFormat("{0} {1} {2}", SilentInstall, NoReboot, LogFile); + if (languageRequested) args.AppendFormat(" {0}", Language); + args.AppendFormat(" {0}", OtherInstallOptions); + + // custom install location must be last for NSIS + if (customInstallLocation) args.AppendFormat(" {0}", CustomInstallLocation); + + return args.ToString(); + } + + public virtual string build_uninstall_command_arguments() + { + return "{0} {1} {2} {3}".format_with(SilentUninstall, NoReboot, LogFile, OtherUninstallOptions); + } + } +} \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/domain/MsiInstaller.cs b/src/chocolatey/infrastructure.app/domain/MsiInstaller.cs index b082cde4e9..2a974517e7 100644 --- a/src/chocolatey/infrastructure.app/domain/MsiInstaller.cs +++ b/src/chocolatey/infrastructure.app/domain/MsiInstaller.cs @@ -15,7 +15,6 @@ namespace chocolatey.infrastructure.app.domain { - using System; using System.Collections.Generic; /// @@ -28,7 +27,7 @@ namespace chocolatey.infrastructure.app.domain /// http://www.advancedinstaller.com/user-guide/msiexec.html /// 1603 search for return value 3 http://blogs.msdn.com/b/astebner/archive/2005/08/01/446328.aspx /// - public class MsiInstaller : IInstaller + public class MsiInstaller : InstallerBase { public MsiInstaller() { @@ -44,37 +43,21 @@ public MsiInstaller() // http://msdn.microsoft.com/en-us/library/aa367559.aspx OtherInstallOptions = "ALLUSERS=1 DISABLEDESKTOPSHORTCUT=1 ADDDESKTOPICON=0 ADDSTARTMENU=0"; UninstallExecutable = "msiexec.exe"; + //todo: eventually will need this //SilentUninstall = "/qn /x{0}".format_with(InstallTokens.UNINSTALLER_LOCATION); SilentUninstall = "/qn"; OtherUninstallOptions = ""; - ValidExitCodes = new List {0, 3010}; + // https://msdn.microsoft.com/en-us/library/aa376931.aspx + // https://support.microsoft.com/en-us/kb/290158 + ValidInstallExitCodes = new List {0, 1641, 3010}; + // we allow unknown 1605/1614 b/c it may have already been uninstalled + // and that's okay + ValidUninstallExitCodes = new List {0, 1605, 1614, 1641, 3010}; } - public InstallerType InstallerType + public override InstallerType InstallerType { get { return InstallerType.Msi; } } - - public string InstallExecutable { get; private set; } - public string SilentInstall { get; private set; } - public string NoReboot { get; private set; } - public string LogFile { get; private set; } - public string OtherInstallOptions { get; private set; } - public string CustomInstallLocation { get; private set; } - public string Language { get; private set; } - public string UninstallExecutable { get; private set; } - public string SilentUninstall { get; private set; } - public string OtherUninstallOptions { get; private set; } - public IEnumerable ValidExitCodes { get; private set; } - - public string build_install_command_arguments() - { - throw new NotImplementedException(); - } - - public string build_uninstall_command_arguments() - { - return "{0} {1} {2}".format_with(SilentUninstall, NoReboot, OtherUninstallOptions); - } } } \ No newline at end of file diff --git a/src/chocolatey/infrastructure.app/domain/NsisInstaller.cs b/src/chocolatey/infrastructure.app/domain/NsisInstaller.cs index a0b4f7c05f..0d2e6e1a92 100644 --- a/src/chocolatey/infrastructure.app/domain/NsisInstaller.cs +++ b/src/chocolatey/infrastructure.app/domain/NsisInstaller.cs @@ -15,7 +15,6 @@ namespace chocolatey.infrastructure.app.domain { - using System; using System.Collections.Generic; /// @@ -26,7 +25,7 @@ namespace chocolatey.infrastructure.app.domain /// It is impossible to look at registry and determine a NSIS installer /// NSIS has no logging or language options. The command line usage is very little. /// - public class NsisInstaller : IInstaller + public class NsisInstaller : InstallerBase { public NsisInstaller() { @@ -34,40 +33,20 @@ public NsisInstaller() SilentInstall = "/S"; NoReboot = ""; LogFile = ""; + // must come last and contain no quotes, even if there are spaces CustomInstallLocation = "/D={0}".format_with(InstallTokens.CUSTOM_INSTALL_LOCATION); //must be last thing specified and no quotes Language = ""; OtherInstallOptions = ""; UninstallExecutable = "\"{0}\"".format_with(InstallTokens.UNINSTALLER_LOCATION); SilentUninstall = "/S"; OtherUninstallOptions = ""; - ValidExitCodes = new List {0}; + ValidInstallExitCodes = new List { 0 }; + ValidUninstallExitCodes = new List { 0 }; } - public InstallerType InstallerType + public override InstallerType InstallerType { get { return InstallerType.Nsis; } } - - public string InstallExecutable { get; private set; } - public string SilentInstall { get; private set; } - public string NoReboot { get; private set; } - public string LogFile { get; private set; } - public string CustomInstallLocation { get; private set; } - public string Language { get; private set; } - public string OtherInstallOptions { get; private set; } - public string UninstallExecutable { get; private set; } - public string SilentUninstall { get; private set; } - public string OtherUninstallOptions { get; private set; } - public IEnumerable ValidExitCodes { get; private set; } - - public string build_install_command_arguments() - { - throw new NotImplementedException(); - } - - public string build_uninstall_command_arguments() - { - return "{0} {1}".format_with(SilentUninstall, OtherInstallOptions); - } } } \ No newline at end of file From b82436bff554bc435b2a515cce28a4f561441c35 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 09:57:41 -0500 Subject: [PATCH 2/6] (GH-304) Auto Installer allow system cleanup If the preceeding chocolatelyUninstall.ps1 uninstalls an application, sometimes the system is still cleaning up files and registry keys. Those keys are found by the auto installer and it decides to also move forward with the uninstall. In cases like that, the auto uninstaller should wait a few seconds before moving forward with checks. --- .../services/AutomaticUninstallerService.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs index 35ed3932bd..638b953869 100644 --- a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs +++ b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs @@ -18,6 +18,7 @@ namespace chocolatey.infrastructure.app.services using System; using System.Collections.Generic; using System.Linq; + using System.Threading; using configuration; using domain; using filesystem; @@ -30,6 +31,7 @@ public class AutomaticUninstallerService : IAutomaticUninstallerService private readonly IFileSystem _fileSystem; private readonly IRegistryService _registryService; private readonly ICommandExecutor _commandExecutor; + private const int SLEEP_TIME = 5; public AutomaticUninstallerService(IChocolateyPackageInformationService packageInfoService, IFileSystem fileSystem, IRegistryService registryService, ICommandExecutor commandExecutor) { @@ -63,6 +65,8 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) } this.Log().Info(" Running auto uninstaller..."); + this.Log().Debug("Sleeping for {0} seconds to allow Windows to finish cleaning up.".format_with(SLEEP_TIME)); + Thread.Sleep((int)TimeSpan.FromSeconds(SLEEP_TIME).TotalMilliseconds); foreach (var key in registryKeys.or_empty_list_if_null()) { From 763ac498364ecd6b9ac6240d70c57ccccca62849 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 10:07:21 -0500 Subject: [PATCH 3/6] (maint) formatting --- .../infrastructure.app/services/AutomaticUninstallerService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs index 638b953869..be16b913af 100644 --- a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs +++ b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs @@ -119,7 +119,7 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) if (key.InstallerType == InstallerType.Msi) { - //because sometimes the key is set with /i to allow for modify :/ + // because sometimes the key is set with /i to allow for modify :/ uninstallArgs = uninstallArgs.Replace("/I{", "/X{"); uninstallArgs = uninstallArgs.Replace("/i{", "/X{"); uninstallArgs = uninstallArgs.Replace("/I ", "/X "); From 44431e6be5b7c20aa2da16a247ab2570202b3c4b Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 10:09:19 -0500 Subject: [PATCH 4/6] (GH-305) Find the installer type every time Only build uninstall command args if there is no silent uninstall key, but allow setting up the proper uninstaller for other actions. --- .../AutomaticUninstallerServiceSpecs.cs | 9 ++- .../services/AutomaticUninstallerService.cs | 69 ++++++++++--------- 2 files changed, 44 insertions(+), 34 deletions(-) diff --git a/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs b/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs index 6cadf4ae80..8b9facb3a7 100644 --- a/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs +++ b/src/chocolatey.tests/infrastructure.app/services/AutomaticUninstallerServiceSpecs.cs @@ -58,6 +58,7 @@ public override void Context() CommandExecutor.initialize_with(new Lazy(() => fileSystem.Object), () => process.Object); service = new AutomaticUninstallerService(packageInfoService.Object, fileSystem.Object, registryService.Object, commandExecutor.Object); + service.WaitForCleanup = false; config.Features.AutoUninstaller = true; package.Setup(p => p.Id).Returns("regular"); package.Setup(p => p.Version).Returns(new SemanticVersion("1.2.0")); @@ -307,6 +308,7 @@ public class when_AutomaticUninstallerService_defines_uninstall_switches : Autom { private Action because; private readonly string registryUninstallArgs = "/bob"; + private readonly string logLocation = "c:\\yes\\dude\\1.2.3-beta"; public override void Because() { @@ -332,12 +334,15 @@ private void test_installertype(IInstaller installer, bool hasQuietUninstallStri InstallerType = installer.InstallerType, }); packageInformation.RegistrySnapshot = new Registry("123", registryKeys); + fileSystem.Setup(x => x.combine_paths(config.CacheLocation, It.IsAny(), It.IsAny())).Returns(logLocation); because(); - var uninstallArgs = !hasQuietUninstallString ? registryUninstallArgs.trim_safe() + " " + installer.build_uninstall_command_arguments().trim_safe() : registryUninstallArgs.trim_safe(); + var installerTypeArgs = installer.build_uninstall_command_arguments().trim_safe().Replace(InstallTokens.PACKAGE_LOCATION, logLocation); - commandExecutor.Verify(c => c.execute(expectedUninstallString, uninstallArgs, It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny()), Times.Once); + var uninstallArgs = !hasQuietUninstallString ? registryUninstallArgs.trim_safe() + " " + installerTypeArgs : registryUninstallArgs.trim_safe(); + + commandExecutor.Verify(c => c.execute(expectedUninstallString, uninstallArgs.trim_safe(), It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny()), Times.Once); } [Fact] diff --git a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs index be16b913af..68fddae73a 100644 --- a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs +++ b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs @@ -39,8 +39,11 @@ public AutomaticUninstallerService(IChocolateyPackageInformationService packageI _fileSystem = fileSystem; _registryService = registryService; _commandExecutor = commandExecutor; + WaitForCleanup = true; } + public bool WaitForCleanup { get; set; } + public void run(PackageResult packageResult, ChocolateyConfiguration config) { if (!config.Features.AutoUninstaller) @@ -65,9 +68,12 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) } this.Log().Info(" Running auto uninstaller..."); - this.Log().Debug("Sleeping for {0} seconds to allow Windows to finish cleaning up.".format_with(SLEEP_TIME)); - Thread.Sleep((int)TimeSpan.FromSeconds(SLEEP_TIME).TotalMilliseconds); - + if (WaitForCleanup) + { + this.Log().Debug("Sleeping for {0} seconds to allow Windows to finish cleaning up.".format_with(SLEEP_TIME)); + Thread.Sleep((int)TimeSpan.FromSeconds(SLEEP_TIME).TotalMilliseconds); + } + foreach (var key in registryKeys.or_empty_list_if_null()) { this.Log().Debug(() => " Preparing uninstall key '{0}'".format_with(key.UninstallString)); @@ -86,37 +92,28 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) var uninstallArgs = key.UninstallString.to_string().Replace(uninstallExe.to_string(), string.Empty); uninstallExe = uninstallExe.remove_surrounding_quotes(); this.Log().Debug(() => " Uninstaller path is '{0}'".format_with(uninstallExe)); - - if (!key.HasQuietUninstall) + + + IInstaller installer = new CustomInstaller(); + + switch (key.InstallerType) { - IInstaller installer = new CustomInstaller(); - - switch (key.InstallerType) - { - case InstallerType.Msi: - installer = new MsiInstaller(); - break; - case InstallerType.InnoSetup: - installer = new InnoSetupInstaller(); - break; - case InstallerType.Nsis: - installer = new NsisInstaller(); - break; - case InstallerType.InstallShield: - installer = new InstallShieldInstaller(); - break; - default: - // skip - break; - } - - this.Log().Debug(() => " Installer type is '{0}'".format_with(installer.GetType().Name)); - - - //todo: ultimately we should merge keys with logging - uninstallArgs += " " + installer.build_uninstall_command_arguments(); + case InstallerType.Msi: + installer = new MsiInstaller(); + break; + case InstallerType.InnoSetup: + installer = new InnoSetupInstaller(); + break; + case InstallerType.Nsis: + installer = new NsisInstaller(); + break; + case InstallerType.InstallShield: + installer = new InstallShieldInstaller(); + break; } + this.Log().Debug(() => " Installer type is '{0}'".format_with(installer.GetType().Name)); + if (key.InstallerType == InstallerType.Msi) { // because sometimes the key is set with /i to allow for modify :/ @@ -126,6 +123,14 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) uninstallArgs = uninstallArgs.Replace("/i ", "/X "); } + if (!key.HasQuietUninstall) + { + //todo: ultimately we should merge keys + uninstallArgs += " " + installer.build_uninstall_command_arguments(); + var logLocation = _fileSystem.combine_paths(config.CacheLocation, pkgInfo.Package.Id, pkgInfo.Package.Version.to_string()); + uninstallArgs = uninstallArgs.Replace(InstallTokens.PACKAGE_LOCATION, logLocation); + } + this.Log().Debug(() => " Args are '{0}'".format_with(uninstallArgs)); var exitCode = _commandExecutor.execute( @@ -153,7 +158,7 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) } else { - this.Log().Info(() => " Auto uninstaller has successfully uninstalled {0} from your machine.".format_with(packageResult.Package.Id)); + this.Log().Info(() => " Auto uninstaller has successfully uninstalled {0} or detected previous uninstall.".format_with(packageResult.Package.Id)); } } } From 0dc79e37951d0ea57c62ee0331c3abd30b143453 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 10:12:58 -0500 Subject: [PATCH 5/6] (GH-305) Installer Type Validate Exit Code When an uninstall finishes, the exit code of zero may not be the only valid exit code. Allow the installer type to validate the exit code against its valid uninstall exit codes. This will catch things like with MSI with 0, 1641, and 3010 (all successful codes), and also 1605 and 1641 (for when the product was not installed, perhaps uninstalled by a different means) as also valid since the end state is that the application is no longer installed. --- .../infrastructure.app/services/AutomaticUninstallerService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs index 68fddae73a..41ceb166e3 100644 --- a/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs +++ b/src/chocolatey/infrastructure.app/services/AutomaticUninstallerService.cs @@ -149,7 +149,7 @@ public void run(PackageResult packageResult, ChocolateyConfiguration config) }, updateProcessPath: false); - if (exitCode != 0) + if (!installer.ValidUninstallExitCodes.Contains(exitCode)) { Environment.ExitCode = exitCode; string logMessage = " Auto uninstaller failed. Please remove machine installation manually."; From c1ff5e180ece80b52e086df88aed26b6ee221a88 Mon Sep 17 00:00:00 2001 From: Rob Reynolds Date: Thu, 4 Jun 2015 10:14:07 -0500 Subject: [PATCH 6/6] (GH-296) add 1605 for valid MSI exit code --- .../infrastructure.app/templates/ChocolateyUninstallTemplate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chocolatey/infrastructure.app/templates/ChocolateyUninstallTemplate.cs b/src/chocolatey/infrastructure.app/templates/ChocolateyUninstallTemplate.cs index 6ed25ccb55..6ad4c2a71b 100644 --- a/src/chocolatey/infrastructure.app/templates/ChocolateyUninstallTemplate.cs +++ b/src/chocolatey/infrastructure.app/templates/ChocolateyUninstallTemplate.cs @@ -65,7 +65,7 @@ public class ChocolateyUninstallTemplate # Uninstall-ChocolateyPackage msiargs (facepalm). $silentArgs = ""$msiProductCodeGuid /qn /norestart"" # https://msdn.microsoft.com/en-us/library/aa376931(v=vs.85).aspx -$validExitCodes = @(0, 3010, 1614, 1641) +$validExitCodes = @(0, 3010, 1605, 1614, 1641) # Don't pass anything for file, it is ignored for msi (facepalm number 2) # Alternatively if you need to pass a path to an msi, determine that and use # it instead of $msiProductCodeGuid in silentArgs, still very first