private PowerShellExecutionResults run_external_powershell(ChocolateyConfiguration configuration, string chocoPowerShellScript) { var result = new PowerShellExecutionResults(); result.ExitCode = PowershellExecutor.execute( wrap_script_with_module(chocoPowerShellScript, configuration), _fileSystem, configuration.CommandExecutionTimeoutSeconds, (s, e) => { if (string.IsNullOrWhiteSpace(e.Data)) { return; } //inspect for different streams if (e.Data.StartsWith("DEBUG:")) { this.Log().Debug(() => " " + e.Data.escape_curly_braces()); } else if (e.Data.StartsWith("WARNING:")) { this.Log().Warn(() => " " + e.Data.escape_curly_braces()); } else if (e.Data.StartsWith("VERBOSE:")) { this.Log().Info(ChocolateyLoggers.Verbose, () => " " + e.Data.escape_curly_braces()); } else { this.Log().Info(() => " " + e.Data.escape_curly_braces()); } }, (s, e) => { if (string.IsNullOrWhiteSpace(e.Data)) { return; } result.StandardErrorWritten = true; this.Log().Error(() => " " + e.Data.escape_curly_braces()); }); return(result); }
public PowerShellExecutionResults run_host(ChocolateyConfiguration config, string chocoPowerShellScript, Action <Pipeline> additionalActionsBeforeScript) { // since we control output in the host, always set these true Environment.SetEnvironmentVariable("ChocolateyEnvironmentDebug", "true"); Environment.SetEnvironmentVariable("ChocolateyEnvironmentVerbose", "true"); var result = new PowerShellExecutionResults(); string commandToRun = wrap_script_with_module(chocoPowerShellScript, config); var host = new PoshHost(config); this.Log().Debug(() => "Calling built-in PowerShell host with ['{0}']".format_with(commandToRun.escape_curly_braces())); var initialSessionState = InitialSessionState.CreateDefault(); // override system execution policy without accidentally setting it initialSessionState.AuthorizationManager = new AuthorizationManager("choco"); using (var runspace = RunspaceFactory.CreateRunspace(host, initialSessionState)) { runspace.Open(); // this will affect actual execution policy //RunspaceInvoke invoker = new RunspaceInvoke(runspace); //invoker.Invoke("Set-ExecutionPolicy ByPass"); using (var pipeline = runspace.CreatePipeline()) { // The powershell host itself handles the following items: // * Write-Debug // * Write-Host // * Write-Verbose // * Write-Warning // // the two methods below will pick up Write-Output and Write-Error // Write-Output pipeline.Output.DataReady += (sender, args) => { PipelineReader <PSObject> reader = sender as PipelineReader <PSObject>; if (reader != null) { while (reader.Count > 0) { host.UI.WriteLine(reader.Read().to_string().escape_curly_braces()); } } }; // Write-Error pipeline.Error.DataReady += (sender, args) => { PipelineReader <object> reader = sender as PipelineReader <object>; if (reader != null) { while (reader.Count > 0) { host.UI.WriteErrorLine(reader.Read().to_string().escape_curly_braces()); } } }; var documentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments, Environment.SpecialFolderOption.DoNotVerify); var currentUserCurrentHostProfile = _fileSystem.combine_paths(documentsFolder, "WindowsPowerShell\\Microsoft.PowerShell_profile.ps1"); var recreateProfileScript = @" if ((Test-Path(""{0}"")) -and ($profile -eq $null -or $profile -eq '')) {{ $global:profile = ""{1}"" }} ".format_with(documentsFolder, currentUserCurrentHostProfile); pipeline.Commands.Add(new Command(recreateProfileScript, isScript: true, useLocalScope: false)); // The PowerShell Output Redirection bug affects System.Management.Automation // it appears with v3 more than others. It is already known to affect v2 // this implements the redirection fix from the post below, fixed up with some comments // http://www.leeholmes.com/blog/2008/07/30/workaround-the-os-handles-position-is-not-what-filestream-expected/ const string outputRedirectionFixScript = @" try { $bindingFlags = [Reflection.BindingFlags] ""Instance,NonPublic,GetField"" $objectRef = $host.GetType().GetField(""externalHostRef"", $bindingFlags).GetValue($host) $bindingFlags = [Reflection.BindingFlags] ""Instance,NonPublic,GetProperty"" $consoleHost = $objectRef.GetType().GetProperty(""Value"", $bindingFlags).GetValue($objectRef, @()) [void] $consoleHost.GetType().GetProperty(""IsStandardOutputRedirected"", $bindingFlags).GetValue($consoleHost, @()) $bindingFlags = [Reflection.BindingFlags] ""Instance,NonPublic,GetField"" $field = $consoleHost.GetType().GetField(""standardOutputWriter"", $bindingFlags) $field.SetValue($consoleHost, [Console]::Out) [void] $consoleHost.GetType().GetProperty(""IsStandardErrorRedirected"", $bindingFlags).GetValue($consoleHost, @()) $field2 = $consoleHost.GetType().GetField(""standardErrorWriter"", $bindingFlags) $field2.SetValue($consoleHost, [Console]::Error) } catch { Write-Output ""Unable to apply redirection fix"" } "; pipeline.Commands.Add(new Command(outputRedirectionFixScript, isScript: true, useLocalScope: false)); if (additionalActionsBeforeScript != null) { additionalActionsBeforeScript.Invoke(pipeline); } pipeline.Commands.Add(new Command(commandToRun, isScript: true, useLocalScope: false)); try { pipeline.Invoke(); } catch (RuntimeException ex) { var errorStackTrace = ex.StackTrace; var record = ex.ErrorRecord; if (record != null) { // not available in v1 //errorStackTrace = record.ScriptStackTrace; var scriptStackTrace = record.GetType().GetProperty("ScriptStackTrace"); if (scriptStackTrace != null) { var scriptError = scriptStackTrace.GetValue(record, null).to_string(); if (!string.IsNullOrWhiteSpace(scriptError)) { errorStackTrace = scriptError; } } } this.Log().Error("ERROR: {0}{1}".format_with(ex.Message.escape_curly_braces(), !config.Debug ? string.Empty : "{0} {1}".format_with(Environment.NewLine, errorStackTrace.escape_curly_braces()))); } catch (Exception ex) { // Unfortunately this doesn't print line number and character. It might be nice to get back to those items unless it involves tons of work. this.Log().Error("ERROR: {0}{1}".format_with(ex.Message.escape_curly_braces(), !config.Debug ? string.Empty : "{0} {1}".format_with(Environment.NewLine, ex.StackTrace.escape_curly_braces()))); } if (pipeline.PipelineStateInfo != null) { switch (pipeline.PipelineStateInfo.State) { // disconnected is not available unless the assembly version is at least v3 //case PipelineState.Disconnected: case PipelineState.Running: case PipelineState.NotStarted: case PipelineState.Failed: case PipelineState.Stopping: case PipelineState.Stopped: if (host.ExitCode == 0) { host.SetShouldExit(1); } host.HostException = pipeline.PipelineStateInfo.Reason; break; case PipelineState.Completed: if (host.ExitCode == -1) { host.SetShouldExit(0); } break; } } } } this.Log().Debug("Built-in PowerShell host called with ['{0}'] exited with '{1}'.".format_with(commandToRun.escape_curly_braces(), host.ExitCode)); result.ExitCode = host.ExitCode; result.StandardErrorWritten = host.StandardErrorWritten; return(result); }
private PowerShellExecutionResults run_external_powershell(ChocolateyConfiguration configuration, string chocoPowerShellScript) { var result = new PowerShellExecutionResults(); result.ExitCode = PowershellExecutor.execute( wrap_script_with_module(chocoPowerShellScript, configuration), _fileSystem, configuration.CommandExecutionTimeoutSeconds, (s, e) => { if (string.IsNullOrWhiteSpace(e.Data)) return; //inspect for different streams if (e.Data.StartsWith("DEBUG:")) { this.Log().Debug(() => " " + e.Data.escape_curly_braces()); } else if (e.Data.StartsWith("WARNING:")) { this.Log().Warn(() => " " + e.Data.escape_curly_braces()); } else if (e.Data.StartsWith("VERBOSE:")) { this.Log().Info(ChocolateyLoggers.Verbose, () => " " + e.Data.escape_curly_braces()); } else { this.Log().Info(() => " " + e.Data.escape_curly_braces()); } }, (s, e) => { if (string.IsNullOrWhiteSpace(e.Data)) return; result.StandardErrorWritten = true; this.Log().Error(() => " " + e.Data.escape_curly_braces()); }); return result; }
public bool run_action(ChocolateyConfiguration configuration, PackageResult packageResult, CommandNameType command) { var installerRun = false; var packageDirectory = packageResult.InstallLocation; if (packageDirectory.is_equal_to(ApplicationParameters.InstallLocation) || packageDirectory.is_equal_to(ApplicationParameters.PackagesLocation)) { packageResult.Messages.Add( new ResultMessage( ResultType.Error, "Install location is not specific enough, cannot run PowerShell script:{0} Erroneous install location captured as '{1}'".format_with(Environment.NewLine, packageResult.InstallLocation) ) ); return(false); } if (!_fileSystem.directory_exists(packageDirectory)) { packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Package install not found:'{0}'".format_with(packageDirectory))); return(installerRun); } var chocoPowerShellScript = get_script_for_action(packageResult, command); if (!string.IsNullOrEmpty(chocoPowerShellScript)) { var failure = false; var package = packageResult.Package; prepare_powershell_environment(package, configuration, packageDirectory); this.Log().Debug(ChocolateyLoggers.Important, "Contents of '{0}':".format_with(chocoPowerShellScript)); string chocoPowerShellScriptContents = _fileSystem.read_file(chocoPowerShellScript); this.Log().Debug(chocoPowerShellScriptContents.escape_curly_braces()); bool shouldRun = !configuration.PromptForConfirmation; if (!shouldRun) { this.Log().Info(ChocolateyLoggers.Important, () => "The package {0} wants to run '{1}'.".format_with(package.Id, _fileSystem.get_file_name(chocoPowerShellScript))); this.Log().Info(ChocolateyLoggers.Important, () => "Note: If you don't run this script, the installation will fail."); this.Log().Info(ChocolateyLoggers.Important, () => @"Note: To confirm automatically next time, use '-y' or consider:"); this.Log().Info(ChocolateyLoggers.Important, () => @"choco feature enable -n allowGlobalConfirmation"); var selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run the script?", new[] { "yes", "no", "print" }, defaultChoice: null, requireAnswer: true, allowShortAnswer: true, shortPrompt: true ); if (selection.is_equal_to("print")) { this.Log().Info(ChocolateyLoggers.Important, "------ BEGIN SCRIPT ------"); this.Log().Info(() => "{0}{1}{0}".format_with(Environment.NewLine, chocoPowerShellScriptContents.escape_curly_braces())); this.Log().Info(ChocolateyLoggers.Important, "------- END SCRIPT -------"); selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run this script?", new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true, allowShortAnswer: true, shortPrompt: true ); } if (selection.is_equal_to("yes")) { shouldRun = true; } if (selection.is_equal_to("no")) { //MSI ERROR_INSTALL_USEREXIT - 1602 - https://support.microsoft.com/en-us/kb/304888 / https://msdn.microsoft.com/en-us/library/aa376931.aspx //ERROR_INSTALL_CANCEL - 15608 - https://msdn.microsoft.com/en-us/library/windows/desktop/ms681384.aspx Environment.ExitCode = 15608; packageResult.Messages.Add(new ResultMessage(ResultType.Error, "User canceled powershell portion of installation for '{0}'.{1} Specify -n to skip automated script actions.".format_with(chocoPowerShellScript, Environment.NewLine))); } } if (shouldRun) { installerRun = true; if (configuration.Features.UsePowerShellHost) { add_assembly_resolver(); } var result = new PowerShellExecutionResults { ExitCode = -1 }; try { result = configuration.Features.UsePowerShellHost ? Execute.with_timeout(configuration.CommandExecutionTimeoutSeconds).command(() => run_host(configuration, chocoPowerShellScript, null), result) : run_external_powershell(configuration, chocoPowerShellScript); } catch (Exception ex) { this.Log().Error(ex.Message.escape_curly_braces()); result.ExitCode = -1; } if (configuration.Features.UsePowerShellHost) { remove_assembly_resolver(); } if (result.StandardErrorWritten && configuration.Features.FailOnStandardError) { failure = true; } else if (result.StandardErrorWritten && result.ExitCode == 0) { this.Log().Warn( () => @"Only an exit code of non-zero will fail the package by default. Set `--failonstderr` if you want error messages to also fail a script. See `choco -h` for details."); } if (result.ExitCode != 0) { Environment.ExitCode = result.ExitCode; packageResult.ExitCode = result.ExitCode; } // 0 - most widely used success exit code // MSI valid exit codes // 1605 - (uninstall) - the product is not found, could have already been uninstalled // 1614 (uninstall) - the product is uninstalled // 1641 - restart initiated // 3010 - restart required var validExitCodes = new List <int> { 0, 1605, 1614, 1641, 3010 }; if (!validExitCodes.Contains(result.ExitCode)) { failure = true; } if (!configuration.Features.UsePackageExitCodes) { Environment.ExitCode = failure ? 1 : 0; } if (failure) { packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Error while running '{0}'.{1} See log for details.".format_with(chocoPowerShellScript, Environment.NewLine))); } packageResult.Messages.Add(new ResultMessage(ResultType.Note, "Ran '{0}'".format_with(chocoPowerShellScript))); } } return(installerRun); }
public bool run_action(ChocolateyConfiguration configuration, PackageResult packageResult, CommandNameType command) { var installerRun = false; var packageDirectory = packageResult.InstallLocation; if (packageDirectory.is_equal_to(ApplicationParameters.InstallLocation) || packageDirectory.is_equal_to(ApplicationParameters.PackagesLocation)) { packageResult.Messages.Add( new ResultMessage( ResultType.Error, "Install location is not specific enough, cannot run PowerShell script:{0} Erroneous install location captured as '{1}'".format_with(Environment.NewLine, packageResult.InstallLocation) ) ); return false; } if (!_fileSystem.directory_exists(packageDirectory)) { packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Package install not found:'{0}'".format_with(packageDirectory))); return installerRun; } var chocoPowerShellScript = get_script_for_action(packageResult, command); if (!string.IsNullOrEmpty(chocoPowerShellScript)) { var failure = false; var package = packageResult.Package; prepare_powershell_environment(package, configuration, packageDirectory); this.Log().Debug(ChocolateyLoggers.Important, "Contents of '{0}':".format_with(chocoPowerShellScript)); string chocoPowerShellScriptContents = _fileSystem.read_file(chocoPowerShellScript); this.Log().Debug(chocoPowerShellScriptContents.escape_curly_braces()); bool shouldRun = !configuration.PromptForConfirmation; if (!shouldRun) { this.Log().Info(ChocolateyLoggers.Important, () => "The package {0} wants to run '{1}'.".format_with(package.Id, _fileSystem.get_file_name(chocoPowerShellScript))); this.Log().Info(ChocolateyLoggers.Important, () => "Note: If you don't run this script, the installation will fail."); this.Log().Info(ChocolateyLoggers.Important, () => @"Note: To confirm automatically next time, use '-y' or consider setting 'allowGlobalConfirmation'. Run 'choco feature -h' for more details."); var selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run the script?", new[] { "yes", "no", "print" }, defaultChoice: null, requireAnswer: true, allowShortAnswer: true, shortPrompt: true ); if (selection.is_equal_to("print")) { this.Log().Info(ChocolateyLoggers.Important, "------ BEGIN SCRIPT ------"); this.Log().Info(() => "{0}{1}{0}".format_with(Environment.NewLine, chocoPowerShellScriptContents.escape_curly_braces())); this.Log().Info(ChocolateyLoggers.Important, "------- END SCRIPT -------"); selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run this script?", new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true, allowShortAnswer: true, shortPrompt: true ); } if (selection.is_equal_to("yes")) shouldRun = true; if (selection.is_equal_to("no")) { Environment.ExitCode = 1; packageResult.Messages.Add(new ResultMessage(ResultType.Error, "User cancelled powershell portion of installation for '{0}'.{1} Specify -n to skip automated script actions.".format_with(chocoPowerShellScript, Environment.NewLine))); } } if (shouldRun) { installerRun = true; if (configuration.Features.UsePowerShellHost) { add_assembly_resolver(); } var result = new PowerShellExecutionResults { ExitCode = -1 }; try { result = configuration.Features.UsePowerShellHost ? Execute.with_timeout(configuration.CommandExecutionTimeoutSeconds).command(() => run_host(configuration, chocoPowerShellScript, null), result) : run_external_powershell(configuration, chocoPowerShellScript); } catch (Exception ex) { this.Log().Error(ex.Message.escape_curly_braces()); result.ExitCode = -1; } if (configuration.Features.UsePowerShellHost) { remove_assembly_resolver(); } if (result.StandardErrorWritten && configuration.Features.FailOnStandardError) { failure = true; } else if (result.StandardErrorWritten && result.ExitCode == 0) { this.Log().Warn( () => @"Only an exit code of non-zero will fail the package by default. Set `--failonstderr` if you want error messages to also fail a script. See `choco -h` for details."); } if (result.ExitCode != 0) { Environment.ExitCode = result.ExitCode; packageResult.ExitCode = result.ExitCode; } // 0 - most widely used success exit code // MSI valid exit codes // 1605 - (uninstall) - the product is not found, could have already been uninstalled // 1614 (uninstall) - the product is uninstalled // 1641 - restart initiated // 3010 - restart required var validExitCodes = new List<int> { 0, 1605, 1614, 1641, 3010 }; if (!validExitCodes.Contains(result.ExitCode)) { failure = true; } if (failure) { packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Error while running '{0}'.{1} See log for details.".format_with(chocoPowerShellScript, Environment.NewLine))); } packageResult.Messages.Add(new ResultMessage(ResultType.Note, "Ran '{0}'".format_with(chocoPowerShellScript))); } } return installerRun; }
public PowerShellExecutionResults run_host(ChocolateyConfiguration config, string chocoPowerShellScript, Action<Pipeline> additionalActionsBeforeScript) { // since we control output in the host, always set these true Environment.SetEnvironmentVariable("ChocolateyEnvironmentDebug", "true"); Environment.SetEnvironmentVariable("ChocolateyEnvironmentVerbose", "true"); var result = new PowerShellExecutionResults(); string commandToRun = wrap_script_with_module(chocoPowerShellScript, config); var host = new PoshHost(config); this.Log().Debug(() => "Calling built-in PowerShell host with ['{0}']".format_with(commandToRun.escape_curly_braces())); var initialSessionState = InitialSessionState.CreateDefault(); // override system execution policy without accidentally setting it initialSessionState.AuthorizationManager = new AuthorizationManager("choco"); using (var runspace = RunspaceFactory.CreateRunspace(host, initialSessionState)) { runspace.Open(); // this will affect actual execution policy //RunspaceInvoke invoker = new RunspaceInvoke(runspace); //invoker.Invoke("Set-ExecutionPolicy ByPass"); using (var pipeline = runspace.CreatePipeline()) { // The powershell host itself handles the following items: // * Write-Debug // * Write-Host // * Write-Verbose // * Write-Warning // // the two methods below will pick up Write-Output and Write-Error // Write-Output pipeline.Output.DataReady += (sender, args) => { PipelineReader<PSObject> reader = sender as PipelineReader<PSObject>; if (reader != null) { while (reader.Count > 0) { host.UI.WriteLine(reader.Read().to_string().escape_curly_braces()); } } }; // Write-Error pipeline.Error.DataReady += (sender, args) => { PipelineReader<object> reader = sender as PipelineReader<object>; if (reader != null) { while (reader.Count > 0) { host.UI.WriteErrorLine(reader.Read().to_string().escape_curly_braces()); } } }; var documentsFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments, Environment.SpecialFolderOption.DoNotVerify); var currentUserCurrentHostProfile = _fileSystem.combine_paths(documentsFolder, "WindowsPowerShell\\Microsoft.PowerShell_profile.ps1"); var recreateProfileScript = @" if ((Test-Path(""{0}"")) -and ($profile -eq $null -or $profile -eq '')) {{ $global:profile = ""{1}"" }} ".format_with(documentsFolder, currentUserCurrentHostProfile); pipeline.Commands.Add(new Command(recreateProfileScript, isScript: true, useLocalScope: false)); // The PowerShell Output Redirection bug affects System.Management.Automation // it appears with v3 more than others. It is already known to affect v2 // this implements the redirection fix from the post below, fixed up with some comments // http://www.leeholmes.com/blog/2008/07/30/workaround-the-os-handles-position-is-not-what-filestream-expected/ const string outputRedirectionFixScript = @" try { $bindingFlags = [Reflection.BindingFlags] ""Instance,NonPublic,GetField"" $objectRef = $host.GetType().GetField(""externalHostRef"", $bindingFlags).GetValue($host) $bindingFlags = [Reflection.BindingFlags] ""Instance,NonPublic,GetProperty"" $consoleHost = $objectRef.GetType().GetProperty(""Value"", $bindingFlags).GetValue($objectRef, @()) [void] $consoleHost.GetType().GetProperty(""IsStandardOutputRedirected"", $bindingFlags).GetValue($consoleHost, @()) $bindingFlags = [Reflection.BindingFlags] ""Instance,NonPublic,GetField"" $field = $consoleHost.GetType().GetField(""standardOutputWriter"", $bindingFlags) $field.SetValue($consoleHost, [Console]::Out) [void] $consoleHost.GetType().GetProperty(""IsStandardErrorRedirected"", $bindingFlags).GetValue($consoleHost, @()) $field2 = $consoleHost.GetType().GetField(""standardErrorWriter"", $bindingFlags) $field2.SetValue($consoleHost, [Console]::Error) } catch { Write-Output ""Unable to apply redirection fix"" } "; pipeline.Commands.Add(new Command(outputRedirectionFixScript, isScript: true, useLocalScope: false)); if (additionalActionsBeforeScript != null) additionalActionsBeforeScript.Invoke(pipeline); pipeline.Commands.Add(new Command(commandToRun, isScript: true, useLocalScope: false)); try { pipeline.Invoke(); } catch (RuntimeException ex) { var errorStackTrace = ex.StackTrace; var record = ex.ErrorRecord; if (record != null) { // not available in v1 //errorStackTrace = record.ScriptStackTrace; var scriptStackTrace = record.GetType().GetProperty("ScriptStackTrace"); if (scriptStackTrace != null) { var scriptError = scriptStackTrace.GetValue(record, null).to_string(); if (!string.IsNullOrWhiteSpace(scriptError)) errorStackTrace = scriptError; } } this.Log().Error("ERROR: {0}{1}".format_with(ex.Message.escape_curly_braces(), !config.Debug ? string.Empty : "{0} {1}".format_with(Environment.NewLine, errorStackTrace.escape_curly_braces()))); } catch (Exception ex) { // Unfortunately this doesn't print line number and character. It might be nice to get back to those items unless it involves tons of work. this.Log().Error("ERROR: {0}{1}".format_with(ex.Message.escape_curly_braces(), !config.Debug ? string.Empty : "{0} {1}".format_with(Environment.NewLine, ex.StackTrace.escape_curly_braces()))); } if (pipeline.PipelineStateInfo != null) { switch (pipeline.PipelineStateInfo.State) { // disconnected is not available unless the assembly version is at least v3 //case PipelineState.Disconnected: case PipelineState.Running: case PipelineState.NotStarted: case PipelineState.Failed: case PipelineState.Stopping: case PipelineState.Stopped: if (host.ExitCode == 0) host.SetShouldExit(1); host.HostException = pipeline.PipelineStateInfo.Reason; break; case PipelineState.Completed: if (host.ExitCode == -1) host.SetShouldExit(0); break; } } } } this.Log().Debug("Built-in PowerShell host called with ['{0}'] exited with '{1}'.".format_with(commandToRun.escape_curly_braces(), host.ExitCode)); result.ExitCode = host.ExitCode; result.StandardErrorWritten = host.StandardErrorWritten; return result; }
public bool run_action(ChocolateyConfiguration configuration, PackageResult packageResult, CommandNameType command) { var installerRun = false; var packageDirectory = packageResult.InstallLocation; if (packageDirectory.is_equal_to(ApplicationParameters.InstallLocation) || packageDirectory.is_equal_to(ApplicationParameters.PackagesLocation)) { packageResult.Messages.Add( new ResultMessage( ResultType.Error, "Install location is not specific enough, cannot run PowerShell script:{0} Erroneous install location captured as '{1}'".format_with(Environment.NewLine, packageResult.InstallLocation) ) ); return(false); } if (!_fileSystem.directory_exists(packageDirectory)) { packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Package install not found:'{0}'".format_with(packageDirectory))); return(installerRun); } var chocoPowerShellScript = get_script_for_action(packageResult, command); if (!string.IsNullOrEmpty(chocoPowerShellScript)) { var failure = false; ConfigurationBuilder.set_environment_variables(configuration); var package = packageResult.Package; Environment.SetEnvironmentVariable("chocolateyPackageName", package.Id); Environment.SetEnvironmentVariable("packageName", package.Id); Environment.SetEnvironmentVariable("chocolateyPackageVersion", package.Version.to_string()); Environment.SetEnvironmentVariable("packageVersion", package.Version.to_string()); Environment.SetEnvironmentVariable("chocolateyPackageFolder", packageDirectory); Environment.SetEnvironmentVariable("packageFolder", packageDirectory); Environment.SetEnvironmentVariable("installArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("installerArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("chocolateyInstallArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("packageParameters", configuration.PackageParameters); Environment.SetEnvironmentVariable("chocolateyPackageParameters", configuration.PackageParameters); if (configuration.ForceX86) { Environment.SetEnvironmentVariable("chocolateyForceX86", "true"); } if (configuration.OverrideArguments) { Environment.SetEnvironmentVariable("chocolateyInstallOverride", "true"); } if (configuration.NotSilent) { Environment.SetEnvironmentVariable("chocolateyInstallOverride", "true"); } //todo:if (configuration.NoOutput) //{ // Environment.SetEnvironmentVariable("ChocolateyEnvironmentQuiet","true"); //} if (package.IsDownloadCacheAvailable) { foreach (var downloadCache in package.DownloadCache.or_empty_list_if_null()) { var urlKey = CryptoHashProvider.hash_value(downloadCache.OriginalUrl, CryptoHashProviderType.Sha256).Replace("=", string.Empty); Environment.SetEnvironmentVariable("CacheFile_{0}".format_with(urlKey), downloadCache.FileName); Environment.SetEnvironmentVariable("CacheChecksum_{0}".format_with(urlKey), downloadCache.Checksum); Environment.SetEnvironmentVariable("CacheChecksumType_{0}".format_with(urlKey), "sha512"); } } this.Log().Debug(ChocolateyLoggers.Important, "Contents of '{0}':".format_with(chocoPowerShellScript)); string chocoPowerShellScriptContents = _fileSystem.read_file(chocoPowerShellScript); this.Log().Debug(chocoPowerShellScriptContents.escape_curly_braces()); bool shouldRun = !configuration.PromptForConfirmation; if (!shouldRun) { this.Log().Info(ChocolateyLoggers.Important, () => "The package {0} wants to run '{1}'.".format_with(package.Id, _fileSystem.get_file_name(chocoPowerShellScript))); this.Log().Info(ChocolateyLoggers.Important, () => "Note: If you don't run this script, the installation will fail."); this.Log().Info(ChocolateyLoggers.Important, () => @"Note: To confirm automatically next time, use '-y' or consider setting 'allowGlobalConfirmation'. Run 'choco feature -h' for more details."); var selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run the script?", new[] { "yes", "no", "print" }, defaultChoice: null, requireAnswer: true, allowShortAnswer: true, shortPrompt: true ); if (selection.is_equal_to("print")) { this.Log().Info(ChocolateyLoggers.Important, "------ BEGIN SCRIPT ------"); this.Log().Info(() => "{0}{1}{0}".format_with(Environment.NewLine, chocoPowerShellScriptContents.escape_curly_braces())); this.Log().Info(ChocolateyLoggers.Important, "------- END SCRIPT -------"); selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run this script?", new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true, allowShortAnswer: true, shortPrompt: true ); } if (selection.is_equal_to("yes")) { shouldRun = true; } if (selection.is_equal_to("no")) { Environment.ExitCode = 1; packageResult.Messages.Add(new ResultMessage(ResultType.Error, "User cancelled powershell portion of installation for '{0}'.{1} Specify -n to skip automated script actions.".format_with(chocoPowerShellScript, Environment.NewLine))); } } if (shouldRun) { installerRun = true; if (configuration.Features.UsePowerShellHost) { add_assembly_resolver(); } var result = new PowerShellExecutionResults { ExitCode = -1 }; try { result = configuration.Features.UsePowerShellHost ? Execute.with_timeout(configuration.CommandExecutionTimeoutSeconds).command(() => run_host(configuration, chocoPowerShellScript), result) : run_external_powershell(configuration, chocoPowerShellScript); } catch (Exception ex) { this.Log().Error(ex.Message.escape_curly_braces()); result.ExitCode = -1; } if (configuration.Features.UsePowerShellHost) { remove_assembly_resolver(); } if (result.StandardErrorWritten && configuration.Features.FailOnStandardError) { failure = true; } else if (result.StandardErrorWritten && result.ExitCode == 0) { this.Log().Warn( () => @"Only an exit code of non-zero will fail the package by default. Set `--failonstderr` if you want error messages to also fail a script. See `choco -h` for details."); } if (result.ExitCode != 0) { Environment.ExitCode = result.ExitCode; packageResult.ExitCode = result.ExitCode; } // 0 - most widely used success exit code // MSI valid exit codes // 1605 - (uninstall) - the product is not found, could have already been uninstalled // 1614 (uninstall) - the product is uninstalled // 1641 - restart initiated // 3010 - restart required var validExitCodes = new List <int> { 0, 1605, 1614, 1641, 3010 }; if (!validExitCodes.Contains(result.ExitCode)) { failure = true; } if (failure) { packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Error while running '{0}'.{1} See log for details.".format_with(chocoPowerShellScript, Environment.NewLine))); } packageResult.Messages.Add(new ResultMessage(ResultType.Note, "Ran '{0}'".format_with(chocoPowerShellScript))); } } return(installerRun); }
private PowerShellExecutionResults run_host(ChocolateyConfiguration config, string chocoPowerShellScript) { var result = new PowerShellExecutionResults(); string commandToRun = wrap_script_with_module(chocoPowerShellScript, config); var host = new PoshHost(config); this.Log().Debug(() => "Calling built-in PowerShell host with ['{0}']".format_with(commandToRun.escape_curly_braces())); var initialSessionState = InitialSessionState.CreateDefault(); // override system execution policy without accidentally setting it initialSessionState.AuthorizationManager = new AuthorizationManager("choco"); using (var runspace = RunspaceFactory.CreateRunspace(host, initialSessionState)) { runspace.Open(); // this will affect actual execution policy //RunspaceInvoke invoker = new RunspaceInvoke(runspace); //invoker.Invoke("Set-ExecutionPolicy ByPass"); using (var pipeline = runspace.CreatePipeline()) { // The powershell host itself handles the following items: // * Write-Debug // * Write-Host // * Write-Verbose // * Write-Warning // // the two methods below will pick up Write-Output and Write-Error // Write-Output pipeline.Output.DataReady += (sender, args) => { PipelineReader<PSObject> reader = sender as PipelineReader<PSObject>; if (reader != null) { while (reader.Count > 0) { host.UI.WriteLine(reader.Read().to_string()); } } }; // Write-Error pipeline.Error.DataReady += (sender, args) => { PipelineReader<object> reader = sender as PipelineReader<object>; if (reader != null) { while (reader.Count > 0) { host.UI.WriteErrorLine(reader.Read().to_string()); } } }; pipeline.Commands.Add(new Command(commandToRun, isScript: true, useLocalScope: false)); try { pipeline.Invoke(); } catch (RuntimeException ex) { var errorStackTrace = ex.StackTrace; var record = ex.ErrorRecord; if (record != null) { // not available in v1 //errorStackTrace = record.ScriptStackTrace; var scriptStackTrace = record.GetType().GetProperty("ScriptStackTrace"); if (scriptStackTrace != null) { var scriptError = scriptStackTrace.GetValue(record, null).to_string(); if (!string.IsNullOrWhiteSpace(scriptError)) errorStackTrace = scriptError; } } this.Log().Error("ERROR: {0}{1}".format_with(ex.Message, !config.Debug ? string.Empty : "{0} {1}".format_with(Environment.NewLine,errorStackTrace))); } catch (Exception ex) { // Unfortunately this doesn't print line number and character. It might be nice to get back to those items unless it involves tons of work. this.Log().Error("ERROR: {0}{1}".format_with(ex.Message, !config.Debug ? string.Empty : "{0} {1}".format_with(Environment.NewLine,ex.StackTrace))); } if (pipeline.PipelineStateInfo != null) { switch (pipeline.PipelineStateInfo.State) { // disconnected is not available unless the assembly version is at least v3 //case PipelineState.Disconnected: case PipelineState.Running: case PipelineState.NotStarted: case PipelineState.Failed: case PipelineState.Stopping: case PipelineState.Stopped: host.SetShouldExit(1); host.HostException = pipeline.PipelineStateInfo.Reason; break; case PipelineState.Completed: host.SetShouldExit(0); break; } } } } this.Log().Debug("Built-in PowerShell host called with ['{0}'] exited with '{1}'.".format_with(commandToRun.escape_curly_braces(), host.ExitCode)); result.ExitCode = host.ExitCode; result.StandardErrorWritten = host.StandardErrorWritten; return result; }
public bool run_action(ChocolateyConfiguration configuration, PackageResult packageResult, CommandNameType command) { var installerRun = false; var file = "chocolateyInstall.ps1"; switch (command) { case CommandNameType.uninstall: file = "chocolateyUninstall.ps1"; break; } var packageDirectory = packageResult.InstallLocation; if (packageDirectory.is_equal_to(ApplicationParameters.InstallLocation) || packageDirectory.is_equal_to(ApplicationParameters.PackagesLocation)) { packageResult.Messages.Add( new ResultMessage( ResultType.Error, "Install location is not specific enough, cannot run PowerShell script:{0} Erroneous install location captured as '{1}'".format_with(Environment.NewLine, packageResult.InstallLocation) ) ); return false; } if (!_fileSystem.directory_exists(packageDirectory)) { packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Package install not found:'{0}'".format_with(packageDirectory))); return installerRun; } var powershellScript = _fileSystem.get_files(packageDirectory, file, SearchOption.AllDirectories); if (powershellScript.Count() != 0) { var chocoPowerShellScript = powershellScript.FirstOrDefault(); var failure = false; //todo: this is here for any possible compatibility issues. Should be reviewed and removed. ConfigurationBuilder.set_environment_variables(configuration); var package = packageResult.Package; Environment.SetEnvironmentVariable("chocolateyPackageName", package.Id); Environment.SetEnvironmentVariable("packageName", package.Id); Environment.SetEnvironmentVariable("chocolateyPackageVersion", package.Version.to_string()); Environment.SetEnvironmentVariable("packageVersion", package.Version.to_string()); Environment.SetEnvironmentVariable("chocolateyPackageFolder", packageDirectory); Environment.SetEnvironmentVariable("packageFolder", packageDirectory); Environment.SetEnvironmentVariable("installArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("installerArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("chocolateyInstallArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("packageParameters", configuration.PackageParameters); Environment.SetEnvironmentVariable("chocolateyPackageParameters", configuration.PackageParameters); if (configuration.ForceX86) { Environment.SetEnvironmentVariable("chocolateyForceX86", "true"); } if (configuration.OverrideArguments) { Environment.SetEnvironmentVariable("chocolateyInstallOverride", "true"); } if (configuration.NotSilent) { Environment.SetEnvironmentVariable("chocolateyInstallOverride", "true"); } //todo:if (configuration.NoOutput) //{ // Environment.SetEnvironmentVariable("ChocolateyEnvironmentQuiet","true"); //} this.Log().Debug(ChocolateyLoggers.Important, "Contents of '{0}':".format_with(chocoPowerShellScript)); string chocoPowerShellScriptContents = _fileSystem.read_file(chocoPowerShellScript); this.Log().Debug(chocoPowerShellScriptContents.escape_curly_braces()); bool shouldRun = !configuration.PromptForConfirmation; if (!shouldRun) { this.Log().Info(ChocolateyLoggers.Important, () => "The package {0} wants to run '{1}'.".format_with(package.Id, _fileSystem.get_file_name(chocoPowerShellScript))); this.Log().Info(ChocolateyLoggers.Important, () => "Note: If you don't run this script, the installation will fail."); this.Log().Info(ChocolateyLoggers.Important, () => @"Note: To confirm automatically next time, use '-y' or consider setting 'allowGlobalConfirmation'. Run 'choco feature -h' for more details."); var selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run the script?", new[] {"yes", "no", "print"}, defaultChoice: null, requireAnswer: true); if (selection.is_equal_to("print")) { this.Log().Info(ChocolateyLoggers.Important, "------ BEGIN SCRIPT ------"); this.Log().Info(() => "{0}{1}{0}".format_with(Environment.NewLine, chocoPowerShellScriptContents.escape_curly_braces())); this.Log().Info(ChocolateyLoggers.Important, "------- END SCRIPT -------"); selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run this script?", new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true); } if (selection.is_equal_to("yes")) shouldRun = true; if (selection.is_equal_to("no")) { Environment.ExitCode = 1; packageResult.Messages.Add(new ResultMessage(ResultType.Error, "User cancelled powershell portion of installation for '{0}'.{1} Specify -n to skip automated script actions.".format_with(powershellScript.FirstOrDefault(), Environment.NewLine))); } } if (shouldRun) { installerRun = true; if (configuration.Features.UsePowerShellHost) { add_assembly_resolver(); } var result = new PowerShellExecutionResults { ExitCode = -1 }; try { result = configuration.Features.UsePowerShellHost ? Execute.with_timeout(configuration.CommandExecutionTimeoutSeconds).command(() => run_host(configuration, chocoPowerShellScript), result) : run_external_powershell(configuration, chocoPowerShellScript); } catch (Exception ex) { this.Log().Error(ex.Message); result.ExitCode = -1; } if (configuration.Features.UsePowerShellHost) { remove_assembly_resolver(); } if (result.StandardErrorWritten && configuration.Features.FailOnStandardError) { failure = true; } else if (result.StandardErrorWritten && result.ExitCode == 0) { this.Log().Warn( () => @"Only an exit code of non-zero will fail the package by default. Set `--failonstderr` if you want error messages to also fail a script. See `choco -h` for details."); } if (result.ExitCode != 0) { failure = true; } if (failure) { Environment.ExitCode = result.ExitCode; packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Error while running '{0}'.{1} See log for details.".format_with(powershellScript.FirstOrDefault(), Environment.NewLine))); } packageResult.Messages.Add(new ResultMessage(ResultType.Note, "Ran '{0}'".format_with(chocoPowerShellScript))); } } return installerRun; }
private PowerShellExecutionResults run_host(ChocolateyConfiguration config, string chocoPowerShellScript) { var result = new PowerShellExecutionResults(); string commandToRun = wrap_script_with_module(chocoPowerShellScript, config); var host = new PoshHost(config); this.Log().Debug(() => "Calling built-in PowerShell host with ['{0}']".format_with(commandToRun.escape_curly_braces())); var initialSessionState = InitialSessionState.CreateDefault(); // override system execution policy without accidentally setting it initialSessionState.AuthorizationManager = new AuthorizationManager("choco"); using (var runspace = RunspaceFactory.CreateRunspace(host, initialSessionState)) { runspace.Open(); // this will affect actual execution policy //RunspaceInvoke invoker = new RunspaceInvoke(runspace); //invoker.Invoke("Set-ExecutionPolicy ByPass"); using (var pipeline = runspace.CreatePipeline()) { // The powershell host itself handles the following items: // * Write-Debug // * Write-Host // * Write-Verbose // * Write-Warning // // the two methods below will pick up Write-Output and Write-Error // Write-Output pipeline.Output.DataReady += (sender, args) => { PipelineReader <PSObject> reader = sender as PipelineReader <PSObject>; if (reader != null) { while (reader.Count > 0) { host.UI.WriteLine(reader.Read().to_string()); } } }; // Write-Error pipeline.Error.DataReady += (sender, args) => { PipelineReader <object> reader = sender as PipelineReader <object>; if (reader != null) { while (reader.Count > 0) { host.UI.WriteErrorLine(reader.Read().to_string()); } } }; pipeline.Commands.Add(new Command(commandToRun, isScript: true, useLocalScope: false)); try { pipeline.Invoke(); } catch (Exception ex) { // Unfortunately this doesn't print line number and character. It might be nice to get back to those items unless it involves tons of work. this.Log().Error("ERROR: {0}".format_with(ex.Message)); //, !config.Debug ? string.Empty : "{0} {1}".format_with(Environment.NewLine,ex.StackTrace))); } if (pipeline.PipelineStateInfo != null) { switch (pipeline.PipelineStateInfo.State) { // disconnected is not available unless the assembly version is at least v3 //case PipelineState.Disconnected: case PipelineState.Running: case PipelineState.NotStarted: case PipelineState.Failed: case PipelineState.Stopping: case PipelineState.Stopped: host.SetShouldExit(1); host.HostException = pipeline.PipelineStateInfo.Reason; break; case PipelineState.Completed: host.SetShouldExit(0); break; } } } } this.Log().Debug("Built-in PowerShell host called with ['{0}'] exited with '{1}'.".format_with(commandToRun.escape_curly_braces(), host.ExitCode)); result.ExitCode = host.ExitCode; result.StandardErrorWritten = host.StandardErrorWritten; return(result); }
public bool run_action(ChocolateyConfiguration configuration, PackageResult packageResult, CommandNameType command) { var installerRun = false; var file = "chocolateyInstall.ps1"; switch (command) { case CommandNameType.uninstall: file = "chocolateyUninstall.ps1"; break; } var packageDirectory = packageResult.InstallLocation; if (packageDirectory.is_equal_to(ApplicationParameters.InstallLocation) || packageDirectory.is_equal_to(ApplicationParameters.PackagesLocation)) { packageResult.Messages.Add( new ResultMessage( ResultType.Error, "Install location is not specific enough, cannot run PowerShell script:{0} Erroneous install location captured as '{1}'".format_with(Environment.NewLine, packageResult.InstallLocation) ) ); return(false); } if (!_fileSystem.directory_exists(packageDirectory)) { packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Package install not found:'{0}'".format_with(packageDirectory))); return(installerRun); } var powershellScript = _fileSystem.get_files(packageDirectory, file, SearchOption.AllDirectories); if (powershellScript.Count() != 0) { var chocoPowerShellScript = powershellScript.FirstOrDefault(); var failure = false; //todo: this is here for any possible compatibility issues. Should be reviewed and removed. ConfigurationBuilder.set_environment_variables(configuration); var package = packageResult.Package; Environment.SetEnvironmentVariable("chocolateyPackageName", package.Id); Environment.SetEnvironmentVariable("packageName", package.Id); Environment.SetEnvironmentVariable("chocolateyPackageVersion", package.Version.to_string()); Environment.SetEnvironmentVariable("packageVersion", package.Version.to_string()); Environment.SetEnvironmentVariable("chocolateyPackageFolder", packageDirectory); Environment.SetEnvironmentVariable("packageFolder", packageDirectory); Environment.SetEnvironmentVariable("installArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("installerArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("chocolateyInstallArguments", configuration.InstallArguments); Environment.SetEnvironmentVariable("packageParameters", configuration.PackageParameters); Environment.SetEnvironmentVariable("chocolateyPackageParameters", configuration.PackageParameters); if (configuration.ForceX86) { Environment.SetEnvironmentVariable("chocolateyForceX86", "true"); } if (configuration.OverrideArguments) { Environment.SetEnvironmentVariable("chocolateyInstallOverride", "true"); } if (configuration.NotSilent) { Environment.SetEnvironmentVariable("chocolateyInstallOverride", "true"); } //todo:if (configuration.NoOutput) //{ // Environment.SetEnvironmentVariable("ChocolateyEnvironmentQuiet","true"); //} this.Log().Debug(ChocolateyLoggers.Important, "Contents of '{0}':".format_with(chocoPowerShellScript)); string chocoPowerShellScriptContents = _fileSystem.read_file(chocoPowerShellScript); this.Log().Debug(chocoPowerShellScriptContents.escape_curly_braces()); bool shouldRun = !configuration.PromptForConfirmation; if (!shouldRun) { this.Log().Info(ChocolateyLoggers.Important, () => "The package {0} wants to run '{1}'.".format_with(package.Id, _fileSystem.get_file_name(chocoPowerShellScript))); this.Log().Info(ChocolateyLoggers.Important, () => "Note: If you don't run this script, the installation will fail."); this.Log().Info(ChocolateyLoggers.Important, () => @"Note: To confirm automatically next time, use '-y' or consider setting 'allowGlobalConfirmation'. Run 'choco feature -h' for more details."); var selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run the script?", new[] { "yes", "no", "print" }, defaultChoice: null, requireAnswer: true); if (selection.is_equal_to("print")) { this.Log().Info(ChocolateyLoggers.Important, "------ BEGIN SCRIPT ------"); this.Log().Info(() => "{0}{1}{0}".format_with(Environment.NewLine, chocoPowerShellScriptContents.escape_curly_braces())); this.Log().Info(ChocolateyLoggers.Important, "------- END SCRIPT -------"); selection = InteractivePrompt.prompt_for_confirmation(@"Do you want to run this script?", new[] { "yes", "no" }, defaultChoice: null, requireAnswer: true); } if (selection.is_equal_to("yes")) { shouldRun = true; } if (selection.is_equal_to("no")) { Environment.ExitCode = 1; packageResult.Messages.Add(new ResultMessage(ResultType.Error, "User cancelled powershell portion of installation for '{0}'.{1} Specify -n to skip automated script actions.".format_with(powershellScript.FirstOrDefault(), Environment.NewLine))); } } if (shouldRun) { installerRun = true; if (configuration.Features.UsePowerShellHost) { add_assembly_resolver(); } var result = new PowerShellExecutionResults { ExitCode = -1 }; try { result = configuration.Features.UsePowerShellHost ? Execute.with_timeout(configuration.CommandExecutionTimeoutSeconds).command(() => run_host(configuration, chocoPowerShellScript), result) : run_external_powershell(configuration, chocoPowerShellScript); } catch (Exception ex) { this.Log().Error(ex.Message); result.ExitCode = -1; } if (configuration.Features.UsePowerShellHost) { remove_assembly_resolver(); } if (result.StandardErrorWritten && configuration.Features.FailOnStandardError) { failure = true; } else if (result.StandardErrorWritten && result.ExitCode == 0) { this.Log().Warn( () => @"Only an exit code of non-zero will fail the package by default. Set `--failonstderr` if you want error messages to also fail a script. See `choco -h` for details."); } if (result.ExitCode != 0) { failure = true; } if (failure) { Environment.ExitCode = result.ExitCode; packageResult.Messages.Add(new ResultMessage(ResultType.Error, "Error while running '{0}'.{1} See log for details.".format_with(powershellScript.FirstOrDefault(), Environment.NewLine))); } packageResult.Messages.Add(new ResultMessage(ResultType.Note, "Ran '{0}'".format_with(chocoPowerShellScript))); } } return(installerRun); }