diff --git a/commands/core/upgrade.drush.inc b/commands/core/upgrade.drush.inc index d31f7a0..58c0782 100644 --- a/commands/core/upgrade.drush.inc +++ b/commands/core/upgrade.drush.inc @@ -119,12 +119,12 @@ function drush_upgrade_site_upgrade($target_key) { } // Get a list of enabled contrib extensions. - $values = drush_invoke_process_args('pm-list', array(), array('status'=>'enabled','no-core'=>TRUE, '#integrate' => FALSE, '#override-simulated' => TRUE)); + $values = drush_invoke_process_args('pm-list', array(), array('status'=>'enabled','no-core'=>TRUE), array('integrate' => FALSE, 'override-simulated' => TRUE)); if ($values['error_status'] != 0) return FALSE; $contrib_extensions = array_keys($values['object']); // Get a list of enabled core extensions. - $values = drush_invoke_process_args('pm-list', array(), array('status'=>'enabled','core'=>TRUE, '#integrate' => FALSE, '#override-simulated' => TRUE)); + $values = drush_invoke_process_args('pm-list', array(), array('status'=>'enabled','core'=>TRUE), array('integrate' => FALSE, 'override-simulated' => TRUE)); if ($values['error_status'] != 0) return FALSE; $core_extensions = array_keys($values['object']); @@ -188,7 +188,7 @@ function drush_upgrade_site_upgrade($target_key) { // we are not ready to upgrade (but still can, without that project); if there is no RECOMMENDED // release, then we might not be ready to upgrade (but still can, with a non-recommended release). if (!empty($project_download_list)) { - $result = drush_invoke_sitealias_args(array('root' => '', 'uri' => ''), 'pm-releases', array_keys($project_download_list), array('default-major' => $target_version, '#integrate' => FALSE, '#override-simulated' => TRUE)); + $result = drush_invoke_sitealias_args(array('root' => '', 'uri' => ''), 'pm-releases', array_keys($project_download_list), array('default-major' => $target_version), array('integrate' => FALSE, 'override-simulated' => TRUE)); $project_releases = $result['object']; foreach ($project_download_list as $project => $extension_list) { if (!array_key_exists($project, $project_releases)) { @@ -312,7 +312,7 @@ function drush_upgrade_site_upgrade($target_key) { } } } - $result = (drush_invoke_sitealias_args($modify_site, 'site-upgrade-prepare', $contrib_extensions, array('uninstall' => implode(',', $uninstall_extensions), 'yes' => TRUE, '#interactive' => TRUE)) == 0); + $result = (drush_invoke_sitealias_args($modify_site, 'site-upgrade-prepare', $contrib_extensions, array('uninstall' => implode(',', $uninstall_extensions), 'yes' => TRUE), array('interactive' => TRUE)) == 0); // Delete the temporary site now that we're done with it. if (isset($modify_site_conf_path)) { @@ -326,7 +326,7 @@ function drush_upgrade_site_upgrade($target_key) { // Run update.php in a subshell. It is run on @target site whereas this request was on @self. drush_log(dt('About to perform updatedb for Drupal core on !target', array('!target' => $target_key)), 'ok'); // When we call drush_invoke_sitealias_args in #interactive mode, the result code comes from drush_op_system, where 0 == success. - $result = drush_invoke_sitealias_args($target_alias, 'updatedb', array(), array('yes' => TRUE, '#interactive' => TRUE)) == 0; + $result = drush_invoke_sitealias_args($target_alias, 'updatedb', array(), array('yes' => TRUE), array('interactive' => TRUE)) == 0; if ($result === FALSE) { return drush_set_error('DRUSH_DRUPAL_CORE_UPGRADE_FAILED', dt("The call to updatedb failed for Drupal core. This may be caused by a contrib module that is not yet ready for upgrade. Try running site-upgrade again with '--uninstall={module list}' to remove all contrib extensions prior to upgrade. Add modules back in until the problematic one is isolated. Please report problems in the issue queue of the module that is causing problems.")); } @@ -338,7 +338,8 @@ function drush_upgrade_site_upgrade($target_key) { // STEP 3: Download and re-enable the contrib modules. if (!empty($contrib_extensions) && !drush_get_option('core-only')) { - $options = array('#interactive' => TRUE); + $options = array(); + $backend_options = array('interactive' => TRUE); if (!empty($project_download_list)) { $projects = implode(',', array_keys($project_download_list)); $options['projects'] = $projects; @@ -353,7 +354,7 @@ function drush_upgrade_site_upgrade($target_key) { // Redispatch to site-upgrade-modules command, so that we will be // bootstrapped to the target site. - $result = (drush_invoke_sitealias_args($target_alias, 'site-upgrade-modules', array_merge($core_extensions, array_diff($contrib_extensions, $unavailable_extensions, $uninstall_extensions)), $options) == 0); + $result = (drush_invoke_sitealias_args($target_alias, 'site-upgrade-modules', array_merge($core_extensions, array_diff($contrib_extensions, $unavailable_extensions, $uninstall_extensions)), $options, $backend_options) == 0); } return $result; @@ -542,7 +543,7 @@ function drush_upgrade_site_upgrade_modules() { // Run updatedb to update all of the contrib extensions. drush_log(dt('About to perform updatedb for extensions'), 'ok'); - $result = drush_invoke_process_args('updatedb', array(), array('yes' => TRUE, '#interactive' => TRUE)); + $result = drush_invoke_process_args('updatedb', array(), array('yes' => TRUE), array('interactive' => TRUE)); if ($result === FALSE) { return drush_set_error('DRUSH_DRUPAL_CONTRIB_UPGRADE_FAILED', dt("The call to updatedb failed for the enabled contrib modules. Try running site-upgrade again with '--uninstall={module list}' to remove all contrib extensions prior to upgrade. Add modules back in until the problematic one is isolated. Please report problems in the issue queue of the module that is causing problems.")); } diff --git a/drush.php b/drush.php index c834dd0..af5f01c 100755 --- a/drush.php +++ b/drush.php @@ -196,16 +196,15 @@ function drush_shutdown() { } } - if (drush_get_context('DRUSH_BACKEND')) { + if (drush_get_context('DRUSH_BACKEND', FALSE)) { drush_backend_output(); } - elseif (drush_get_context('DRUSH_QUIET')) { + elseif (drush_get_context('DRUSH_QUIET', FALSE)) { ob_end_clean(); - } - - // If we are in pipe mode, emit the compact representation of the command, if available. - if (drush_get_context('DRUSH_PIPE')) { - drush_pipe_output(); + // If we are in pipe mode, emit the compact representation of the command, if available. + if (drush_get_context('DRUSH_PIPE')) { + drush_pipe_output(); + } } /** diff --git a/examples/example.aliases.drushrc.php b/examples/example.aliases.drushrc.php index 076697c..534523a 100644 --- a/examples/example.aliases.drushrc.php +++ b/examples/example.aliases.drushrc.php @@ -156,6 +156,9 @@ * '%files': Path to 'files' directory. This will be looked up if not specified. * '%root': A reference to the Drupal root defined in the 'root' item * in the site alias record. + * - 'php': path to custom php interpreter, defaults tu /usr/bin/php + * - 'php-options': commandline options for php interpreter, you may + * want to set this to '-d error_reporting="E_ALL^E_DEPRECATED"' * - 'command-specific': These options will only be set if the alias * is used with the specified command. In the example below, the option * `--no-cache` will be selected whenever the @stage alias diff --git a/includes/backend.inc b/includes/backend.inc index f26743a..3813bf1 100644 --- a/includes/backend.inc +++ b/includes/backend.inc @@ -61,11 +61,23 @@ */ define('DRUSH_BACKEND_OUTPUT_DELIMITER', 'DRUSH_BACKEND_OUTPUT_START>>>%s<< "\\0")); + $data['output'] = preg_replace("/$packet_regex/s", '', drush_backend_output_collect(NULL)); + } + + if (drush_get_context('DRUSH_QUIET', FALSE)) { + ob_end_clean(); + } $result_object = drush_backend_get_result(); if (isset($result_object)) { @@ -138,24 +165,69 @@ function drush_backend_output() { // Return the options that were set at the end of the process. $data['context'] = drush_get_merged_options(); - if (!drush_get_context('DRUSH_QUIET')) { - printf(DRUSH_BACKEND_OUTPUT_DELIMITER, json_encode($data)); + printf(DRUSH_BACKEND_OUTPUT_DELIMITER, json_encode($data)); +} + +/** + * Callback to collect backend command output. + */ +function drush_backend_output_collect($string) { + static $output = ''; + if (is_null($string)) { + return $output; + } + + $output .= $string; + return $string; +} + +/** + * Output buffer functions that discards all output but backend packets. + */ +function drush_backend_output_discard($string) { + $packet_regex = strtr(sprintf(DRUSH_BACKEND_PACKET_PATTERN, "([^\0]*)"), array("\0" => "\\0")); + if (preg_match_all("/$packet_regex/s", $string, $matches)) { + return implode('', $matches[0]); } } /** + * Output a backend packet if we're running as backend. + * + * @param packet + * The packet to send. + * @param data + * Data for the command. + * + * @return + * A boolean indicating whether the command was output. + */ +function drush_backend_packet($packet, $data) { + if (drush_get_context('DRUSH_BACKEND')) { + $data['packet'] = $packet; + $data = json_encode($data); + drush_print(sprintf(DRUSH_BACKEND_PACKET_PATTERN, $data), 0, STDERR); + return TRUE; + } + + return FALSE; +} + +/** * Parse output returned from a Drush command. * * @param string * The output of a drush command * @param integrate * Integrate the errors and log messages from the command into the current process. + * @param outputted + * Whether output has already been handled. * * @return * An associative array containing the data from the external command, or the string parameter if it * could not be parsed successfully. */ -function drush_backend_parse_output($string, $integrate = TRUE) { +function drush_backend_parse_output($string, $backend_options = array(), $outputted = FALSE) { $regex = sprintf(DRUSH_BACKEND_OUTPUT_DELIMITER, '(.*)'); preg_match("/$regex/s", $string, $match); @@ -170,9 +242,7 @@ function drush_backend_parse_output($string, $integrate = TRUE) { if (!empty($output)) { $data = json_decode($output, TRUE); if (is_array($data)) { - if ($integrate) { - _drush_backend_integrate($data); - } + _drush_backend_integrate($data, $backend_options, $outputted); return $data; } } @@ -180,19 +250,23 @@ function drush_backend_parse_output($string, $integrate = TRUE) { } /** - * Integrate log messages and error statuses into the current process. + * Integrate log messages and error statuses into the current + * process. * - * Output produced by the called script will be printed, errors will be set - * and log messages will be logged locally. + * Output produced by the called script will be printed if we didn't print it + * on the fly, errors will be set, and log messages will be logged locally, if + * not already logged. * * @param data * The associative array returned from the external command. + * @param outputted + * Whether output has already been handled. */ -function _drush_backend_integrate($data) { - if (is_array($data['log'])) { +function _drush_backend_integrate($data, $backend_options, $outputted) { + if (is_array($data['log']) && $backend_options['log'] && !$outputted) { foreach($data['log'] as $log) { $message = is_array($log['message']) ? implode("\n", $log['message']) : $log['message']; - if (!is_null($log['error'])) { + if (!is_null($log['error']) && $backend_options['integrate']) { drush_set_error($log['error'], $message); } else { @@ -201,13 +275,15 @@ function _drush_backend_integrate($data) { } } // Output will either be printed, or buffered to the drush_backend_output command. - if (drush_cmp_error('DRUSH_APPLICATION_ERROR') && !empty($data['output'])) { - drush_set_error("DRUSH_APPLICATION_ERROR", dt("Output from failed command :\n !output", array('!output' => $data['output']))); - } - else { - print ($data['output']); + // If the output has already been printed, then we do not need to show it again on a failure. + if (!$outputted) { + if (drush_cmp_error('DRUSH_APPLICATION_ERROR') && !empty($data['output'])) { + drush_set_error("DRUSH_APPLICATION_ERROR", dt("Output from failed command :\n !output", array('!output' => $data['output']))); + } + elseif ($backend_options['output']) { + _drush_backend_print_output($data['output'], $backend_options); + } } - } /** @@ -224,7 +300,8 @@ function _drush_backend_integrate($data) { * If it executed successfully, it returns an associative array containing the command * called, the output of the command, and the error code of the command. */ -function _drush_proc_open($cmd, $data = NULL, $context = NULL) { +function _drush_proc_open($cmd, $command_options = NULL, $context = NULL, $backend_options = array()) { + $descriptorspec = array( 0 => array("pipe", "r"), // stdin is a pipe that the child will read from 1 => array("pipe", "w"), // stdout is a pipe that the child will write to @@ -232,17 +309,29 @@ function _drush_proc_open($cmd, $data = NULL, $context = NULL) { ); $process = proc_open($cmd, $descriptorspec, $pipes, null, null, array('context' => $context)); if (is_resource($process)) { - if ($data) { - fwrite($pipes[0], json_encode($data)); // pass the data array in a JSON encoded string + if ($command_options) { + fwrite($pipes[0], json_encode($command_options)); // pass the data array in a JSON encoded string } fclose($pipes[0]); $info = stream_get_meta_data($pipes[1]); stream_set_blocking($pipes[1], TRUE); stream_set_timeout($pipes[1], 1); - $string = ''; + $output = ''; + $end_of_output = FALSE; + $outputted = FALSE; while (!feof($pipes[1]) && !$info['timed_out']) { - $string .= fgets($pipes[1], 4096); + $string = fread($pipes[1], 4096); + if (preg_match('/DRUSH_BACKEND_OUTPUT_START/', $string)) { + $end_of_output = TRUE; + } + if (!$end_of_output) { + drush_backend_parse_packets($string, $backend_options); + // Pass output through. + _drush_backend_print_output($string, $backend_options); + $outputted = TRUE; + } + $output .= $string; $info = stream_get_meta_data($pipes[1]); flush(); }; @@ -251,7 +340,9 @@ function _drush_proc_open($cmd, $data = NULL, $context = NULL) { stream_set_blocking($pipes[2], TRUE); stream_set_timeout($pipes[2], 1); while (!feof($pipes[2]) && !$info['timed_out']) { - $string .= fgets($pipes[2], 4096); + $string = fgets($pipes[2], 4096); + $output .= $string; + fwrite(STDERR, $string); $info = stream_get_meta_data($pipes[2]); flush(); }; @@ -259,57 +350,100 @@ function _drush_proc_open($cmd, $data = NULL, $context = NULL) { fclose($pipes[1]); fclose($pipes[2]); $code = proc_close($process); - return array('cmd' => $cmd, 'output' => $string, 'code' => $code); + return array('cmd' => $cmd, 'output' => $output, 'code' => $code, 'outputted' => $outputted); } return FALSE; } /** - * Invoke a drush backend command. - * - * @param command - * A defined drush command such as 'cron', 'status' or so on. - * @param args - * An array of command arguments. - * @param data - * Optional. An array containing options to pass to the call. Common options would be 'uri' if you want to call a command - * on a different site, or 'root', if you want to call a command using a different Drupal installation. - * Array items with a numeric key are treated as optional arguments to the command. - * @param method - * Optional. Defaults to 'GET'. - * If this parameter is set to 'POST', the $data array will be passed to the script being called as a JSON encoded string over - * the STDIN pipe of that process. This is preferable if you have to pass sensitive data such as passwords and the like. - * For any other value, the $data array will be collapsed down into a set of command line options to the script. - * @param integrate - * Optional. Defaults to TRUE. - * If TRUE, any error statuses or log messages will be integrated into the current process. This might not be what you want, - * if you are writing a command that operates on multiple sites. - * @param drush_path - * Optional. Defaults to the current drush.php file on the local machine, and - * to simply 'drush' (the drush script in the current PATH) on remote servers. - * You may also specify a different drush.php script explicitly. You will need - * to set this when calling drush on a remote server if 'drush' is not in the - * PATH on that machine. - * @param hostname - * Optional. A remote host to execute the drush command on. - * @param username - * Optional. Defaults to the current user. If you specify this, you can choose which module to send. - * - * @deprecated Prefer wrapper function @see drush_invoke_process when possible. - * n.b. the function drush_backend_invoke is now obsolete. - * - * @return - * If the command could not be completed successfully, FALSE. - * If the command was completed, this will return an associative array containing the data from drush_backend_output(). + * Print the output received from a call to backend invoke, + * adding the label to the head of each line if necessary. */ -function drush_backend_invoke_args($command, $args = array(), $data = array(), $method = 'GET', $integrate = TRUE, $drush_path = NULL, $hostname = NULL, $username = NULL, $ssh_options = NULL) { - $cmd = _drush_backend_generate_command($command, $args, $data, $method, $drush_path, $hostname, $username, $ssh_options); - return _drush_backend_invoke($cmd, $data, array_key_exists('#integrate', $data) ? $data['#integrate'] : $integrate); +function _drush_backend_print_output($output_string, $backend_options) { + if ($backend_options['output'] && !empty($output_string)) { + $output_label = array_key_exists('output-label', $backend_options) ? $backend_options['output-label'] : FALSE; + if ($output_label) { + foreach (explode("\n", $output_string) as $line) { + if (empty($line)) { + fwrite(STDOUT, "\n"); + } + else { + // We rtrim here because sometimes lines (e.g. from the console table + // output) are padded with spaces, and this can sometimes force a + // blank line to be inserted into the output. + fwrite(STDOUT, $output_label . rtrim($line) . "\n"); + } + } + } + else { + fwrite(STDOUT, $output_string); + } + } +} + +/** + * Parse out and remove backend packet from the supplied string and + * invoke the commands. + */ +function drush_backend_parse_packets(&$string, $backend_options) { + $packet_regex = strtr(sprintf(DRUSH_BACKEND_PACKET_PATTERN, "([^\0]*)"), array("\0" => "\\0")); + if (preg_match_all("/$packet_regex/s", $string, $match, PREG_PATTERN_ORDER)) { + foreach ($match[1] as $packet_data) { + $entry = (array) json_decode($packet_data); + if (is_array($entry) && isset($entry['packet'])) { + $function = 'drush_backend_packet_' . $entry['packet']; + if (function_exists($function)) { + $function($entry, $backend_options); + } + else { + drush_log(dt("Unknown backend packet @packet", array('@packet' => $entry['packet'])), 'notice'); + } + } + else { + drush_log(dt("Malformed backend packet"), 'error'); + drush_log(dt("Bad packet: @packet", array('@packet' => print_r($entry, TRUE))), 'debug'); + drush_log(dt("String is: @str", array('@str' => $packet_data), 'debug')); + } + } + + $string = trim(preg_replace("/$packet_regex/s", '', $string)); + } +} + +/** + * Backend command for setting errors. + */ +function drush_backend_packet_set_error($data, $backend_options) { + if (!$backend_options['integrate']) { + return; + } + drush_set_error($data['error'], $data['message']); +} + +/** + * Default options for backend_invoke commands. + */ +function _drush_backend_adjust_options($command, $command_options, $backend_options) { + $result = $backend_options + array( + 'method' => 'GET', + 'output' => TRUE, + 'log' => TRUE, + 'integrate' => TRUE, + ); + // Convert '#integrate' et. al. into backend options + foreach ($command_options as $key => $value) { + if (substr($key,0,1) != '#') { + $result[substr($key,1)] = $value; + } + } + return $result; } /** * Execute a new local or remote command in a new process. * + * n.b. Prefer drush_invoke_sitealias_args() to this function. + * * @param site_record * An array containing information used to generate the command. * 'remote-host' @@ -330,47 +464,53 @@ function drush_backend_invoke_args($command, $args = array(), $data = array(), $ * A defined drush command such as 'cron', 'status' or any of the available ones such as 'drush pm'. * @param args * An array of arguments for the command. - * @param data + * @param command_options * Optional. An array containing options to pass to the remote script. - * Array items with a numeric key are treated as optional arguments to the command. - * This parameter is a reference, as any options that have been represented as either an option, or an argument will be removed. - * This allows you to pass the left over options as a JSON encoded string, without duplicating data. - * Parameters that begin with a '#' are not passed on, but are used to affect - * the operation of backend invoke. Available options include: - * '#integrate' - * Print the output and merge the logs and error codes into - * the data structures for the running drush command. This causes - * the command to act as if it were called directly, without using - * backend invoke, while still running it in a separate process. - * Function results are still available. - * '#interactive' - * The output is displayed immediately, as it is produced, and it is - * possible for the user to send keyboard input to the command being - * executed. If interactive mode is used, then the command output, - * logs, etc. are NOT returned to the caller. - * '#override-simulated' - * Backend invoke will run the command even if DRUSH_SIMULATE is set. - * This is useful to run backend commands that fetch data that will - * be used by the simulated command. For example, sql-sync looks up - * the database options via backend invoke of sql-conf with override - * simulated set so that the sql-sync operation can be simulated using - * actual database setting values. - * @param method - * Optional. Defaults to 'GET'. - * If this parameter is set to 'POST', the $data array will be passed to the script being called as a JSON encoded string over - * the STDIN pipe of that process. This is preferable if you have to pass sensitive data such as passwords and the like. - * For any other value, the $data array will be collapsed down into a set of command line options to the script. - * @param integrate - * Optional. Defaults to TRUE. - * If TRUE, any error statuses or log messages will be integrated into the current process. This might not be what you want, - * if you are writing a command that operates on multiple sites. + * Array items with a numeric key are treated as optional arguments to the + * command. This parameter is a reference, as any options that have been + * represented as either an option, or an argument will be removed. This + * allows you to pass the left over options as a JSON encoded string, + * without duplicating data. + * @param backend_options + * Optional. An array of options for the invocation. + * @param backend_options + * Optional. An array of options for the invocation. + * 'method' + * Optional. Defaults to 'GET'. + * If this parameter is set to 'POST', the $data array will be passed + * to the script being called as a JSON encoded string over the STDIN + * pipe of that process. This is preferable if you have to pass + * sensitive data such as passwords and the like. + * For any other value, the $data array will be collapsed down into a + * set of command line options to the script. + * 'integrate' + * Optional. Defaults to TRUE. + * If TRUE, any error statuses will be integrated into the current + * process. This might not be what you want, if you are writing a + * command that operates on multiple sites. + * 'log' + * Optional. Defaults to TRUE. + * If TRUE, any log messages will be integrated into the current + * process. + * 'output' + * Optional. Defaults to TRUE. + * If TRUE, output from the command will be synchronously printed to + * stdout. + * 'drush-script' + * Optional. Defaults to the current drush.php file on the local + * machine, and to simply 'drush' (the drush script in the current + * PATH) on remote servers. You may also specify a different drush.php + * script explicitly. You will need to set this when calling drush on + * a remote server if 'drush' is not in the PATH on that machine. * * @return - * A text string representing a fully escaped command. + * If the command could not be completed successfully, FALSE. + * If the command was completed, this will return an associative array containing the data from drush_backend_output(). */ -function drush_backend_invoke_sitealias($site_record, $command, $args, $data = array(), $method = 'GET', $integrate = TRUE) { - $cmd = _drush_backend_generate_command_sitealias($site_record, $command, $args, $data, $method); - return _drush_backend_invoke($cmd, $data, array_key_exists('#integrate', $data) ? $data['#integrate'] : $integrate); +function drush_backend_invoke_sitealias_command($site_record, $command, $args, $command_options = array(), $backend_options = array()) { + $backend_options = _drush_backend_adjust_options($command, $command_options, $backend_options); + $cmd = _drush_backend_generate_command_sitealias($site_record, $command, $args, $command_options, $backend_options); + return _drush_backend_invoke($cmd, $command_options, $backend_options); } /** @@ -385,34 +525,34 @@ function drush_backend_invoke_sitealias($site_record, $command, $args, $data = a * * @param cmd * The complete command line call to use. - * @param data + * @param command_options * An associative array to pass to the remote script. - * @param integrate - * Integrate data from remote script with local process. + * @param backend_options + * Options for the invocation. * * @return * If the command could not be completed successfully, FALSE. * If the command was completed, this will return an associative array containing the data from drush_backend_output(). */ -function _drush_backend_invoke($cmd, $data = null, $integrate = TRUE) { - if (drush_get_context('DRUSH_SIMULATE') && !array_key_exists('#override-simulated', $data)) { +function _drush_backend_invoke($cmd, $command_options = NULL, $backend_options = array()) { + if (drush_get_context('DRUSH_SIMULATE') && !array_key_exists('override-simulated', $backend_options)) { drush_print(dt('Simulating backend invoke: !cmd', array('!cmd' => $cmd))); return FALSE; } drush_log(dt('Backend invoke: !cmd', array('!cmd' => $cmd)), 'command'); - if (array_key_exists('#interactive', $data)) { + if (array_key_exists('interactive', $backend_options)) { drush_log(dt("executing !cmd", array('!cmd' => $cmd))); return drush_op_system($cmd); } else { - $proc = _drush_proc_open($cmd, $data); + $proc = _drush_proc_open($cmd, $command_options, NULL, $backend_options); - if (($proc['code'] == DRUSH_APPLICATION_ERROR) && $integrate) { + if (($proc['code'] == DRUSH_APPLICATION_ERROR) && $backend_options['integrate']) { drush_set_error('DRUSH_APPLICATION_ERROR', dt("The external command could not be executed due to an application error.")); } if ($proc['output']) { - $values = drush_backend_parse_output($proc['output'], $integrate); + $values = drush_backend_parse_output($proc['output'], $backend_options, $proc['outputted']); if (is_array($values)) { return $values; } @@ -425,48 +565,25 @@ function _drush_backend_invoke($cmd, $data = null, $integrate = TRUE) { } /** - * Generate a command to execute. - * - * @param command - * A defined drush command such as 'cron', 'status' or any of the available ones such as 'drush pm'. - * @param args - * An array of arguments for the command. - * @param data - * Optional. An array containing options to pass to the remote script. - * Array items with a numeric key are treated as optional arguments to the command. - * This parameter is a reference, as any options that have been represented as either an option, or an argument will be removed. - * This allows you to pass the left over options as a JSON encoded string, without duplicating data. - * @param method - * Optional. Defaults to 'GET'. - * If this parameter is set to 'POST', the $data array will be passed to the script being called as a JSON encoded string over - * the STDIN pipe of that process. This is preferable if you have to pass sensitive data such as passwords and the like. - * For any other value, the $data array will be collapsed down into a set of command line options to the script. - * @param drush_path - * Optional. Defaults to the current drush.php file on the local machine, and - * to simply 'drush' (the drush script in the current PATH) on remote servers. - * You may also specify a different drush.php script explicitly. You will need - * to set this when calling drush on a remote server if 'drush' is not in the - * PATH on that machine. - * @param hostname - * Optional. A remote host to execute the drush command on. - * @param username - * Optional. Defaults to the current user. If you specify this, you can choose which module to send. - * - * @return - * A text string representing a fully escaped command. - * - * @deprecated Is not as flexible as recommended command. @see _drush_backend_generate_command_sitealias(). + * Helper function that generates an anonymous site alias specification for + * the given parameters. */ -function _drush_backend_generate_command($command, $args, &$data, $method = 'GET', $drush_path = null, $hostname = null, $username = null, $ssh_options = NULL) { - return _drush_backend_generate_command_sitealias( - array( - 'remote-host' => $hostname, - 'remote-user' => $username, - 'ssh-options' => $ssh_options, - 'path-aliases' => array( - '%drush-script' => $drush_path, - ), - ), $command, $args, $data, $method); +function drush_backend_generate_sitealias($backend_options) { + // Ensure default values. + $backend_options += array( + 'remote-host' => NULL, + 'remote-user' => NULL, + 'ssh-options' => NULL, + 'drush-script' => NULL, + ); + return array( + 'remote-host' => $backend_options['remote-host'], + 'remote-user' => $backend_options['remote-user'], + 'ssh-options' => $backend_options['ssh-options'], + 'path-aliases' => array( + '%drush-script' => $backend_options['drush-script'], + ), + ); } /** @@ -492,35 +609,39 @@ function _drush_backend_generate_command($command, $args, &$data, $method = 'GET * A defined drush command such as 'cron', 'status' or any of the available ones such as 'drush pm'. * @param args * An array of arguments for the command. - * @param data + * @param command_options * Optional. An array containing options to pass to the remote script. - * Array items with a numeric key are treated as optional arguments to the command. - * This parameter is a reference, as any options that have been represented as either an option, or an argument will be removed. - * This allows you to pass the left over options as a JSON encoded string, without duplicating data. - * @param method - * Optional. Defaults to 'GET'. - * If this parameter is set to 'POST', the $data array will be passed to the script being called as a JSON encoded string over - * the STDIN pipe of that process. This is preferable if you have to pass sensitive data such as passwords and the like. - * For any other value, the $data array will be collapsed down into a set of command line options to the script. + * Array items with a numeric key are treated as optional arguments to the + * command. This parameter is a reference, as any options that have been + * represented as either an option, or an argument will be removed. This + * allows you to pass the left over options as a JSON encoded string, + * without duplicating data. + * @param backend_options + * Optional. An array of options for the invocation. + * @see drush_backend_invoke for documentation. * * @return * A text string representing a fully escaped command. */ -function _drush_backend_generate_command_sitealias($site_record, $command, $args, &$data, $method = 'GET') { +function _drush_backend_generate_command_sitealias($site_record, $command, $args, &$command_options, $backend_options = array()) { $drush_path = null; $php = ''; - $hostname = array_key_exists('remote-host', $site_record) ? $site_record['remote-host'] : null; - $username = array_key_exists('remote-user', $site_record) ? $site_record['remote-user'] : null; - $ssh_options = array_key_exists('ssh-options', $site_record) ? $site_record['ssh-options'] : null; - $os = drush_os($site_record); + $site_record += array( + 'remote-host' => NULL, + 'remote-user' => NULL, + 'ssh-options' => NULL, + 'path-aliases' => array(), + ); + $site_record['path-aliases'] += array( + '%drush-script' => NULL, + ); - $drush_path = NULL; - if (array_key_exists('path-aliases', $site_record)) { - if (array_key_exists('%drush-script', $site_record['path-aliases'])) { - $drush_path = $site_record['path-aliases']['%drush-script']; - } - } + $hostname = $site_record['remote-host']; + $username = $site_record['remote-user']; + $ssh_options = $site_record['ssh-options']; + $drush_path = $site_record['path-aliases']['%drush-script']; + $os = drush_os($site_record); if (drush_is_local_host($hostname)) { $hostname = null; @@ -531,22 +652,22 @@ function _drush_backend_generate_command_sitealias($site_record, $command, $args // machine, we will use DRUSH_COMMAND, which is the path to the drush.php // that is running right now. For remote commands, we will run a wrapper // script instead of drush.php -- drush.bat on Windows, or drush on Linux. - $drush_command = drush_build_drush_command($drush_path, array_key_exists('php', $data) ? $data['php'] : NULL, $os, !empty($hostname)); + $drush_command = drush_build_drush_command($drush_path, array_key_exists('php', $command_options) ? $command_options['php'] : NULL, $os, !empty($hostname)); - $data['root'] = array_key_exists('root', $data) ? $data['root'] : drush_get_context('DRUSH_DRUPAL_ROOT'); - $data['uri'] = array_key_exists('uri', $data) ? $data['uri'] : drush_get_context('DRUSH_URI'); + $command_options['root'] = array_key_exists('root', $command_options) ? $command_options['root'] : drush_get_context('DRUSH_DRUPAL_ROOT'); + $command_options['uri'] = array_key_exists('uri', $command_options) ? $command_options['uri'] : drush_get_context('DRUSH_URI'); - $option_str = _drush_backend_argument_string($data, $method, $os); - foreach ($data as $key => $arg) { + $option_str = _drush_backend_argument_string($command_options, $backend_options['method']); + foreach ($command_options as $key => $arg) { if (is_numeric($key)) { $args[] = $arg; - unset($data[$key]); + unset($command_options[$key]); } } foreach ($args as $arg) { $command .= ' ' . drush_escapeshellarg($arg, $os); } - $cmd = $drush_command . " " . $option_str . " " . $command . (empty($data['#interactive']) ? " --backend" : ""); + $cmd = $drush_command . " " . $option_str . " " . $command . (empty($backend_options['interactive']) ? " --backend=2" : ""); if (!is_null($hostname)) { $username = (!is_null($username)) ? drush_escapeshellarg($username, "LOCAL") . "@" : ''; $ssh_options = (!is_null($ssh_options)) ? $ssh_options : drush_get_option('ssh-options', "-o PasswordAuthentication=no"); @@ -554,7 +675,7 @@ function _drush_backend_generate_command_sitealias($site_record, $command, $args } else { // TODO: `tty` is not usable on Windows. Is this necessary at all, and if so, is there a better way to do it? - $interactive = ' ' . ((drush_is_windows() || empty($data['#interactive'])) ? '' : ' > `tty`') . ' 2>&1'; + $interactive = ' ' . ((drush_is_windows() || empty($backend_options['interactive'])) ? '' : ' > `tty`') . ' 2>&1'; $cmd .= $interactive; } @@ -562,23 +683,6 @@ function _drush_backend_generate_command_sitealias($site_record, $command, $args } /** - * A small utility function to call a drush command in the background. - * - * Takes the same parameters as drush_backend_invoke, but forks a new - * process by calling the command using system() and adding a '&' at the - * end of the command. - * - * Use this if you don't care what the return value of the command may be. - */ -function drush_backend_fork($command, $data, $drush_path = null, $hostname = null, $username = null) { - $data['quiet'] = TRUE; - $args = explode(" ", $command); - $command = array_shift($args); - $cmd = "(" . _drush_backend_generate_command($command, $args, $data, 'GET', $drush_path, $hostname, $username) . ' &) > /dev/null'; - drush_op_system($cmd); -} - -/** * Map the options to a string containing all the possible arguments and options. * * @param data @@ -704,3 +808,34 @@ function _drush_backend_get_stdin() { } return FALSE; } + +/* =========== Deprecated functions below this line: avoid using these ================== */ + +// For internal use only; use drush_invoke_process_args() instead. Future: to be renamed _drush_backend_invoke_command +function drush_backend_invoke_command($command, $args, $command_options = array(), $backend_options = array()) { + return drush_backend_invoke_sitealias_command(drush_backend_generate_sitealias($backend_options), $command, $args, $command_options, $backend_options); +} + +// Use drush_invoke_process_args() instead of drush_backend_invoke_args. Future: drush_invoke_process_args to be removed. +function drush_backend_invoke_args($command, $args = array(), $data = array(), $method = 'GET', $integrate = TRUE, $drush_path = NULL, $hostname = NULL, $username = NULL, $ssh_options = NULL) { + return drush_backend_invoke_command($command, $args, $data, + array( + 'method' => $method, + 'integrate' => $integrate, + 'remote-host' => $hostname, + 'remote-user' => $username, + 'ssh-options' => $ssh_options, + 'drush-script' => $drush_path, + ) + ); +} + +// Use drush_invoke_sitealias_args() instead of drush_backend_invoke_sitealias. Future: drush_backend_invoke_sitealias to be removed. +function drush_backend_invoke_sitealias($site_record, $command, $args, $data = array(), $method = 'GET', $integrate = TRUE) { + return drush_backend_invoke_sitealias_command($site_record, $command, $args, $data, + array( + 'method' => $method, + 'integrate' => $integrate, + ) + ); +} diff --git a/includes/command.inc b/includes/command.inc index b21d51b..c5a1d54 100644 --- a/includes/command.inc +++ b/includes/command.inc @@ -238,7 +238,7 @@ function drush_invoke_process($command_name) { /** * Invoke a command in a new process. - * Prefer this to drush_backend_invoke_args(...); + * Prefer this to drush_backend_invoke_command(...); * * @param command_name * The drush command to execute. @@ -248,8 +248,8 @@ function drush_invoke_process($command_name) { * array containing the results of the API call. * @see drush_backend_get_result() */ -function drush_invoke_process_args($command_name, $commandline_args, $commandline_options = array()) { - return drush_backend_invoke_args($command_name, $commandline_args, $commandline_options); +function drush_invoke_process_args($command_name, $commandline_args, $commandline_options = array(), $backend_options = array()) { + return drush_backend_invoke_command($command_name, $commandline_args, $commandline_options, $backend_options); } /** @@ -266,12 +266,12 @@ function drush_invoke_process_args($command_name, $commandline_args, $commandlin * array containing the results of the API call. * @see drush_backend_get_result() */ -function drush_invoke_sitealias($site_alias_record, $command_name) { +function drush_invoke_sitealias($site_alias_record, $command_name, $backend_options = FALSE) { $args = func_get_args(); array_shift($args); array_shift($args); - return drush_invoke_sitealias_args($site_alias_record, $command_name, $args); + return drush_invoke_sitealias_args($site_alias_record, $command_name, $args, array(), $backend_options); } /** @@ -292,14 +292,14 @@ function drush_invoke_sitealias($site_alias_record, $command_name) { * array containing the results of the API call. * @see drush_backend_get_result() */ -function drush_invoke_sitealias_args($site_alias_record, $command_name, $commandline_args, $commandline_options = array()) { +function drush_invoke_sitealias_args($site_alias_record, $command_name, $commandline_args, $commandline_options = array(), $backend_options = FALSE) { // If the first parameter is not a site alias record, // then presume it is an alias name, and try to look up // the alias record. if (!is_array($site_alias_record)) { $site_alias_record = drush_sitealias_get_record($site_alias_record); } - return drush_do_site_command($site_alias_record, $command_name, $commandline_args, $commandline_options); + return drush_do_site_command($site_alias_record, $command_name, $commandline_args, $commandline_options, $backend_options); } /** @@ -315,7 +315,13 @@ function drush_invoke_sitealias_args($site_alias_record, $command_name, $command function drush_redispatch_get_options() { // Start off by taking everything from the site alias and command line // ('cli' context) - $options = array_merge(drush_get_context('alias'), drush_get_context('cli')); + $cli_context = drush_get_context('cli'); + // local php settings should not override sitealias settings + unset($cli_context['php']); + unset($cli_context['php-options']); + // cli overrides sitealias + $options = $cli_context + drush_get_context('alias'); + $options = array_diff_key($options, array_flip(drush_sitealias_site_selection_keys())); unset($options['command-specific']); unset($options['path-aliases']); @@ -332,10 +338,6 @@ function drush_redispatch_get_options() { } } } - // 'php', if needed, will be included in DRUSH_COMMAND. If DRUSH_COMMAND - // is not used (e.g. when calling a remote instance of drush), then --php - // should not be passed along. - unset($options['php']); // If --bootstrap-to-first-arg is specified, do not // pass it along to remote commands. unset($options['bootstrap-to-first-arg']); diff --git a/includes/drush.inc b/includes/drush.inc index 8f61989..ffe38b0 100644 --- a/includes/drush.inc +++ b/includes/drush.inc @@ -782,6 +782,7 @@ function drush_remote_command() { } $command = array_shift($args); $multi_options = drush_get_context('cli'); + $backend_options = array(); if (!drush_get_option('no-label', FALSE) && !$interactive) { $label_separator = ' >> '; @@ -793,24 +794,17 @@ function drush_remote_command() { } $multi_options['reserve-margin'] = $max_name_length + strlen($label_separator); foreach ($site_list as $alias_name => $alias_record) { - $values = drush_do_site_command($alias_record, $command, $args, $multi_options); - foreach (explode("\n", $values['output']) as $line) { - if (empty($line)) { - drush_print(); - } - else { - drush_print(str_pad($alias_name, $max_name_length, " ") . $label_separator . $line); - } - } + $backend_options['output-label'] = str_pad($alias_name, $max_name_length, " ") . $label_separator; + $values = drush_do_site_command($alias_record, $command, $args, $multi_options, $backend_options); } } else { if ($interactive) { - $multi_options['#interactive'] = TRUE; + $backend_options['interactive'] = TRUE; } foreach ($site_list as $alias_name => $alias_record) { drush_print(dt("!site >> !command", array('!command' => $command . ' ' . implode(" ", $args), '!site' => $alias_name))); - $values = drush_do_site_command($alias_record, $command, $args, $multi_options); + $values = drush_do_site_command($alias_record, $command, $args, $multi_options, $backend_options); drush_print($values['output']); } } @@ -872,7 +866,7 @@ function drush_do_multiple_command($command, $source_record, $destination_record $target_spec = drush_sitealias_alias_record_to_spec($one_target); drush_log(dt('Begin do_multiple !command via backend invoke', array('!command' => $command))); - $values = drush_backend_invoke_args($command, array($source_spec . $source_path, $target_spec . $destination_path), $data, 'GET', TRUE); + $values = drush_invoke_process_args($command, array($source_spec . $source_path, $target_spec . $destination_path), $data); drush_log(dt('Backend invoke is complete')); } } @@ -886,16 +880,31 @@ function drush_do_multiple_command($command, $source_record, $destination_record * The standard function that provides this service is called * drush_invoke_sitealias_args. Please call the standard function * unless you need to set $integrate = TRUE. + * + * @param backend_options + * TRUE - integrate errors + * FALSE - do not integrate errors + * array - @see drush_backend_invoke_sitealias_command */ -function drush_do_site_command($site_record, $command, $args = array(), $data = array(), $integrate = FALSE) { +function drush_do_site_command($site_record, $command, $args = array(), $data = array(), $backend_options = FALSE) { $values = NULL; if (!empty($site_record)) { + if (is_bool($backend_options)) { + $integrate = $backend_options; + $backend_options = array('integrate' => $integrate); + } foreach ($site_record as $key => $value) { if (!isset($data[$key]) && !in_array($key, drush_sitealias_site_selection_keys())) { $data[$key] = $site_record[$key]; } } - $values = drush_backend_invoke_sitealias($site_record, $command, $args, $data, 'GET', $integrate); + // By default, if the caller does not specify a value for 'output', but does + // specify 'integrate' === FALSE, then we will set output to FALSE. Otherwise we + // will allow it to default to TRUE. + if ((array_key_exists('integrate', $backend_options)) && ($backend_options['integrate'] === FALSE) && (!array_key_exists('output', $backend_options))) { + $backend_options['output'] = FALSE; + } + $values = drush_backend_invoke_sitealias_command($site_record, $command, $args, $data, $backend_options); } return $values; } @@ -905,15 +914,9 @@ function drush_do_site_command($site_record, $command, $args = array(), $data = * options that were passed to this invocation of drush. */ function drush_do_command_redispatch($command, $args = array(), $remote_host = NULL, $remote_user = NULL, $drush_path = NULL) { - $data = drush_redispatch_get_options(); - - // The option allows ad hoc usage of #interactive. - // @see _drush_backend_invoke(). - if (drush_get_option(array('interactive', 'ia'))) { - $data['#interactive'] = TRUE; - } + $command_options = drush_redispatch_get_options(); - // If the path to drush was supplied, then pass it to backend invoke. + // If the path to drush was supplied, then use it to invoke the new command. if ($drush_path == NULL) { $drush_path = drush_get_option('drush-script'); if (!isset($drush_path)) { @@ -923,10 +926,15 @@ function drush_do_command_redispatch($command, $args = array(), $remote_host = N } } } - // Call through to backend invoke. - drush_log(dt('Begin redispatch via backend invoke')); - $values = drush_backend_invoke_args($command, $args, $data, 'GET', TRUE, $drush_path, $remote_host, $remote_user); - drush_log(dt('Backend invoke is complete')); + $backend_options = array('drush-script' => $drush_path, 'remote-host' => $remote_host, 'remote-user' => $remote_user, 'integrate' => TRUE); + if (drush_get_option(array('interactive', 'ia'))) { + $backend_options['interactive'] = TRUE; + } + + // Run the command in a new process. + drush_log(dt('Begin redispatch via invoke process')); + $values = drush_invoke_process_args($command, $args, $command_options, $backend_options); + drush_log(dt('Invoke process is complete')); return $values; } @@ -975,6 +983,28 @@ function drush_log($message, $type = 'notice', $error = null) { ); $entry['error'] = $error; $log[] = $entry; + drush_backend_packet('log', $entry); + + return $callback($entry); +} + +/** + * Backend command callback. Add a log message to the log history. + * + * @param entry + * The log entry. + */ +function drush_backend_packet_log($entry, $backend_options) { + if (!$backend_options['log']) { + return; + } + $log =& drush_get_context('DRUSH_LOG', array()); + $callback = drush_get_context('DRUSH_LOG_CALLBACK', '_drush_print_log'); + $log[] = $entry; + // Yes, this looks odd, but we might in fact be a backend command + // that ran another backend command. + drush_backend_packet('log', $entry); + return $callback($entry); } @@ -1255,7 +1285,9 @@ function drush_set_error($error, $message = null) { } $error_log[$error][] = $message; - drush_log(($message) ? $message : $error, 'error', $error); + if (!drush_backend_packet('set_error', array('error' => $error, 'message' => $message))) { + drush_log(($message) ? $message : $error, 'error', $error); + } return FALSE; } diff --git a/includes/environment.inc b/includes/environment.inc index 94bcbca..64f5d48 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -642,22 +642,33 @@ function _drush_bootstrap_drush() { $backend = drush_set_context('DRUSH_BACKEND', drush_get_option(array('b', 'backend'))); + // Pipe implies quiet. + $quiet = drush_set_context('DRUSH_QUIET', drush_get_option(array('q', 'quiet', 'p', 'pipe'))); + + drush_set_context('DRUSH_PIPE', drush_get_option(array('p', 'pipe'))); + if ($backend) { // Load options passed as a JSON encoded string through STDIN. $stdin_options = _drush_backend_get_stdin(); if (is_array($stdin_options)) { drush_set_context('stdin', $stdin_options); } + // Add an output buffer handler to collect output/pass through backend + // packets. Using a chunksize of 2 ensures that each line is flushed + // straight away. + if ($quiet) { + // Pass through of backend packets, discard regular output. + ob_start('drush_backend_output_discard', 2); + } + else { + // Collect output. + ob_start('drush_backend_output_collect', 2); + } } - // Pipe implies quiet. - $quiet = drush_set_context('DRUSH_QUIET', drush_get_option(array('q', 'quiet', 'p', 'pipe'))); - - drush_set_context('DRUSH_PIPE', drush_get_option(array('p', 'pipe'))); - - // When running in backend mode, all output is buffered, and returned - // as a property of a JSON encoded associative array. - if ($backend || $quiet) { + // In non-backend quiet mode we start buffering and discards it on command + // completion. + if ($quiet && !$backend) { ob_start(); } @@ -965,10 +976,13 @@ function _drush_bootstrap_drupal_database() { * Attempt to load the full Drupal system. */ function _drush_bootstrap_drupal_full() { - ob_start(); + if (!drush_get_context('DRUSH_QUIET', FALSE)) { + ob_start(); + } drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); - ob_end_clean(); - + if (!drush_get_context('DRUSH_QUIET', FALSE)) { + ob_end_clean(); + } // If needed, prod module_implements() to recognize our system_watchdog() implementation. $dogs = module_implements('watchdog'); if (!in_array('system', $dogs)) { diff --git a/includes/sitealias.inc b/includes/sitealias.inc index f526df4..c7da194 100644 --- a/includes/sitealias.inc +++ b/includes/sitealias.inc @@ -602,9 +602,9 @@ function _drush_sitealias_cache_alias($alias_name, $alias_record) { */ function drush_sitealias_add_db_url(&$alias_record) { if (!isset($alias_record['db-url']) && !isset($alias_record['databases']) && !isset($alias_record['site-list'])) { - // We use sql-conf to fetch our database info. We set #override-simulated so that + // We use sql-conf to fetch our database info. We set 'override-simulated' so that // we will fetch the database values even in --simulate mode. - $values = drush_invoke_sitealias_args($alias_record, "sql-conf", array(), array('db-url' => TRUE, '#override-simulated' => TRUE)); + $values = drush_invoke_sitealias_args($alias_record, "sql-conf", array(), array('db-url' => TRUE), array('integrate' => FALSE, 'override-simulated' => TRUE)); if (isset($values['object']['db-url'])) { $alias_record['db-url'] = $values['object']['db-url']; } @@ -642,7 +642,7 @@ function drush_sitealias_add_db_settings(&$alias_record) // If the alias record does not have a defined 'databases' entry, // then we'll need to look one up if (!isset($alias_record['db-url']) && !isset($alias_record['databases']) && !isset($alias_record['site-list'])) { - $values = drush_invoke_sitealias_args($alias_record, "sql-conf", array(), array('all' => TRUE, '#override-simulated' => TRUE)); + $values = drush_invoke_sitealias_args($alias_record, "sql-conf", array(), array('all' => TRUE), array('integrate' => FALSE, 'override-simulated' => TRUE)); if (is_array($values) && ($values['error_status'] == 0)) { $altered_record = TRUE; // If there are any special settings in the '@self' record returned by drush_invoke_sitealias_args, @@ -740,7 +740,7 @@ function drush_sitealias_resolve_path_references(&$alias_record, $test_string = $status_values = _core_site_status_table($project_list); } else { - $values = drush_invoke_sitealias_args($alias_record, "status", array(), empty($project_list) ? array() : array('project' => $project_list, '#override-simulated' => TRUE)); + $values = drush_invoke_sitealias_args($alias_record, "status", array(), empty($project_list) ? array() : array('project' => $project_list), array('integrate' => FALSE, 'override-simulated' => TRUE)); $status_values = $values['object']; } if (isset($status_values['%paths'])) { @@ -1367,9 +1367,18 @@ function drush_sitealias_set_alias_context($site_alias_settings, $prefix = '') { // There are some items that we should just skip $skip_list = drush_get_special_keys(); - // Also skip 'remote-host' and 'remote-user' if 'remote-host' is actually - // the local machine - if (array_key_exists('remote-host', $site_alias_settings) && drush_is_local_host($site_alias_settings['remote-host'])) { + // If 'php-options' are set in the alias, then we will force drush + // to redispatch via the remote dispatch mechanism even if the target is localhost. + if (array_key_exists('php-options', $site_alias_settings) || drush_get_context('DRUSH_BACKEND', FALSE)) { + if (!array_key_exists('remote-host', $site_alias_settings)) { + $site_alias_settings['remote-host'] = 'localhost'; + } + } + // If 'php-options' are not set in the alias, then skip 'remote-host' + // and 'remote-user' if 'remote-host' is actually the local machine. + // This prevents drush from using the remote dispatch mechanism (the command + // is just run directly on the local machine, bootstrapping to the specified alias) + elseif (array_key_exists('remote-host', $site_alias_settings) && drush_is_local_host($site_alias_settings['remote-host'])) { $skip_list[] = 'remote-host'; $skip_list[] = 'remote-user'; } diff --git a/tests/backendTest.php b/tests/backendTest.php index c6d64aa..eb25cc7 100644 --- a/tests/backendTest.php +++ b/tests/backendTest.php @@ -24,10 +24,10 @@ class backendCase extends Drush_TestCase { * General handling of site aliases will be in sitealiasTest.php. */ function testOrigin() { - $exec = sprintf('%s %s version --simulate --ssh-options=%s | grep ssh', self::unish_escapeshellarg(UNISH_DRUSH), self::unish_escapeshellarg('user@server/path/to/drupal#sitename'), self::unish_escapeshellarg('-i mysite_dsa')); + $exec = sprintf('%s %s version --simulate --ssh-options=%s 2>/dev/null | grep ssh', self::unish_escapeshellarg(UNISH_DRUSH), self::unish_escapeshellarg('user@server/path/to/drupal#sitename'), self::unish_escapeshellarg('-i mysite_dsa')); $this->execute($exec); // $expected might be different on non unix platforms. We shall see. - $expected = "Simulating backend invoke: ssh -i mysite_dsa user@server 'drush --uri=sitename --root=/path/to/drupal --simulate version 2>&1' 2>&1"; + $expected = "Simulating backend invoke: ssh -i mysite_dsa user@server 'drush --simulate --uri=sitename --root=/path/to/drupal version 2>&1' 2>&1"; $output = $this->getOutput(); $this->assertEquals($expected, $output, 'Expected ssh command was built'); } @@ -41,7 +41,7 @@ class backendCase extends Drush_TestCase { */ function testTarget() { $stdin = json_encode(array('filter'=>'sql')); - $exec = sprintf('echo %s | %s help --backend', self::unish_escapeshellarg($stdin), self::unish_escapeshellarg(UNISH_DRUSH)); + $exec = sprintf('echo %s | %s help --backend 2>/dev/null', self::unish_escapeshellarg($stdin), self::unish_escapeshellarg(UNISH_DRUSH)); $this->execute($exec); $parsed = $this->parse($this->getOutput()); $this->assertTrue((bool) $parsed, 'Successfully parsed backend output'); @@ -54,7 +54,7 @@ class backendCase extends Drush_TestCase { $this->assertEquals('Bootstrap to phase 0.', $parsed['log'][0]['message']); // Check error propogation by requesting an invalid command (missing Drupal site). - $exec = sprintf('%s core-cron --backend', self::unish_escapeshellarg(UNISH_DRUSH)); + $exec = sprintf('%s core-cron --backend 2>/dev/null', self::unish_escapeshellarg(UNISH_DRUSH)); $this->execute($exec, self::EXIT_ERROR); $parsed = $this->parse($this->getOutput()); $this->assertEquals(1, $parsed['error_status']); @@ -62,6 +62,25 @@ class backendCase extends Drush_TestCase { } /* + * Covers the following target responsibilities. + * - Insures that the 'Drush version' line from drush status appears in the output. + * - Insures that the backend output start marker appears in the output (this is a backend command). + * - Insures that the drush output appears before the backend output start marker (output is displayed in 'real time' as it is produced). + */ + function testRealtimeOutput() { + $exec = sprintf('%s core-status --backend 2>&1', self::unish_escapeshellarg(UNISH_DRUSH)); + $this->execute($exec); + + $output = $this->getOutput(); + $drush_version_offset = strpos($output, "Drush version"); + $backend_output_offset = strpos($output, "DRUSH_BACKEND_OUTPUT_START>>>"); + + $this->assertTrue($drush_version_offset !== FALSE, "'Drush version' string appears in output."); + $this->assertTrue($backend_output_offset !== FALSE, "Drush backend output marker appears in output."); + $this->assertTrue($drush_version_offset < $backend_output_offset, "Drush version string appears in output before the backend output marker."); + } + + /* * A slightly less functional copy of drush_backend_parse_output(). */ function parse($string) {