- 5.3
- 5.4
env:
- - UNISH_DB_URL=mysql://root:@127.0.0.1 PHPUNIT_ARGS=--exclude-group=make
- - UNISH_DB_URL=mysql://root:@127.0.0.1 PHPUNIT_ARGS=--group=make
- - UNISH_DB_URL=pgsql://postgres:@127.0.0.1 PHPUNIT_ARGS=--exclude-group=make
- - UNISH_DB_URL=pgsql://postgres:@127.0.0.1 PHPUNIT_ARGS=--group=make
+ - UNISH_DB_URL=mysql://root:@127.0.0.1
+ - UNISH_DB_URL=pgsql://postgres:@127.0.0.1
- UNISH_DB_URL=sqlite://none/of/this/matters
before_script:
- export UNISH_DRUSH="${PWD}/drush" && cd tests
-script: phpunit $PHPUNIT_ARGS
+script: phpunit --verbose
* Drush is designed for a Unix-like OS (Linux, OS X)
-* Most Drush commands run on Windows. See INSTALLING DRUSH ON WINDOWS, below.
+* Some Drush commands run on Windows. See INSTALLING DRUSH ON WINDOWS, below.
* Drush works with Drupal 6, Drupal 7, and usually Drupal 8.
and usage-send commands to more carefully send data.
6. Optional. See examples/example.bashrc for instructions on how to add some
- useful shell aliases that provides even tighter integration between
- drush and bash. You may source this file directly into your shell by adding to
- your .bashrc (rr equivalent): source /path/to/drush/examples/example.bashrc
+ very useful shell aliases that provides even tighter integration between
+ drush and bash.
-7. Optional. If you didn't source it in Step 6 above, see top of
+7. Optional. If you didn't source it in Step 5 above, see top of
drush.complete.sh file for instructions adding bash completion for drush
command to your shell. Once configured, completion works for site aliases,
command names, shell aliases, global options, and command-specific options.
INSTALLING DRUSH ON WINDOWS:
----------------------------
-Windows support has improved, but is still lagging. Consider using on
+Windows support is improving, but is still lacking! Consider using on
Linux/Unix/OSX using Virtualbox or other virtual machine.
There is a Windows msi installer for drush available at:
* Originally developed by Arto Bendiken <http://bendiken.net/> for Drupal 4.7.
* Redesigned by Franz Heinzmann (frando) <http://unbiskant.org/> in May 2007 for Drupal 5.
* Maintained by Moshe Weitzman <http://drupal.org/moshe> with much help from
- Owen Barton, greg.1.anderson, jonhattan, Mark Sonnabaum, and Jonathan Hedstrom.
+ Owen Barton, Adrian Rossouw, greg.1.anderson, jonhattan, Jonathan Hedstrom.
// Create the unique temporary name.
$date = gmdate('Ymd_his');
$prefix = 'none';
- if (!empty($full)) {
+ if (!empty($full) && !empty($first['databases']['default']['default']['database'])) {
$first = current($full);
- if ( !empty($first['databases']['default']['default']['database']) ) {
- $prefix = count($full) > 1 ? 'multiple_sites' : str_replace('/', '-', $first['databases']['default']['default']['database']);
- }
+ $prefix = count($sites_subdirs) > 1 ? 'multiple_sites' : str_replace('/', '-', $first['databases']['default']['default']['database']);
}
$temp_dest_name = "$prefix.$date.tar";
drush_shell_cd_and_exec($workdir, "$tar --exclude \"{$docroot}/sites\" {$dereference}-cf %s %s", $destination, $docroot);
// Add sites/all to the same archive.
drush_shell_cd_and_exec($workdir, "$tar {$dereference}-rf %s %s", $destination, "{$docroot}/sites/all");
- // Add sites/sites.php to the same archive.
- if (file_exists("{$docroot}/sites/sites.php")) {
- drush_shell_cd_and_exec($workdir, "$tar {$dereference}-rf %s %s", $destination, "{$docroot}/sites/sites.php");
- }
$tmp = drush_tempdir();
$all_dbs = array();
);
$table_selection = drush_sql_get_table_selection();
list($dump_exec, $dump_file) = drush_sql_build_dump_command($table_selection, $db, $result_file);
- if (!drush_shell_exec($dump_exec)) {
- return drush_set_error('DRUSH_SQL_DUMP_FAIL', 'Database dump failed.');
- }
+ drush_shell_exec($dump_exec);
drush_shell_cd_and_exec($tmp, "$tar --dereference -rf %s %s", $destination, basename($result_file));
}
}
// Ensure that default/default.settings.php is in the archive. This is needed
// by site-install when restoring a site without any DB.
- // NOTE: Windows tar file replace operation is broken so we have to check if file already exists.
- // Otherwise it will corrupt the archive.
- $res = drush_shell_cd_and_exec($workdir, "$tar -tzf %s %s", $destination, $docroot . '/sites/default/default.settings.php');
- $output = drush_shell_exec_output();
- if (!$res || !isset($output[0]) || empty($output[0])) {
- drush_shell_cd_and_exec($workdir, "$tar --dereference -vrf %s %s", $destination, $docroot . '/sites/default/default.settings.php');
- }
+ drush_shell_cd_and_exec($workdir, "$tar --dereference -vrf %s %s", $destination, $docroot . '/sites/default/default.settings.php');
// Compress the archive
drush_shell_exec("gzip --no-name -f %s", $destination);
if ($type) {
drush_op($types[$type]);
- if ($type == 'all' && !drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
- $type = 'drush';
- }
+ drush_log(dt("'!name' cache was cleared", array('!name' => $type)), 'success');
}
else {
- // Don't offer 'all' unless Drush has bootstrapped the Drupal site
- if (!drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
- unset($types['all']);
- }
- $type = drush_choice($types, 'Enter a number to choose which cache to clear.', '!key');
- if ($type !== FALSE) {
- call_user_func($types[$type]);
- }
- }
- if ($type !== FALSE) {
- $site_label = '';
- if ($type != 'drush') {
- $self_name = drush_sitealias_bootstrapped_site_name();
- if (isset($self_name)) {
- $site_label = dt(' in !name', array('!name' => $self_name));
- }
+ $choice = drush_choice($types, 'Enter a number to choose which cache to clear.', '!key');
+ if ($choice !== FALSE) {
+ call_user_func($types[$choice]);
+ drush_log(dt("'!name' cache was cleared", array('!name' => $choice)), 'success');
}
- drush_log(dt("'!name' cache was cleared!insitename", array('!name' => $type, '!insitename' => $site_label)), 'success');
}
}
'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, // No bootstrap.
'options' => array(
'sort' => 'Sort commands in alphabetical order. drush waits for full bootstrap before printing any commands when this option is used.',
- 'filter' => array(
- 'description' => 'Restrict command list to those commands defined in the specified file. Omit value to choose from a list of names.',
- 'example-value' => 'category',
- 'value' => 'optional',
- ),
+ 'filter' => 'Restrict command list to those commands defined in the specified file.',
'html' => 'Print help for all commands in HTML format.',
'pipe' => 'A list of available commands, one per line.',
),
'drush core-requirements --pipe' => 'Print out a short report in the format "identifier: severity", where severity 2=error, 1=warning, and 0/-1=OK',
),
'options' => array(
- 'severity' => array(
- 'description' => 'Only show status report messages with a severity greater than or equal to the specified value.',
- 'value' => 'required',
- 'example-value' => '3',
- ),
+ 'severity' => 'Only show status report messages with a severity greater than or equal to the specified value.',
'ignore' => 'Comma-separated list of requirements to remove from output. Run with --pipe to see key values to use.',
),
);
'filename' => 'Optional. The file you wish to execute (without extension). If omitted, list files ending in .php in the current working directory and specified script-path. Some might not be real drush scripts. Beware.',
),
'options' => array(
- 'script-path' => array(
- 'description' => "Additional paths to search for scripts, separated by : (Unix-based systems) or ; (Windows).",
- 'example-value' => '~/scripts',
- ),
+ 'script-path' => "Additional paths to search for scripts, separated by : (Unix-based systems) or ; (Windows).",
),
'allow-additional-options' => TRUE,
'bootstrap' => DRUSH_BOOTSTRAP_MAX,
'required-arguments' => TRUE,
'allow-additional-options' => TRUE,
'handle-remote-commands' => TRUE,
- 'strict-option-handling' => TRUE,
+ 'mask-local-cli-options' => TRUE,
'examples' => array(
'drush core-execute git pull origin rebase' => 'Retrieve latest code from git',
),
'destination' => 'May be rsync path or site alias. See rsync documentation and example.aliases.drushrc.php.',
),
'options' => array(
- 'mode' => 'The unary flags to pass to rsync; --mode=rultz implies rsync -rultz. Default is -akz.',
+ 'mode' => 'The unary flags to pass to rsync; --mode=rultz implies rsync -rultz. Default is -az.',
'RSYNC-FLAG' => 'Most rsync flags passed to drush sync will be passed on to rsync. See rsync documentation.',
'exclude-conf' => 'Excludes settings.php from being rsynced. Default.',
'include-conf' => 'Allow settings.php to be rsynced. Default is to exclude settings.php.',
'exclude-paths' => 'List of paths to exclude, seperated by : (Unix-based systems) or ; (Windows).',
'include-paths' => 'List of paths to include, seperated by : (Unix-based systems) or ; (Windows).',
),
- 'strict-option-handling' => TRUE,
+ 'allow-additional-options' => TRUE,
'examples' => array(
'drush rsync @dev @stage' => 'Rsync Drupal root from dev to stage (one of which must be local).',
'drush rsync ./ @stage:%files/img' => 'Rsync all files in the current directory to the \'img\' directory in the file storage folder on stage.',
'key=value...' => 'any additional settings you wish to pass to the profile. Fully supported on D7+, partially supported on D6 (single step configure forms only). The key is in the form [form name].[parameter name] on D7 or just [parameter name] on D6.',
),
'options' => array(
- 'db-url' => array(
- 'description' => 'A Drupal 6 style database URL. Only required for initial install - not re-install.',
- 'example-value' => 'mysql://root:pass@127.0.0.1/db',
- ),
+ 'db-url' => 'A Drupal 6 style database URL. Only required for initial install - not re-install.',
'db-prefix' => 'An optional table prefix to use for initial install. Can be a key-value array of tables/prefixes in a drushrc file (not the command line).',
'db-su' => 'Account to use when creating a new database. Must have Grant permission (mysql only). Optional.',
'db-su-pw' => 'Password for the "db-su" account. Optional.',
'account-name' => 'uid1 name. Defaults to admin',
'account-pass' => 'uid1 pass. Defaults to a randomly generated password. If desired, set a fixed password in drushrc.php.',
'account-mail' => 'uid1 email. Defaults to admin@example.com',
- 'locale' => array(
- 'description' => 'A short language code. Sets the default site language. Language files must already be present. You may use download command to get them.',
- 'example-value' => 'en-GB',
- ),
+ 'locale' => 'A short language code. Sets the default site language. Language files must already be present. You may use download command to get them.',
'clean-url'=> 'Defaults to 1',
'site-name' => 'Defaults to Site-Install',
'site-mail' => 'From: for system mailings. Defaults to admin@example.com',
- 'sites-subdir' => array(
- 'description' => "Name of directory under 'sites' which should be created. Only needed when the subdirectory does not already exist. Defaults to 'default'",
- 'value' => 'required',
- 'example-value' => 'directory_name',
- ),
+ 'sites-subdir' => "Name of directory under 'sites' which should be created. Only needed when the subdirectory does not already exist. Defaults to 'default'",
),
'examples' => array(
'drush site-install expert --locale=uk' => '(Re)install using the expert install profile. Set default language to Ukranian.',
),
'examples' => array(
'drush qd' => 'Download and install stable release of Drupal into a timestamped directory, start server, and open the site logged in as admin.',
- 'drush qd --profile=minimal --dev --cache --core=drupal-8.x --yes' => 'Fire up dev release of Drupal site with minimal install profile.',
+ 'drush qd --profile=minimal --dev --cache --default-major=8 --yes' => 'Fire up dev release of Drupal site with minimal install profile.',
'drush qd testsite devel --server=:8081/admin --browser=firefox --cache --yes' => 'Fire up stable release (using the cache) of Drupal site called "testsite", download and enable devel module, start a server on port 8081 and open /admin in firefox.',
'drush qd commercesite --core=commerce_kickstart --profile=commerce_kickstart --cache --yes --watchdog' => 'Download and install the "Commerce Kickstart" distribution/install profile, display watchdog messages on the server console.',
),
$status_table['Drush alias files'] = dt("There are !count alias files. Run with --full to see the full list.", array('!count' => count($alias_files)));
}
}
-
+
// None of the Status keys are in dt(); this helps with machine-parsing of status?
$path_names['root'] = 'Drupal root';
$path_names['site'] = 'Site path';
$fake = drush_global_options_command(FALSE);
$global_option_rows = drush_format_help_section($fake, 'options');
drush_print_table($global_option_rows);
- drush_print();
- drush_print("See also: `drush topic docs-strict-options`");
}
function _drush_core_is_named_in_array($key, $the_array) {
);
$pm = pm_drush_command();
foreach ($pm['pm-download']['options'] as $option => $description) {
- if (is_array($description)) {
- $description = $description['description'];
- }
$options[$option] = 'Download option: ' . $description;
}
// Unset a few options that are not usable here, as we control them ourselves
unset($options['default-major']);
unset($options['use-site-dir']);
foreach ($items['site-install']['options'] as $option => $description) {
- if (is_array($description)) {
- $description = $description['description'];
- }
$options[$option] = 'Site install option: ' . $description;
}
unset($options['sites-subdir']);
// any keys are -not- replaced in _drush_core_directory.
if ($path && (strpos($path, '%') === FALSE)) {
drush_print($path);
- return $path;
}
else {
return drush_set_error('DRUSH_TARGET_NOT_FOUND', dt("Target '!target' not found.", array('!target' => $target)));
}
+
+ return TRUE;
}
/**
* that start with !.
*/
function drush_core_execute() {
- // Get all of the args and options that appear after the command name.
- $args = drush_get_original_cli_args_and_options();
+ // The DRUSH_COMMAND_ARGS contain all args and options that appear after the command name.
+ $args = drush_get_context('DRUSH_COMMAND_ARGS', array());
for ($x = 0; $x < sizeof($args); $x++) {
// escape all args except for command separators.
if (!in_array($args[$x], array('&&', '||', ';'))) {
'callback' => 'drush_print_file',
'callback arguments' => array($docs_dir . '/examples/sandwich.drush.inc'),
);
- $items['docs-example-sync-extension'] = array(
- 'description' => 'Example Drush commandfile that extends sql-sync to enable development modules in the post-sync hook.',
- 'hidden' => TRUE,
- 'topic' => TRUE,
- 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
- 'callback' => 'drush_print_file',
- 'callback arguments' => array($docs_dir . '/examples/sync_enable.drush.inc'),
- );
- $items['docs-example-sync-via-http'] = array(
- 'description' => 'Example Drush commandfile that extends sql-sync to allow transfer of the sql dump file via http rather than ssh and rsync.',
- 'hidden' => TRUE,
- 'topic' => TRUE,
- 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
- 'callback' => 'drush_print_file',
- 'callback arguments' => array($docs_dir . '/examples/sync_via_http.drush.inc'),
- );
$items['docs-policy'] = array(
'description' => 'Example policy file.',
'hidden' => TRUE,
'callback' => 'drush_print_file',
'callback arguments' => array($docs_dir . '/examples/policy.drush.inc'),
);
- $items['docs-strict-options'] = array(
- 'description' => 'Strict option handling, and how commands that use it differ from regular Drush commands.',
- 'hidden' => TRUE,
- 'topic' => TRUE,
- 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
- 'callback' => 'drush_print_file',
- 'callback arguments' => array($docs_dir . '/docs/strict-options.html'),
- );
return $items;
}
* @return
* An array containing module info for all available modules.
*/
-function drush_get_modules($include_hidden = TRUE) {
- $modules = system_rebuild_module_data();
- if (!$include_hidden) {
- foreach ($modules as $key => $module) {
- if (isset($module->info['hidden'])) {
- unset($modules[$key]);
- }
- }
- }
-
- return $modules;
+function drush_get_modules() {
+ return system_rebuild_module_data();
}
/**
}
/**
+ * Submit the system modules form.
+ *
+ * The modules should already be fully enabled/disabled before calling this
+ * function. Calling this function just makes sure any activities triggered by
+ * the form submit (such as admin_role) are completed.
+ */
+function drush_system_modules_form_submit($active_modules) {
+ module_load_include('inc', 'system', 'system.admin');
+ $form_state = array('values' => array('status' => $active_modules));
+ drupal_form_submit('system_modules', $form_state);
+ // Because normally system_modules_submit would call this function if modules
+ // had been changed, in this case we are submitting the module form without
+ // any changes, so we need to clear caches manually.
+ drupal_flush_all_caches();
+}
+
+/**
* Get complete information for all available themes.
*
* @return
* An array containing theme info for all available themes.
*/
-function drush_get_themes($include_hidden = TRUE) {
- $themes = system_rebuild_theme_data();
- if (!$include_hidden) {
- foreach ($themes as $key => $theme) {
- if (isset($theme->info['hidden'])) {
- unset($themes[$key]);
- }
- }
- }
-
- return $themes;
+function drush_get_themes() {
+ return system_rebuild_theme_data();
}
/**
* @return
* An array containing module info for all available modules.
*/
-function drush_get_modules($include_hidden = TRUE) {
+function drush_get_modules() {
$modules = module_rebuild_cache();
- foreach ($modules as $key => $module) {
+ foreach ($modules as $module) {
if (!isset($module->type)) {
$module->type = 'module';
}
- if ((!$include_hidden) && $module->info['hidden']) {
- unset($modules[$key]);
- }
}
return $modules;
_drupal_install_module($module);
}
module_enable($modules);
- $modules = drush_map_assoc(array_keys(drush_get_modules(FALSE)));
- drush_system_modules_form_submit($modules);
}
/**
*/
function drush_module_disable($modules) {
module_disable($modules, FALSE);
- $modules = drush_map_assoc(array_keys(drush_get_modules(FALSE)));
- drush_system_modules_form_submit($modules);
}
/**
*/
function drush_get_themes() {
$themes = system_theme_data();
- foreach ($themes as $key => $theme) {
+ foreach ($themes as $theme) {
if (!isset($theme->type)) {
$theme->type = 'theme';
}
- if ((!$include_hidden) && $theme->info['hidden']) {
- unset($themes[$key]);
- }
}
return $themes;
$profile = 'standard';
}
+ define('MAINTENANCE_MODE', 'install');
require_once DRUSH_DRUPAL_CORE . '/includes/install.core.inc';
$db_spec = _drush_sql_get_db_spec();
$output .= sprintf('$_COOKIE=unserialize("%s");', str_replace('"', '\"', $cookie));
}
else {
- $output .= 'function _cli_cookie_print(){print(serialize(array(session_name()=>session_id())));} register_shutdown_function("_cli_cookie_print");';
+ $output .= 'function _cli_cookie_print(){print(serialize(array(session_name()=>session_id())));}
+register_shutdown_function("_cli_cookie_print");';
}
return $output;
while ($result = db_fetch_object($query)) {
if (update_check_incompatibility($result->name, $result->type)) {
$incompatible[] = $result->name;
- drush_log(dt("!type !name is incompatible with this release of Drupal, and will be disabled.",
- array("!type" => $result->type, '!name' => $result->name)), "warning");
+ drush_log(dt("%type %name is incompatible with this release of Drupal, and will be disabled.",
+ array("%type" => $result->type, '%name' => $result->name)), "warning");
}
}
if (!empty($incompatible)) {
drupal_set_installed_schema_version($module, $number);
}
- $context['message'] = 'Performed update: ' . $function;
+ $context['message'] = 'Performing ' . $function;
}
/**
$confirm = FALSE;
}
else {
- if (!drush_confirm(dt('Do you want to delete the !field_name field?', array('!field_name' => $field_name)))) {
+ if (!drush_confirm(dt('Do you want to delete the %field_name field?', array('%field_name' => $field_name)))) {
return drush_user_abort();
}
}
function drush_field_clone($source_field_name, $target_field_name) {
if (!$info = field_info_field($source_field_name)) {
- return drush_set_error(dt('!source not found in field list.', array('!source' => $source_field_name)));
+ return drush_set_error(dt('%source not found in field list.', array('%source' => $source_field_name)));
}
unset($info['id']);
$help = array($command['description']);
}
- if ($command['strict-option-handling']) {
- $command['topics'][] = 'docs-strict-options';
- }
-
// Give commandfiles an opportunity to add examples and options to the command.
drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_SITE);
drush_command_invoke_all_ref('drush_help_alter', $command);
*/
function drush_help_section_formatter_options($command, &$help_attributes) {
if ($help_attributes['label'][0] == '-') {
- drush_log(dt("Option '!option' of command !command should instead be declared as '!fixed'", array('!option' => $help_attributes['label'], '!command' => $command['command'], '!fixed' => preg_replace('/^--*/', '', $help_attributes['label']))), 'debug');
+ drush_log(dt("Option '%option' of command %command should instead be declared as '%fixed'", array('%option' => $help_attributes['label'], '%command' => $command['command'], '%fixed' => preg_replace('/^--*/', '', $help_attributes['label']))), 'debug');
}
else {
$help_attributes['label'] = '--' . $help_attributes['label'];
if (array_key_exists('required', $help_attributes)) {
$help_attributes['description'] .= " " . dt("Required.");
}
-
- $prefix = '<';
- $suffix = '>';
if (array_key_exists('example-value', $help_attributes)) {
- if (isset($help_attributes['value']) && $help_attributes['value'] == 'optional') {
- $prefix = '[';
- $suffix = ']';
- }
- $help_attributes['label'] .= '=' . $prefix . $help_attributes['example-value'] . $suffix;
-
+ $help_attributes['label'] .= '=' . $help_attributes['example-value'];
if (array_key_exists('short-form', $help_attributes)) {
- $help_attributes['short-form'] .= " $prefix" . $help_attributes['example-value'] . $suffix;
+ $help_attributes['short-form'] .= ' ' . $help_attributes['example-value'];
}
}
if (array_key_exists('short-form', $help_attributes)) {
+++ /dev/null
-<?php
-
-/**
- * Implements hook_drush_help().
- */
-function queue_drush_help($section) {
- switch ($section) {
- case 'drush:queue-run':
- return dt('Run Drupal queue workers. As opposed to "drush cron" that can only be run one at a time on a single site, "drush queue-run" can be invoked as many times as the server load allows.');
- }
-}
-
-/**
- * Implements hook_drush_command().
- */
-function queue_drush_command() {
- $items['queue-run'] = array(
- 'description' => 'Run a specific queue by name',
- 'arguments' => array(
- 'queue_name' => 'The name of the queue to run, as defined in either hook_queue_info or hook_cron_queue_info.',
- ),
- 'required-arguments' => TRUE,
- );
- $items['queue-list'] = array(
- 'description' => 'Returns a list of all defined queues',
- 'options' => array(
- 'pipe' => 'Return a comma delimited list of queues.'
- )
- );
-
- return $items;
-}
-
-
-/**
- * Validation for Drupal 6 to ensure the drupal_queue module is enabled.
- *
- * @param $command
- * The command being validated.
- */
-function drush_queue_validate($command) {
- if (drush_drupal_major_version() == 6) {
- if (!module_exists('drupal_queue')) {
- $args = array('!command' => $command, '!dependencies' => 'drupal_queue');
- return drush_set_error('DRUSH_COMMAND_DEPENDENCY_ERROR', dt('Command !command needs the following modules installed/enabled to run: !dependencies.', $args));
- }
- else {
- drupal_queue_include();
- }
- }
-}
-
-/**
- * Validation callback for drush queue-run.
- */
-function drush_queue_run_validate($queue_name) {
- drush_queue_validate('queue-run');
-
- // Get all queues.
- $queues = drush_queue_get_queues();
- if (!isset($queues[$queue_name])) {
- return drush_set_error('DRUSH_QUEUE_ERROR', dt('Could not find the !name queue.', array('!name' => $queue_name)));
- }
-}
-
-/**
- * Validation callback for drush queue-list.
- */
-function drush_queue_list_validate() {
- drush_queue_validate('queue-list');
-}
-
-/**
- * Command callback for drush queue-run.
- *
- * Queue runner that is compatible with queues declared using both
- * hook_queue_info() and hook_cron_queue_info().
- *
- * @param $queue_name
- * Arbitrary string. The name of the queue to work with.
- */
-function drush_queue_run($queue_name) {
- // Get all queues.
- $queues = drush_queue_get_queues();
-
- // Try to increase the maximum execution time if it is too low.
- $max_execution_time = ini_get('max_execution_time');
- if ($max_execution_time > 0 && $max_execution_time < 240 && !ini_get('safe_mode')) {
- set_time_limit(240);
- }
-
- $info = $queues[$queue_name];
- $function = $info['cron']['callback'];
- $end = time() + (isset($info['cron']['time']) ? $info['cron']['time'] : 15);
- $queue = DrupalQueue::get($queue_name);
- while (time() < $end && ($item = $queue->claimItem())) {
- $function($item->data);
- $queue->deleteItem($item);
- }
-}
-
-/**
- * Command callback for drush queue-list.
- */
-function drush_queue_list() {
- $queues = drush_queue_get_queues();
- $default_class = variable_get('queue_default_class', 'SystemQueue');
- $rows = array(array('Queue', 'Items', 'Class'));
- foreach (array_keys($queues) as $name) {
- $q = DrupalQueue::get($name);
- $class = variable_get('queue_class_' . $name, $default_class);
- $rows[] = array($name, $q->numberOfItems(), $class);
- }
-
- if (drush_get_option('pipe')) {
- $pipe = array();
- array_shift($rows);
- foreach ($rows as $r) {
- $pipe[] = implode(",", $r);
- }
- drush_print_pipe($pipe);
- }
- else {
- drush_print_table($rows, TRUE);
- }
-
- // Return the result for backend invoke
- return $rows;
-}
-
-/**
- * Get queues defined with hook_cron_queue_info().
- *
- * @return
- * Array of queues indexed by name and containing queue object and number
- * of items.
- */
-function drush_queue_get_queues() {
- static $queues;
- if (!isset($queues)) {
- // Invoke hook_queue_info(), currently only defined by the queue_ui module.
- $queues = module_invoke_all('queue_info');
-
- // Merge in queues from modules that implement hook_cron_queue_info().
- $cron_queues = module_invoke_all('cron_queue_info');
- drupal_alter('cron_queue_info', $cron_queues);
- foreach($cron_queues as $name => $queue) {
- $queues[$name] = array(
- 'cron' => array(
- 'callback' => $queue['worker callback'],
- 'time' => $queue['time'],
- )
- );
- }
- }
- return $queues;
-}
* the scope of this one call).
*/
function drush_core_rsync($source, $destination, $additional_options = array()) {
- // Preflight source in case it defines aliases used by the destination
- _drush_sitealias_preflight_path($source);
- // After preflight, evaluate file paths. We evaluate destination paths first, because
- // there is a first-one-wins policy with --exclude-paths, and we want --target-command-specific
- // to take precidence over --source-command-specific.
- $destination_settings = drush_sitealias_evaluate_path($destination, $additional_options, FALSE, "rsync", 'target-');
- $source_settings = drush_sitealias_evaluate_path($source, $additional_options, FALSE, "rsync", 'source-');
+ // Preflight destination in case it defines aliases used by the source
+ _drush_sitealias_preflight_path($destination);
+ // After preflight, evaluate file paths
+ $source_settings = drush_sitealias_evaluate_path($source, $additional_options, FALSE, "rsync");
+ $destination_settings = drush_sitealias_evaluate_path($destination, $additional_options, FALSE, "rsync");
$source_path = $source_settings['evaluated-path'];
$destination_path = $destination_settings['evaluated-path'];
// destination contains a : or a /.
$include_settings_is_default = (strpos($source . $destination, ':') !== FALSE) || (strpos($source . $destination, '/') !== FALSE);
- $options = _drush_build_rsync_options($additional_options, $include_settings_is_default);
-
- // Get all of the args and options that appear after the command name.
- $original_args = drush_get_original_cli_args_and_options();
- foreach ($original_args as $original_option) {
- if ($original_option{0} == '-') {
- $options .= ' ' . $original_option;
- }
- }
-
// Go ahead and call rsync with the paths we determined
- return drush_core_exec_rsync($source_path, $destination_path, $options);
+ return drush_core_call_rsync($source_path, $destination_path, $additional_options, $include_settings_is_default);
}
}
* TRUE on success, FALSE on failure.
*/
function drush_core_call_rsync($source, $destination, $additional_options = array(), $include_settings_is_default = TRUE, $live_output = TRUE) {
- $options = _drush_build_rsync_options($additional_options, $include_settings_is_default);
- return drush_core_exec_rsync($source, $destination, $options, $additional_options, $live_output);
-}
-
-function drush_core_exec_rsync($source, $destination, $options, $additional_options = array(), $live_output = TRUE) {
- $ssh_options = drush_get_option_override($additional_options, 'ssh-options', '');
- $exec = "rsync -e 'ssh $ssh_options' $options $source $destination";
-
- if ($live_output) {
- $exec_result = drush_op_system($exec);
- $result = ($exec_result == 0);
- }
- else {
- $result = drush_shell_exec($exec);
- }
-
- if (!$result) {
- drush_set_error('DRUSH_RSYNC_FAILED', dt("Could not rsync from !source to !dest", array('!source' => $source, '!dest' => $destination)));
- }
-
- return $result;
-}
-
-function _drush_build_rsync_options($additional_options, $include_settings_is_default = TRUE) {
- $options = '';
// Exclude vcs reserved files.
+ $options = '';
if (!_drush_rsync_option_exists('include-vcs', $additional_options)) {
$vcs_files = drush_version_control_reserved_files();
foreach ($vcs_files as $file) {
$options .= ' --exclude="'.$file.'"';
}
- unset($additional_options['include-vcs']);
}
$mode = '-akz';
foreach (array('include', 'exclude') as $include_exclude) {
// Get the option --include-path or --exclude-path and explode to an array of paths
// that we will translate into an --include or --exclude option to pass to rsync
- $inc_ex_path = explode(PATH_SEPARATOR, drush_get_option($include_exclude . '-paths', ''));
+ $inc_ex_path = explode(PATH_SEPARATOR, drush_get_option(array($include_exclude . '-path', $include_exclude . '-paths'), ''));
foreach ($inc_ex_path as $one_path_to_inc_ex) {
if (!empty($one_path_to_inc_ex)) {
$options .= ' --' . $include_exclude . '="' . $one_path_to_inc_ex . '"';
}
}
- // Remove stuff inserted by evaluate path
- unset($additional_options[$include_exclude . '-paths']);
- unset($additional_options[$include_exclude . '-files-processed']);
}
// drush_core_rsync passes in $include_settings_is_default such that
// 'exclude-conf' is the default when syncing from one alias to
// is included.
if ($include_settings_is_default ? _drush_rsync_option_exists('exclude-conf', $additional_options) : !_drush_rsync_option_exists('include-conf', $additional_options)) {
$options .= ' --exclude="settings.php"';
- unset($additional_options['exclude-conf']);
}
if (_drush_rsync_option_exists('exclude-sites', $additional_options)) {
$options .= ' --include="sites/all" --exclude="sites/*"';
- unset($additional_options['exclude-sites']);
}
if (_drush_rsync_option_exists('mode', $additional_options)) {
$mode = "-" . drush_get_option_override($additional_options, 'mode');
- unset($additional_options['mode']);
}
if (drush_get_context('DRUSH_VERBOSE')) {
// the drush_op() will be verbose about the command that gets executed.
$mode .= 'v';
$options .= ' --stats --progress';
}
-
+ $rsync_available_options = array(
+ // unary options
+ 'archive', // -a
+ 'recursive', // -r
+ 'relative', // -R
+ 'backup', // -b
+ 'update', // -u
+ 'checksum', // -c
+ 'dirs', // -d
+ 'links', // -l
+ 'copy-links', // -L
+ 'copy-dirlinks', // -k
+ 'keep-dirlinks', // -K
+ 'hard-links', // -H
+ 'perms', // -p
+ 'executability', // -E
+ 'acls', // -A
+ 'xattrs', // -X
+ 'owner', // -o
+ 'group', // -g
+ 'times', // -t
+ 'omit-dir-times', // -O
+ 'sparse', // -S
+ 'dry-run', // -n
+ 'whole-file', // -W
+ 'one-file-system', // -x
+ 'prune-empty-dirs', // -m
+ 'ignore-times', // -I
+ 'fuzzy', // -y
+ 'cvs-exclude', // -C
+ 'compress', // -Z
+ 'protect-args', // -s
+ '8-bit-output', // -8
+ 'human-readable', // -h
+ 'itemize-changes', // -i
+ 'copy-unsafe-links',
+ 'safe-links',
+ 'no-implied-dirs',
+ 'inplace',
+ 'append',
+ 'append-verify',
+ 'existing',
+ 'remove-source-files',
+ 'delete',
+ 'delete-before',
+ 'delete-during',
+ 'delete-delay',
+ 'delete-after',
+ 'delete-excluded',
+ 'ignore-errors',
+ 'force',
+ 'ignore-existing',
+ 'partial',
+ 'delay-updates',
+ 'numeric-ids',
+ 'size-only',
+ 'blocking-io',
+ 'stats',
+ 'progress',
+ 'list-only',
+ // options with values
+ 'block-size',
+ 'backup-dir',
+ 'suffix',
+ 'chmod',
+ 'rsync-path',
+ 'modify-window',
+ 'compare-dest',
+ 'copy-dest',
+ 'link-dest',
+ 'skip-compress',
+ 'filter',
+ // filter out 'exclude'; consistency with --include. Use --exclude-paths.
+ 'exclude-from',
+ // filter out 'include'; conflicts with global drush --include option. Use --include-paths.
+ 'include-from',
+ 'files-from',
+ 'address',
+ 'port',
+ 'sockopts',
+ 'out-format',
+ 'bwlimit',
+ 'iconv',
+ 'checksum-seed',
+ 'max-delete',
+ 'max-size',
+ 'min-size',
+ 'partial-dir',
+ 'timeout',
+ 'temp-dir',
+ 'compress-level',
+ 'out-format',
+ 'protocol',
+ );
// Check if the user has set $options['rsync-version'] to enable rsync legacy version support.
// Drush was written for rsync 2.6.9 or later, so assume that version if nothing was explicitly set.
$rsync_version = drush_get_option(array('rsync-version','source-rsync-version','target-rsync-version'), '2.6.9');
- $options_to_exclude = array('ssh-options');
- foreach ($additional_options as $test_option => $value) {
+ foreach ($rsync_available_options as $test_option) {
+ $value = drush_get_option_override($additional_options, $test_option);
// Downgrade some options for older versions of rsync
if ($test_option == 'remove-source-files') {
if (version_compare($rsync_version, '2.6.4', '<')) {
$test_option = 'remove-sent-files';
}
}
- if ((isset($test_option)) && !in_array($test_option, $options_to_exclude) && (isset($value) && !is_array($value))) {
+ if ((isset($test_option)) && (isset($value) && !is_array($value))) {
if ($value === TRUE) {
$options .= " --$test_option";
}
}
}
- return $mode . $options;
+ $ssh_options = drush_get_option_override($additional_options, 'ssh-options', '');
+ $exec = "rsync -e 'ssh $ssh_options' $mode$options $source $destination";
+
+ if ($live_output) {
+ $exec_result = drush_op_system($exec);
+ $result = ($exec_result == 0);
+ }
+ else {
+ $result = drush_shell_exec($exec);
+ }
+
+ if (!$result) {
+ drush_set_error('DRUSH_RSYNC_FAILED', dt("Could not rsync from !source to !dest", array('!source' => $source, '!dest' => $destination)));
+ }
+
+ return $result;
}
function _drush_rsync_option_exists($option, $additional_options) {
* Command validate.
*/
function drush_core_site_install_validate() {
- if ($sites_subdir = drush_get_option('sites-subdir')) {
+ if ($sites_subdir = drush_get_option('sites-subdir', 'default')) {
$lower = strtolower($sites_subdir);
if ($sites_subdir != $lower) {
drush_log(dt('Only lowercase sites-subdir are valid. Switching to !lower.', array('!lower' => $lower)), 'warning');
drush_set_error(dt('Could not determine database connection parameters. Pass --db-url option.'));
return;
}
- $default_sites_subdir = drush_get_context('DRUSH_DRUPAL_SITE', 'default');
- $sites_subdir = drush_get_option('sites-subdir', $default_sites_subdir);
+ $sites_subdir = drush_get_option('sites-subdir', 'default');
$conf_path = "sites/$sites_subdir";
$files = "$conf_path/files";
// The Drupal 6 installer needs to bootstrap up to the specified site.
// We need to be at least at DRUSH_BOOTSTRAP_DRUPAL_SITE to select the site uri to install to
- define('MAINTENANCE_MODE', 'install');
if (drush_drupal_major_version() == 6) {
drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION);
}
),
'options' => drush_shell_exec_proc_build_options(),
'handle-remote-commands' => TRUE,
- 'strict-option-handling' => TRUE,
+ 'mask-local-cli-options' => TRUE,
'examples' => array(
'drush @mysite ssh' => 'Open an interactive shell on @mysite\'s server.',
'drush @prod ssh \'ls /tmp\'' => 'Run "ls /tmp" on @prod site. If @prod is a site list, then ls will be executed on each site.',
* Command callback.
*/
function drush_ssh_site_ssh($command = NULL) {
- // Get all of the args and options that appear after the command name.
- $args = drush_get_original_cli_args_and_options();
+ // The DRUSH_COMMAND_ARGS contain all args and options that appear after the command name.
+ $args = drush_get_context('DRUSH_COMMAND_ARGS', array());
// n.b. we do not escape the first (0th) arg to allow `drush ssh 'ls /path'`
// to work in addition to the preferred form of `drush ssh ls /path`.
// Supporting the legacy form means that we cannot give the full path to an
}
$command = implode(' ', $args);
if (!$alias = drush_get_context('DRUSH_TARGET_SITE_ALIAS')) {
- return drush_set_error('DRUSH_MISSING_TARGET_ALIAS', 'A remote site alias is required. The way you call ssh command has changed to `drush @alias ssh`.');
+ return drush_set_error('DRUSH_MISSING_TARGET_ALIAS', 'A remote site alias is required. The way call ssh command has changed to `drush @alias ssh`.');
}
$site = drush_sitealias_get_record($alias);
// We only accept remote aliases.
* Array of test classes and groups.
*/
function test_test_run_complete() {
- if (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_FULL) == DRUSH_BOOTSTRAP_DRUPAL_FULL && module_exists('simpletest')) {
+ if (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_FULL) && module_exists('simpletest')) {
// Retrieve all tests and groups.
list($groups, $all_tests) = drush_test_get_all_tests();
- return array('values' => array_keys($groups) + $all_tests);
}
+ return array('values' => array_keys($groups) + $all_tests);
}
// Command callback
// Run all tests.
foreach (array_keys($groups) as $group) {
foreach (array_keys($groups[$group]) as $class) {
- drush_invoke_process('@self', 'test-run', array($class));
+ drush_invoke_process('@none', 'test-run', array($class), array('--no-all'));
}
}
return;
* different endpoint.
*/
function usage_drush_exit() {
- // Ignore statistics for simulated commands. (n.b. in simulated mode, _drush_usage_mongolab will print rather than send statistics)
- if (!drush_get_context('DRUSH_SIMULATE')) {
- $file = _drush_usage_get_file();
- if (drush_get_option('drush_usage_log', FALSE)) {
- _drush_usage_log(drush_get_command(), $file);
- }
- if (drush_get_option('drush_usage_send', FALSE)) {
- _drush_usage_mongolab($file, drush_get_option('drush_usage_size', 51200));
- }
+ $file = _drush_usage_get_file();
+ if (drush_get_option('drush_usage_log', FALSE)) {
+ _drush_usage_log(drush_get_command(), $file);
+ }
+ if (drush_get_option('drush_usage_send', FALSE)) {
+ _drush_usage_mongolab($file, drush_get_option('drush_usage_size', 51200));
}
}
'drush vget user' => 'List all variables beginning with the string "user".',
),
'options' => array(
- 'format' => array(
- 'description' => 'Format to output the object. Use "print_r" for print_r (default), "export" for var_export, and "json" for JSON.',
- 'example-value' => 'export',
- ),
+ 'format' => 'Format to output the object. Use "print_r" for print_r (default), "export" for var_export, and "json" for JSON.',
'pipe' => 'A synonym for --format=export. Useful for pasting into code.',
),
'aliases' => array('vget'),
'options' => array(
'yes' => 'Skip confirmation if only one variable name matches.',
'always-set' => 'Always skip confirmation.',
- 'format' => array(
- 'description' => 'Format to parse the object. Use "auto" to detect format from value (default), "string", "integer" or "boolean" for corresponding primitive type, and "json" for JSON.',
- 'example-value' => 'boolean',
- ),
+ 'format' => 'Format to parse the object. Use "auto" to detect format from value (default), "string", "integer" or "boolean" for corresponding primitive type, and "json" for JSON.',
),
'examples' => array(
'drush vset --yes preprocess_css TRUE' => 'Set the preprocess_css variable to true. Skip confirmation if variable already exists.',
$items['watchdog-delete'] = array(
'description' => 'Delete watchdog messages.',
'drupal dependencies' => array('dblog'),
- 'options' => array(
+ 'arguments' => array(
'severity' => 'Delete messages of a given severity level.',
'type' => 'Delete messages of a given type.',
),
--- /dev/null
+<?php
+
+/**
+ * @file
+ * Drush support for entities.
+ */
+/**
+ * Define how a print out needs to be
+ */
+define('DRUSH_ENTITY_SEPARATED_SPACE', 'space-separated');
+
+/**
+ * Implementation of hook_drush_command().
+ */
+function entity_drush_command() {
+ // Some standard settings
+ $output_json = "Process the entity as JSON";
+ $output_format = "Define output format. Known formats are: json, print_r, properties, export";
+ $type = "Entity type";
+ $nids = "A list of space-separated entity IDs to print to stdout.";
+
+ $items = array();
+
+ /*
+ * Entity type commands
+ */
+ $items['entity-type-read'] = array(
+ 'description' => "List details of entity types",
+ 'arguments' => array(
+ 'type' => "Entity type to list. If omitted all types are listed.",
+ ),
+ 'options' => array(
+ 'format' => $output_format,
+ 'fields' => 'fields of type to list specific info.',
+ 'exclude-fields' => 'exclude fields from the list',
+ 'include-fieldapi' => 'include the bundle specific field definitions',
+ ),
+ 'examples' => array(
+ 'entity-type-read' => 'List available entity types',
+ 'entity-type-read node' => 'List node type information',
+ 'entity-type-read node --fields=bundles' => 'List bundles for node type',
+ "etr `drush etr` --fields='entity keys',fieldable" => 'List all entity keys and fieldable.',
+ 'entity-type-read node --fields=bundles/*/admin/path' => 'List admin path for all bundles',
+ ),
+ 'aliases' => array('etr'),
+ );
+
+ /*
+ * Entity commands
+ */
+ $items['entity-create'] = array(
+ 'description' => "Create an entity from a json object",
+ 'arguments' => array(
+ 'type' => "Entity type",
+ ),
+ 'options' => array(
+ 'json' => $output_json,
+ ),
+ 'examples' => array(
+ 'entity-read node 4 --json | drush entity-create node' => 'Copy node/4 to a new entity.',
+ ),
+ 'aliases' => array('ec'),
+ );
+
+ $items['entity-read'] = array(
+ 'description' => "Print entity contents",
+ 'arguments' => array(
+ 'type' => $type,
+ 'nids' => $nids,
+ ),
+ 'options' => array(
+ 'format' => $output_format,
+ 'fields' => 'fields of type to list specific info',
+ ),
+ 'examples' => array(
+ 'entity-read user 4' => 'Print the user object 4',
+ 'entity-read taxonomy_vocabulary 1' => 'Print the taxonomy_vocabulary object 1',
+ ),
+ 'aliases' => array('er'),
+ );
+
+ $items['entity-update'] = array(
+ 'description' => "Update an entity as json in the default editor",
+ 'arguments' => array(
+ 'type' => $type,
+ 'nids' => $nids,
+ ),
+ 'options' => array(
+ 'json' => $output_json,
+ 'fields' => 'fields of type to list specific info',
+ 'json-input' => 'Filename with json content, or - for STDIN'
+ ),
+ 'examples' => array(
+ 'entity-update node 4' => 'Update node 4 as json in the default editor',
+ ),
+ 'aliases' => array('eu'),
+ );
+
+ $items['entity-delete'] = array(
+ 'description' => 'Delete entities.',
+ 'arguments' => array(
+ 'type' => $type,
+ 'nids' => $nids,
+ ),
+ 'options' => array(
+ 'json' => $output_json,
+ ),
+ 'examples' => array(
+ 'entity-delete' => '.',
+ 'entity-delete node 64' => '.',
+ 'entity-delete node --type=story' => '.',
+ ),
+ 'aliases' => array('ed'),
+ );
+
+ return $items;
+}
+
+/**
+ * Implementation of hook_drush_help().
+ *
+ * @param
+ * A string with the help section (prepend with 'drush:')
+ *
+ * @return
+ * A string with the help text for your command.
+ */
+function entity_drush_help($section) {
+ // TODO: is this function still needed?
+ switch ($section) {
+ case 'drush:entity-show':
+ return dt("Print entity objects to stdout");
+ case 'drush:entity-create':
+ return dt("Create an entity from a json object");
+ case 'drush:entity-edit':
+ return dt("Edit an entity as json in the default editor");
+ case 'drush:entity-delete':
+ return dt('Delete entities.');
+ }
+}
+
+/**
+ * List details of entity types
+ *
+ * @param type $type
+ * @return type
+ */
+function drush_entity_type_read($entity_type = NULL) {
+ $types = func_get_args();
+ $entities_info = _drush_entity_get_info();
+
+ // List the available types
+ if (empty($entity_type)) {
+ $result = array_keys($entities_info);
+ drush_print_pipe(drush_format($result));
+ drush_print(drush_format($result));
+ return;
+ }
+
+ $fields = _drush_entity_get_fields();
+ $result = array();
+ if (count($types)) {
+ foreach ($types as $type) {
+ $info = $entities_info[$type];
+ $result_type = _drush_entity_filter_fields($info, $fields, _drush_entity_get_exclude_fields());
+ if (!empty($result_type)) {
+ $result[$type] = $result_type;
+ }
+ }
+ }
+
+ drush_print_pipe(drush_format($result));
+ drush_print(drush_format($result));
+ return;
+}
+
+/**
+ * Filter on the given paths
+ *
+ * Each path may contain forward slashes / to filter subtrees
+ *
+ * A path may contain a star * or ** to wildcard a path
+ *
+ * Example paths (ignore space after *)
+ * - schema_fields_sql
+ * - bundles/* /label
+ * - ** /display
+ *
+ * @param array $value
+ * @param array $paths
+ * List of paths to filter about.
+ * @return array
+ * The matching path
+ */
+function _drush_entity_filter_fields(array $value, array $paths, array $delete_path = array()) {
+ // Store object hashs to prevent object ref recursion
+ $object_hash_list = array();
+ $return = array();
+ if (empty($paths)) {
+ $return = $value;
+ }
+ // List of path already done
+ $visited = array();
+ while (count($paths) > 0) {
+ $path = array_shift($paths);
+ if (isset($visited[$path])) {
+ continue;
+ }
+ $visited[$path] = $path;
+ // Contains the resulting path
+ $head = array();
+ // The result is a linked list done with an key => array construct
+ $tail = &$head; // By ref
+ $sub_tree = split('/', $path);
+ $current_path = '';
+ $work_value = $value;
+ $failed_path = FALSE;
+ while (count($sub_tree) > 0) {
+ $p = array_shift($sub_tree);
+ if ($p == '*' || $p == '**') {
+ // Wildcards only usefull on arrays
+ if (is_array($work_value) || _drush_entity_visit_object(&$object_hash_list, $work_value)) {
+ // Say a/**/b was requested
+ // We need to search for a/b and a/$option/**/b
+ $new_tree = $sub_tree;
+ $new_path = $current_path . join('/', $new_tree);
+ if (!isset($visited[$new_path])) {
+ array_push($paths, $new_path);
+ }
+ // We cast to array only to generate paths
+ $p_options = array_keys((array)$work_value);
+ //$p = array_shift($p_options);
+ foreach ($p_options as $p_option) {
+ // Create
+ $new_tree = $sub_tree;
+ array_unshift($new_tree, $p_option);
+ $new_path = $current_path . join('/', $new_tree);
+ if (!isset($visited[$new_path])) {
+ array_push($paths, $new_path);
+ if ($p == '**') {
+ $new_tree = $sub_tree;
+ array_unshift($new_tree, '**');
+ array_unshift($new_tree, $p_option);
+ $new_path = $current_path . join('/', $new_tree);
+ if (!isset($visited[$new_path])) {
+ array_push($paths, $new_path);
+ }
+ }
+ }
+ }
+ }
+ else {
+ $failed_path = TRUE;
+ }
+ // We processed the wildcard paths so stop processing
+ break; // sub tree processing
+ }
+ // Remember current path
+ $current_path .= $p . '/';
+ // Consume key
+ $temp = (array) $work_value;
+ if (is_array($temp) && isset($temp[$p])) {
+ $work_value = $temp[$p];
+ if (is_object($work_value) || is_array($work_value)) {
+ if (count($sub_tree)) {
+ // We are not done yet
+ $tail[$p] = array();
+ }
+ else {
+ // Stuff value: object or array
+ $tail[$p] = $work_value;
+ }
+ }
+ else {
+ $tail[$p] = $work_value;
+ }
+ // Follow the build tree
+ $tail = &$tail[$p]; // By ref
+ }
+ else {
+ $failed_path = TRUE;
+ break; // while
+ }
+ }
+ if (!$failed_path && count($sub_tree) == 0) {
+ $return = array_merge_recursive($return, $head);
+ }
+ }
+ if ($delete_path) {
+ $delete = _drush_entity_filter_fields($return, $delete_path);
+ // TODO substract $delete from $return .. next command deletes more
+ // try etr --include-fieldapi ... the are deleted too
+ // $return = array_diff($return, $delete);
+ }
+ return $return;
+}
+
+/**
+ * List the entity IDs of the given type
+ *
+ * @param type $entity_type
+ */
+function _drush_entity_read_list($entity_type) {
+ $entity_info = _drush_entity_get_info($entity_type);
+
+ if (drush_drupal_major_version() < 7) {
+ if (isset($entity_info['load list sql'])) {
+ $sql = $entity_info['load list sql'];
+ $entities = array();
+ $result = db_query($sql);
+ while ($row = db_fetch_array($result)) {
+ $id = $row['id'];
+ $entities[$entity_type][$id] = $id;
+ }
+ }
+ }
+ else {
+ $query = new EntityFieldQuery();
+ $entities = $query->entityCondition('entity_type', $entity_type)->execute();
+ }
+
+ if (count($entities)) {
+ $result = array_keys($entities[$entity_type]);
+ $format = DRUSH_ENTITY_SEPARATED_SPACE;
+ $header = "Available ids for $entity_type";
+ }
+ else {
+ $format = DRUSH_ENTITY_SEPARATED_SPACE;
+ $header = "No ids for $entity_type";
+ $result = array();
+ }
+ _drush_entity_print($result, $format, $header);
+}
+
+/**
+ * Show entities by given type and id
+ *
+ * @param string $type
+ * Given entity type
+ */
+function drush_entity_read($entity_type = NULL) {
+ if (!isset($entity_type)) {
+ drush_die("You must specify an entity_type");
+ }
+ $ids = func_get_args();
+ $entity_type = array_shift($ids);
+ _drush_entity_check_type($entity_type);
+
+ // Do listing
+ if (count($ids) == 0) {
+ _drush_entity_read_list($entity_type);
+ return;
+ }
+
+ // Do content
+ $entities = _drush_entity_load($entity_type, $ids);
+
+ $fields = _drush_entity_get_fields();
+ if ($fields) {
+ $entities = _drush_entity_select_fields($entities, $fields, TRUE);
+ }
+
+ if (drush_get_option('json', FALSE) && count($entities) == 1) {
+ // We only shift when requesting json and just one entity
+ $entities = array_shift($entities);
+ }
+
+ drush_print_pipe(drush_format($entities));
+ drush_print(drush_format($entities));
+}
+
+/**
+ * Extract the selected fields from a number of entities.
+ *
+ * @param type $entities Array of entities
+ * @param type $fields Array of fileds to be returned for each entity
+ * @return type
+ */
+function _drush_entity_select_fields($entities, $fields, $allow_path = FALSE) {
+ if (!$allow_path) {
+ foreach ($fields as $path) {
+ if (strpos($path, '/') !== FALSE) {
+ drush_die("No path supported yet : $path");
+ }
+ if (strpos($path, '*') !== FALSE) {
+ drush_die("No wildcard supported yet : $path");
+ }
+ }
+ }
+
+ $result = array();
+ foreach ($entities as $eid => $entity) {
+ $result[$eid] = _drush_entity_filter_fields((array) $entity, $fields);
+ }
+ return $result;
+}
+
+/**
+ * Create an entity by the given type.
+ *
+ * @param $entity_type
+ * @param $arg
+ */
+function drush_entity_create($entity_type, $arg = NULL) {
+ if (!isset($entity_type)) {
+ drush_die("You must specify a entity_type");
+ }
+
+// if (!drush_get_option('json')) {
+// drush_die("You must specify --json");
+// }
+
+ _drush_entity_check_type($entity_type);
+
+ $entity_info = _drush_entity_get_info($entity_type);
+
+ if (empty($arg)) {
+ $entity = new stdClass();
+ if (isset($entity_info['drush']['defaults'])) {
+ foreach ($entity_info['drush']['defaults'] as $key => $value) {
+ $entity->$key = $value;
+ }
+ }
+ $entity_json = _drush_entity_edit_string(drush_json_encode($entity));
+ }
+ else if ($arg === '-' || file_exists($arg)) {
+ if ($arg === '-') {
+ $entity_json = stream_get_contents(STDIN);
+ }
+ else {
+ drush_log("Reading file $arg");
+ $entity_json = file_get_contents($arg);
+ }
+ }
+ else {
+ drush_die("Improper input/args");
+ }
+
+ if (!empty($entity_json)) {
+ $entity = (object) drush_json_decode($entity_json);
+
+ $eid = $entity_info['drush']['new'][0];
+ if (_drush_entity_save($entity_type, $entity, TRUE)) {
+ _drush_entity_print('', DRUSH_ENTITY_SEPARATED_SPACE, 'Entity created.');
+ }
+ else if (isset($entity->$eid)) {
+ _drush_entity_print(array($entity->$eid), DRUSH_ENTITY_SEPARATED_SPACE, 'Entity created.');
+ }
+ else {
+ drush_set_error('DRUSH_ERROR', dt("Failed to create entity!"));
+ }
+ }
+ else if ($arg === '-') {
+ drush_set_error('DRUSH_NO_STDIN', dt("stdin empty!"));
+ }
+ else {
+ drush_set_error('DRUSH_EMPTY_FILE', dt("Empty file!"));
+ }
+}
+
+/**
+ * Edit a string with the default shell editor.
+ *
+ * @param type $string
+ * @return type
+ */
+function _drush_entity_edit_string($string) {
+ $editor = getenv('EDITOR');
+ if (empty($editor)) {
+ drush_set_error('DRUSH_NO_EDITOR', dt('The environment variable EDITOR is not set'));
+ return $string;
+ }
+ else {
+ $file = drush_save_data_to_temp_file($string);
+ drush_shell_exec_interactive('$EDITOR ' . $file);
+ return file_get_contents($file, "r");
+ }
+}
+
+/**
+ * Convert the given fields into an array
+ *
+ * @return Array
+ */
+function _drush_entity_get_fields() {
+ $fields = drush_get_option('fields', '');
+ if ($fields) {
+ return split(',', $fields);
+ }
+ return array();
+}
+
+function _drush_entity_get_exclude_fields() {
+ $fields = drush_get_option('exclude-fields', '');
+ // We always include 'drush' to exclude fields
+ if (!$fields) {
+ $fields = 'drush';
+ }
+ else {
+ $fields .= ',drush';
+ }
+ $exclude_fields = split(',', $fields);
+ return array_diff($exclude_fields, _drush_entity_get_fields());
+}
+
+/**
+ * Update given entity.
+ */
+function drush_entity_update($entity_type, $id) {
+ $fields = _drush_entity_get_fields();
+ $entities = _drush_entity_load($entity_type, array($id));
+
+ if (isset($entities[$id])) {
+ $entity = $entities[$id];
+ $input = drush_get_option('json-input', NULL);
+ if ($input === '-') {
+ $edited_obj_json = stream_get_contents(STDIN);
+ }
+ elseif (file_exists($input)) {
+ drush_log("Reading file $input");
+ $edited_obj_json = file_get_contents($input);
+ }
+ else {
+ // Prepare to EDIT a json string
+ if ($fields) {
+ $entities_fields = _drush_entity_select_fields($entities, $fields, FALSE);
+ }
+
+ if ($fields) {
+ $edit_obj = $entities_fields[$id];
+ }
+ else {
+ $edit_obj = $entity;
+ }
+ $edited_obj_json = _drush_entity_edit_string(drush_json_encode($edit_obj));
+ }
+
+ // Prepare the changed entity to be saved
+ if ($fields) {
+ // Remap fields onto entity
+ $edited_entity = $entity;
+ $edited_fields = (object) drush_json_decode($edited_obj_json);
+ foreach ($fields as $field) {
+ $edited_entity->$field = $edited_fields->$field;
+ }
+ }
+ else {
+ $edited_entity = (object) drush_json_decode($edited_obj_json);
+ }
+
+ // Save
+ _drush_entity_save($entity_type, $edited_entity);
+ }
+ else {
+ drush_set_error('DRUSH_ERROR', dt("Entity to update not found!"));
+ }
+}
+
+/**
+ * Load entities with the given type and IDs
+ *
+ * @param type $entity_type
+ * @param type $ids
+ * @return array with entity objects
+ */
+function _drush_entity_load($entity_type, $ids) {
+ $entities = array();
+ switch (drush_drupal_major_version()) {
+ case 5:
+ case 6:
+ foreach ($ids as $id) {
+ $entity = _drush_entity_op('load', $entity_type, $id);
+ $entities[$id] = (object) $entity;
+ }
+ break;
+ case 7:
+ $entities = entity_load($entity_type, $ids);
+ break;
+ }
+ return $entities;
+}
+
+/**
+ * Command callback.
+ */
+function drush_entity_delete($entity_type, $ids = NULL) {
+ if (!isset($entity_type)) {
+ drush_die("You must specify an entity type");
+ }
+ $info = _drush_entity_get_info($entity_type);
+ if (!isset($info)) {
+ drush_die("Type '$entity_type' does not exist.");
+ }
+ $ids = func_get_args();
+ $entity_type = array_shift($ids);
+
+ if (!is_null($ids)) {
+ $result = _drush_entity_delete($entity_type, $ids);
+ if (count($result) > 0) {
+ _drush_entity_print($result);
+ }
+ }
+ else {
+ drush_log("NOT YET IMPLEMENTED ... Delete ALL entities of type $entity_type", "ERROR");
+ //$nids = drush_get_nodes_by_type($entity_type);
+ //_drush_entity_delete($entity_type, $nids);
+ }
+}
+
+/**
+ * Save the given entity as a given type.
+ *
+ *
+ * @param $entity_type
+ * The type of the entity.
+ * @param $entity
+ * The entity to save.
+ * @return
+ * Depending on implementation and drupal version
+ *
+ * @see entity_type_supports()
+ */
+function _drush_entity_save($entity_type, &$entity, $new = FALSE) {
+ if ($new) {
+ $entity_info = _drush_entity_get_info($entity_type);
+ if (isset($entity_info['drush']['new'])) {
+ foreach ($entity_info['drush']['new'] as $field) {
+ unset($entity->$field);
+ }
+ }
+ }
+
+ return _drush_entity_op('save', $entity_type, $entity);
+}
+
+/**
+ * Try running CRUD op on the given id or entity.
+ *
+ * This mimics from D7 Entity API entity.module but for all CRUD ops
+ *
+ * @param type $op
+ * Operation to perform. Supported ops are: save, delete, load
+ * @param type $entity_type
+ * @param type $entity_or_id
+ * TODO: why by ref?
+ * @return type
+ */
+function _drush_entity_op($op, $entity_type, &$entity_or_id) {
+ // We need to fix for D7 when $op == delete
+ $op_alias = $op;
+ if ($op == 'delete') {
+ // D7 operator rename ..
+ // TODO: is this changed ?
+ $op_alias = 'deletion';
+ // D6 user_delete requires two arguments
+ if (drush_drupal_major_version() < 7 && $entity_type == 'user') {
+ user_delete(array(), $entity_or_id);
+ return;
+ }
+ }
+ $info = _drush_entity_get_info($entity_type);
+
+ // TODO: add comment
+ if (isset($info['drush'][$op . ' needs'])) {
+ switch ($info['drush'][$op . ' needs']) {
+ case 'array':
+ $entity_or_id = (array) $entity_or_id;
+ break;
+ case 'entity':
+ $old = $entity_or_id;
+ $entity_or_id = reset(_drush_entity_load($entity_type, array($entity_or_id)));
+ if (empty($entity_or_id)) {
+ drush_log("Unable to run $op on $entity_type : $old", 'warning');
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (method_exists($entity_or_id, $op)) {
+ return $entity_or_id->$op();
+ }
+ elseif (isset($info[$op_alias . ' callback'])) {
+ return $info[$op . ' callback']($entity_or_id);
+ }
+ elseif (isset($info['controller class']) && in_array('EntityAPIControllerInterface', class_implements($info['controller class']))) {
+ return entity_get_controller($entity_type)->$op($entity_or_id);
+ }
+ elseif (function_exists($entity_type . '_' . $op)) {
+ $op_function = $entity_type . '_' . $op;
+ // Fix user_save
+ if ($op == 'save' && isset($info['drush']['save needs keys'])) {
+ $keys = (array) $entity_or_id;
+ return $op_function($entity_or_id, $keys);
+ }
+ else {
+ return $op_function($entity_or_id);
+ }
+ }
+ else {
+ drush_log("Unable to $op the entity $entity_or_id. Maybe you could try to install and enable the entity.module");
+ return FALSE;
+ }
+}
+
+/**
+ * Try to run _delete_multiple or otherwise iterate.
+ *
+ * @param type $entity_type
+ * @param type $ids
+ * @return type the delete results per type implementation
+ */
+function _drush_entity_delete($entity_type, $ids = array()) {
+ if ($entity_type == 'node') {
+ // We want to use _delete_multiple
+ $result = _drush_entity_delete_node($ids);
+ }
+ else {
+ $result = array();
+ foreach ($ids as $id) {
+ $result["$id"] = _drush_entity_op('delete', $entity_type, $id);
+ }
+ }
+ return $result;
+}
+
+/**
+ * This is special as we have node_delete_multiple
+ *
+ * The code for D6 is also special as cache_clear_all is only invoked in the end
+ *
+ * @param array $nids
+ * //TODO : better doc please
+ * @return the drupal behaviour for the delete function used.
+ */
+function _drush_entity_delete_node(array $nids) {
+ if (drush_drupal_major_version() >= 7) {
+ return node_delete_multiple($nids);
+ }
+ else {
+ // Drupal 5/6
+ // node-delete is implemented this way to prevent calling cache_clear_all() for each node.
+ foreach ($nids as $nid) {
+ $node = node_load($nid, NULL, TRUE);
+ db_query('DELETE FROM {node} WHERE nid = %d', $nid);
+ db_query('DELETE FROM {node_revisions} WHERE nid = %d', $nid);
+ node_invoke($node, 'delete');
+ node_invoke_nodeapi($node, 'delete');
+ if (function_exists('search_wipe')) {
+ search_wipe($node->nid, 'node');
+ }
+ }
+ cache_clear_all();
+ }
+}
+
+/**
+ * Wrapper for entity_info()
+ *
+ * We wrap entity_info to make D5/6 compatible
+ */
+function _drush_entity_get_info($entity_type = NULL) {
+ static $entities_info;
+
+ if (!isset($entities_info)) {
+ if (drush_drupal_major_version() < 7) {
+ /*
+ * Mapping for D6/5 which mimic D7 info structure
+ */
+ $entities_info = array(
+ 'node' => array(
+ 'entity keys' => array(
+ 'id' => 'nid',
+ 'revision' => 'vid',
+ 'label' => 'title',
+ ),
+ 'drush' => array(
+ 'defaults' => array(
+ 'type' => '',
+ 'title' => '',
+ ),
+ // What fields to zap for a new entity
+ 'new' => array('nid', 'vid'),
+ ),
+ 'load list sql' => 'select nid id from {node}',
+ ),
+ 'user' => array(
+ 'entity keys' => array(
+ 'id' => 'nid',
+ 'label' => 'name',
+ ),
+ 'drush' => array(
+ 'defaults' => array(
+ 'name' => '',
+ ),
+ // What fields to zap for a new entity
+ 'new' => array('uid'),
+ // user_save needs list of keys
+ 'save needs keys' => TRUE,
+ ),
+ 'load list sql' => 'select uid id from {users}',
+ ),
+ 'taxonomy_vocabulary' => array(
+ 'entity keys' => array(
+ 'id' => 'nid',
+ 'label' => 'name',
+ ),
+ 'drush' => array(
+ 'defaults' => array(
+ 'name' => '',
+ ),
+ // What fields to zap for a new entity
+ 'new' => array('vid'),
+ ),
+ 'load list sql' => 'select vid id from {vocabulary}',
+ ),
+ 'taxonomy_term' => array(
+ 'entity keys' => array(
+ 'id' => 'nid',
+ 'label' => 'name',
+ ),
+ 'drush' => array(
+ 'defaults' => array(
+ 'name' => '',
+ ),
+ // What fields to zap for a new entity
+ 'new' => array('tid'),
+ 'save needs' => 'array',
+ ),
+ 'load list sql' => 'select tid id from {term_data}',
+ 'load callback' => 'taxonomy_get_term',
+ 'save callback' => 'taxonomy_save_term',
+ ),
+ );
+ }
+ else {
+ $entities_info = entity_get_info();
+ foreach ($entities_info as $key => $info) {
+ $entities_info[$key]['drush'] = array(
+ 'defaults' => array(
+ ),
+ );
+ if (drush_get_option('include-fieldapi', FALSE)) {
+ // Add field related values
+ foreach ($entities_info[$key]['bundles'] as $bundle => $dummy) {
+ // Merge in the fields
+ $field_info_instances = field_info_instances($key, $bundle);
+ if ($field_info_instances) {
+ $entities_info[$key]['field_info_instances'][$bundle] = $field_info_instances;
+ }
+ // Merge in the pseudo fields
+ foreach (array('form', 'display') as $context) {
+ $field_info_extra_fields = field_info_extra_fields($entity_type, $bundle, $context);
+ if ($field_info_extra_fields) {
+ $entities_info[$key]['field_info_extra_fields'][$bundle][$context] = $field_info_extra_fields;
+ }
+ }
+ }
+ }
+ }
+ // What keys to delete on create
+ $entities_info['node']['drush']['new'] = array('nid', 'vid');
+ $entities_info['user']['drush']['new'] = array('uid');
+ $entities_info['file']['drush']['new'] = array('fid');
+
+ // Defaults
+ $entities_info['node']['drush']['defaults']['type'] = '';
+ $entities_info['node']['drush']['defaults']['title'] = '';
+ $entities_info['node']['drush']['defaults']['language'] = 'und';
+ $entities_info['node']['drush']['defaults']['body'] = array(
+ LANGUAGE_NONE => array(
+ 0 => array(
+ 'value' => "",
+ 'format' => filter_default_format(),
+ ),
+ ),
+ );
+ // To delete a file we need the file object
+ $entities_info['file']['drush']['delete needs'] = 'entity';
+ }
+ }
+ if (isset($entity_type)) {
+ return $entities_info[$entity_type];
+ }
+ return $entities_info;
+}
+
+/**
+ * Print the given object depending on json switch
+ *
+ * @param $object
+ */
+function _drush_entity_print($object, $format = NULL, $header = '') {
+ if (drush_get_option('json')) {
+ // php-5.4.0 : JSON_PRETTY_PRINT
+ drush_print(drush_json_encode($object));
+ }
+ else {
+ if ($header) {
+ drush_print($header);
+ }
+ if ($format == DRUSH_ENTITY_SEPARATED_SPACE) {
+ drush_print(join(" ", (array) $object));
+ }
+ else {
+ //$object = drush_json_decode(drush_json_encode($object));
+ drush_print_r($object);
+ }
+ }
+}
+
+/**
+ * Prevent D5/6 from invoking wrong types
+ *
+ * @param type $type
+ */
+function _drush_entity_check_type($type) {
+ switch (drush_drupal_major_version()) {
+ case 5:
+ case 6:
+ $entity_info = _drush_entity_get_info();
+ if (!isset($entity_info[$type])) {
+ drush_die("No support for $type for drupal core < 7.x");
+ }
+ }
+}
; To include the most recent views module:
-projects[] = views
+projects[] = views
; This will, by default, retrieve the latest recommended version of the project
; using its update XML feed on Drupal.org. If any of those defaults are not
; If you want to retrieve a specific version of a project:
-; projects[views] = 2.16
+projects[views] = 2.16
; Or an alternative, extended syntax:
**Project using options (see below):**
- projects[drupal][version] = 7.12
+ projects[drupal][version] = 6.15
Do not use both types of declarations for a single project in your makefile.
### Project download options
Use an alternative download method instead of retrieval through update XML.
-
- If no download type is specified, make defaults the type to
- `git`. Additionally, if no url is specified, make defaults to use
- Drupal.org.
-
The following methods are available:
- `download[type] = file`
`md5`, `sha1`, `sha256`, or `sha512` - one or more checksums for the file. Optional.
- `request_type` - the request type - get or post. post depends on
- http://drupal.org/project/make_post. Optional.
+ `request_type` - the request type - get or post. Defaults to get. Optional.
`data` - The post data to be submitted with the request. Should be a
- valid URL query string. Requires http://drupal.org/project/make_post. Optional.
+ valid URL query string. Required.
`filename` - What to name the file, if it's not an archive. Optional.
- `subtree` - if the download is an archive, only this subtree within the
- archive will be copied to the target destination. Optional.
-
- `download[type] = bzr`
Use a bazaar repository as the source for this project. Options:
projects[mytheme][download][type] = "git"
projects[mytheme][download][url] = "git://github.com/jane_doe/mytheme.git"
- Shorthand is available to pull a specific revision from a git
- repository:
-
- projects[context_admin][revision] = "eb9f05e"
-
- is the same as:
-
- projects[context_admin][download][revision] = "eb9f05e"
-
- `refspec` - the git reference to fetch and checkout. Optional.
-
- If this is set, it will have priority over tag, revision and branch options.
-
- `download[type] = svn`
Use an SVN repository as the source for this project. Options:
Drush make also comes with testing capabilities, designed to test drush make
itself. Writing a new test is extremely simple. The process is as follows:
-1. Figure out what you want to test. Write a makefile that will test
- this out. You can refer to existing test makefiles for
- examples. These are located in `DRUSH/tests/makefiles`.
+1. Figure out what you want to test. Write a makefile that will test this out.
+ You can refer to existing test makefiles for examples.
2. Drush make your makefile, and use the --md5 option. You may also use other
options, but be sure to take note of which ones for step 4.
3. Verify that the result you got was in fact what you expected. If so,
continue. If not, tweak it and re-run step 2 until it's what you expected.
4. Using the md5 hash that was spit out from step 2, make a new entry in the
- tests clase (DRUSH/tests/makeTest.php), following the example below.
+ tests array in drush_make.test.inc, following the example below.
'machine-readable-name' => array(
'name' => 'Human readable name',
'makefile' => 'tests/yourtest.make',
),
'options' => array('any' => TRUE, 'other' => TRUE, 'options' => TRUE),
),
-5. Test! Run drush test suite (see DRUSH/tests/README.txt). To just
- run the make tests:
-
- `phpunit --filter=makeMake .`
-
+5. Test! Run drush make-test machine-readable-name to see if the test passes.
You can check for any messages you want in the message array, but the most
basic tests would just check the build hash.
it is fully functional.
Maintainers
------------
-- Jonathan Hedstrom (jhedstrom)
+----------
+- Jonathan Hedstrom
- The rest of the Drush maintainers
Original Author
----------------
+----------
Dmitri Gaskin (dmitrig01)
<?php
-/**
- * @file
- * Functions for the generate makefile command.
- */
include_once 'includes/install.inc';
include_once drupal_get_path('module', 'system') . '/system.install';
$all_extensions = drush_get_extensions();
$projects = _drush_make_generate_projects($all_extensions, $version_options);
$contents = _drush_make_generate_makefile_contents($projects);
-
+
if (!$file) {
drush_print($contents);
}
drush_log(dt("Wrote .make file @file", array('@file' => $file)), 'ok');
}
else {
- drush_make_error('FILE_ERROR', dt("Unable to write .make file !file", array('!file' => $file)));
+ drush_make_error('FILE_ERROR', dt("Unable to write .make file %file", array('%file' => $file)));
}
}
/**
- * Create the $version_options array from the --include-versions and
- * --exclude-versions command line options.
+ * Create the $version_options array from the --include-versions and --exclude-versions
+ * command line options.
*/
function _drush_make_generate_get_version_options() {
// What projects should we pin the versions for?
- // Check the command-line options for details.
- foreach (array("include", "exclude") as $option) {
+ // Check the command-line options for details
+ foreach(array("include", "exclude") as $option) {
$version_options[$option] = drush_get_option("$option-versions");
if ($version_options[$option] !== TRUE) {
$version_options[$option] = array_filter(explode(",", $version_options[$option]));
$projects = array();
$system_requirements = system_requirements('runtime');
- // Update xml expects the drupal version to be expressed as "7.x" or "8.x"
+ // update xml expects the drupal version to be expressed as "7.x" or "8.x"
// We used to check $system_requirements['drupal']['value'], but this now
// contains values such as "7.10-dev".
$drupal_major_version = drush_drupal_major_version() . '.x';
- $core_project = strtolower($system_requirements['drupal']['title']);
+ $core_project = strtolower($system_requirements['drupal']['title']);
$projects[$core_project] = array('_type' => 'core');
if ($core_project != 'drupal') {
$projects[$core_project]['custom_download'] = TRUE;
$projects[$core_project]['type'] = 'core';
}
else {
- // Drupal core - we can determine the version if required.
+ // Drupal core - we can determine the version if required
if (_drush_generate_track_version("drupal", $version_options)) {
$projects[$core_project]["version"] = $drupal_major_version;
}
// Non-default profiles section.
$install_profile = variable_get('install_profile', '');
if (!in_array($install_profile, array('default', 'standard', 'minimal')) && $install_profile != '') {
- $projects[$install_profile]['type']
- = $projects[$install_profile]['_type'] = 'profile';
+ $projects[$install_profile]['type'] =
+ $projects[$install_profile]['_type'] = 'profile';
$request = array(
'name' => $install_profile,
'drupal_version' => $drupal_major_version,
$projects[$name]['custom_download'] = TRUE;
}
// Add 'subdir' if the project is installed in a non-default location.
- if (isset($project['path'])) {
- $projects[$name] += _drush_generate_makefile_check_path($project);
- }
+ $projects[$name] += _drush_generate_makefile_check_path($project);
// Add version number if this project's version is to be tracked.
if (_drush_generate_track_version($name, $version_options) && $project["version"]) {
- $projects[$name]['version'] = preg_replace("/^" . DRUPAL_CORE_COMPATIBILITY . "-/", "", $project["version"]);
+ $projects[$name]['version'] = preg_replace("/^". DRUPAL_CORE_COMPATIBILITY . "-/", "", $project["version"]);
}
foreach ($project['extensions'] as $extension_name) {
_drush_make_generate_add_patch_files($projects[$name], dirname($all_extensions[$extension_name]->filename));
$patchfile = DRUPAL_ROOT . '/' . $location . '/PATCHES.txt';
if (is_file($patchfile)) {
foreach (file($patchfile) as $line) {
- if (substr($line, 0, 2) == '- ') {
- $project['patches'][] = trim(substr($line, 2));
+ if (substr($line,0,2) == '- ') {
+ $project['patches'][] = trim(substr($line,2));
}
}
}
$output = drush_shell_exec_output();
if (!empty($output)) {
$git_dir = $output[0];
- // Find the actual base of the git repository.
+ // Find the actual base of the git repository
$repo_root = $git_dir == ".git" ? $location : dirname($git_dir);
// If the repository root is at the drupal root or some parent
// of the drupal root, or some other location that could not
$project['download][type'] = 'git';
$project['download][url'] = $url;
- // Fill in the branch as well.
+ // Fill in the branch as well
drush_shell_cd_and_exec($repo_root, 'git branch');
$output = drush_shell_exec_output();
foreach ($output as $line) {
}
}
- // Put in the commit hash.
+ // Put in the commit hash
drush_shell_cd_and_exec($repo_root, 'git log');
$output = drush_shell_exec_output();
if (substr($output[0], 0, 7) == "commit ") {
}
}
- // Add patch files, if any.
+ // Add patch files, if any
_drush_make_generate_add_patch_files($project, $repo_root);
}
}
}
}
}
- // If we could not figure out where the extension came from, then give up and
- // flag it as a "custom" download.
+ // If we could not figure out where the extension came from,
+ // then give up and flag it as a "custom" download
if (!isset($project['download][type'])) {
$project['custom_download'] = TRUE;
}
if ((strlen(DRUPAL_ROOT) >= strlen($repo_root)) || (dirname($repo_root) == DRUPAL_ROOT)) {
return NULL;
}
- // Also exclude sites/* and sites/*/{modules,themes} and profile/* and
- // profile/*/{modules,themes}.
- return $project_name;
+ // also exclude sites/* and sites/*/{modules,themes} and
+ // profile/* and profile/*/{modules,themes}
+ return $project_name;
}
/**
- * Helper function to determine if a given project is to have its version
- * tracked.
+ * Helper function to determine if a given project is to have its version tracked
*/
function _drush_generate_track_version($project, $version_options) {
// A. If --exclude-versions has been specified:
- // A.a. if it's a boolean, check the --include-versions option.
+ // A.a. if it's a boolean, check the --include-versions option
if ($version_options["exclude"] === TRUE) {
- // A.a.1 if --include-versions has been specified, ensure it's an array.
+ // A.a.1 if --include-versions has been specified, ensure it's an array
+ //
if (is_array($version_options["include"])) {
return in_array($project, $version_options["include"]);
}
- // A.a.2 If no include array, then we're excluding versions for ALL
- // projects.
+ // A.a.2 If no include array, then we're excluding versions for ALL projects
return FALSE;
}
- // A.b. if --exclude-versions is an array with items, check this project is in
- // it: if so, then return FALSE.
+ // A.b. if --exclude-versions is an array with items, check this project is in it: if so,
+ // then return FALSE.
elseif (is_array($version_options["exclude"]) && count($version_options["exclude"])) {
return !in_array($project, $version_options["exclude"]);
}
// B. If by now no --exclude-versions, but --include-versions is an array,
- // examine it for this project.
+ // examine it for this project
if (is_array($version_options["include"]) && count($version_options["include"])) {
return in_array($project, $version_options["include"]);
}
- // If none of the above conditions match, include version number by default.
+ // If none of the above conditions match, include version number by default
return TRUE;
}
function _drush_generate_makefile_check_path($project) {
$info = array();
$type = $project['type'];
- $path = dirname($project['path']);
- // Check to see if the path is in a subdir sites/all/modules or
- // profiles/profilename/modules
+ $path = dirname(_pm_find_common_path($type, $project['extensions']));
+ // Check to see if the path is in a subdir sites/all/modules or profiles/profilename/modules
if (preg_match('@^sites/[a-zA-Z0-9_]*/' . $type . 's/..*@', $path) || preg_match('@^sites/[a-zA-Z0-9_]*/' . $type . 's/..*@', $path)) {
$subdir = preg_replace(array('@^[a-zA-Z0-9_]*/[a-zA-Z0-9_]*/' . $type . 's/*@', "@/$name" . '$@'), '', $path);
if (!empty($subdir)) {
return $info;
}
-/**
- * Generate the actual contents of the .make file.
- */
function _drush_make_generate_makefile_contents($projects) {
$output = array();
$custom = FALSE;
<?php
-/**
- * @file
- * Download-specific functions for Drush Make.
- */
/**
* Downloads the given package to the destination directory.
*
- * @return mixed
+ * @return
* The destination path on success, FALSE on failure.
*/
function make_download_factory($name, $download, $download_location) {
* Download project using drush's pm-download command.
*/
function make_download_pm($name, $download, $download_location) {
- $full_project_version = $name . '-' . $download['full_version'];
+ $version = $name . '-' . $download['version'];
$options = array(
'destination' => dirname($download_location),
'yes' => TRUE,
'package-handler' => 'wget',
- 'source' => $download['status url'],
- // This is only relevant for profiles, but we always want the variant to
- // be 'profile-only' so we don't end up with extra copies of core.
- 'variant' => 'profile-only',
'cache' => TRUE,
);
if ($name == 'drupal') {
$backend_options = array();
if (!drush_get_option(array('verbose', 'debug'), FALSE)) {
- $backend_options['integrate'] = TRUE;
+ $backend_options['integrate'] = FALSE;
$backend_options['log'] = FALSE;
}
// Perform actual download with `drush pm-download`.
- $return = drush_invoke_process('@none', 'pm-download', array($full_project_version), $options, $backend_options);
+ $return = drush_invoke_process('@none', 'pm-download', array($version), $options, $backend_options);
if (empty($return['error_log'])) {
- // @todo Report the URL we used for download. See
- // http://drupal.org/node/1452672.
- drush_log(dt('@project downloaded.', array('@project' => $full_project_version)), 'ok');
+ drush_log(dt('@project downloaded from @url', array('@project' => $version, '@url' => $download['download_link'])), 'ok');
}
}
/**
* Downloads a file to the specified location.
*
- * @return mixed
+ * @return
* The destination directory on success, FALSE on failure.
*/
-function make_download_file($name, $download, $download_location, $cache_duration = DRUSH_CACHE_LIFETIME_DEFAULT) {
- if ($filename = _make_download_file($download['url'], $cache_duration)) {
+function make_download_file($name, $download, $download_location) {
+ if ($filename = _make_download_file($download)) {
if (!drush_get_option('ignore-checksums') && !_make_verify_checksums($download, $filename)) {
return FALSE;
}
drush_log(dt('@project downloaded from @url.', array('@project' => $name, '@url' => $download['url'])), 'ok');
- $download_filename = isset($download['filename']) ? $download['filename'] : '';
- $subtree = isset($download['subtree']) ? $download['subtree'] : NULL;
- return make_download_file_unpack($filename, $download_location, $download_filename, $subtree);
+ return make_download_file_unpack($filename, $download_location, (isset($download['filename']) ? $download['filename'] : ''));
}
make_error('DOWNLOAD_ERROR', dt('Unable to download @project from @url.', array('@project' => $name, '@url' => $download['url'])));
return FALSE;
}
-/**
- * Wrapper to drush_download_file().
- *
- * @param string $download
- * The url of the file to download.
- * @param int $cache_duration
- * The time in seconds to cache the resultant download.
- *
- * @return string
- * The location of the downloaded file, or FALSE on failure.
- */
-function _make_download_file($download, $cache_duration = DRUSH_CACHE_LIFETIME_DEFAULT) {
- if (drush_get_option('no-cache', FALSE)) {
- $cache_duration = 0;
+function _make_download_file($download) {
+ // TODO cleanup so $download is always a string, since with
+ // drush_download_file(), that's all we pass in.
+ if (is_string($download)) {
+ $download = array('url' => $download);
}
- $tmp_path = make_tmp();
- // Ensure that we aren't including the querystring when generating a filename
- // to save our download to.
- $file = basename(reset(explode('?', $download, 2)));
- return drush_download_file($download, $tmp_path . '/' . $file, $cache_duration);
-}
-
-/**
- * Determines the MIME content type of the specified file. The power of this
- * function depends on whether the PHP installation has either
- * mime_content_type() or finfo installed -- if not, only tar, gz, and zip types
- * can be detected, and other file types will return 'unknown'.
- *
- * @return mixed
- * The MIME content type of the file, 'unknown', or FALSE on error.
- */
-function drush_mime_content_type($filename) {
- // Tar files have no magic value, so we can detect them only by filename.
- if (strrpos($filename, '.tar') === (strlen($filename) - 4)) {
- return 'application/x-tar';
+ // Default to use cache.
+ $cache = drush_get_option('cache');
+ if (!drush_get_option('no-cache', FALSE)) {
+ drush_set_option('cache', TRUE);
}
+ drush_set_option('cache', $cache);
- $content_type = '';
-
- // mime_content_type() and the finfo class are not available everywhere.
- if (function_exists('mime_content_type')) {
- $content_type = mime_content_type($filename);
- }
- elseif (class_exists('finfo')) {
- $finfo = new finfo(FILEINFO_MIME);
- // We only want the first part, before the ;
- $type = explode(';', $finfo->file($filename));
- $content_type = $type[0];
- }
-
- // If we found what we're looking for, we're done.
- if (!empty($content_type) && ($content_type == 'application/x-gzip' || $content_type == 'application/x-zip')) {
- return $content_type;
- }
-
- // If PHP's built-ins aren't present or PHP is configured in such a way that
- // all these files are considered octet-stream (e.g with mod_mime_magic and
- // an http conf that's serving all archives as octet-stream for other
- // reasons) we'll detect (a few select) mime types on our own by examing the
- // file's magic header bytes.
- if ($file = fopen($filename, 'rb')) {
- $first = fread($file, 2);
- fclose($file);
-
- if ($first === FALSE) {
- return FALSE;
- }
-
- // Interpret the two bytes as a little endian 16-bit unsigned int.
- $data = unpack('v', $first);
- switch ($data[1]) {
- case 0x8b1f:
- // First two bytes of gzip files are 0x1f, 0x8b.
- // See http://www.gzip.org/zlib/rfc-gzip.html#header-trailer.
- return 'application/x-gzip';
-
- case 0x4b50:
- // First two bytes of zip files are 0x50, 0x4b ('PK').
- // See http://en.wikipedia.org/wiki/Zip_(file_format)#File_headers.
- return 'application/zip';
-
- default:
- // Not a file type we can detect. If PHP's built-ins gave us
- // something, we can at least return that.
- return !empty($content_type) ? $content_type : 'unknown';
- }
- }
- return FALSE;
+ $tmp_path = make_tmp();
+ $file = basename($download['url']);
+ return drush_download_file($download['url'], $tmp_path . '/' . $file);
}
/**
* Unpacks a file to the specified download location.
*
- * @return mixed
+ * @return
* The download location on success, FALSE on failure.
*/
-function make_download_file_unpack($filename, $download_location, $name, $subtree = NULL) {
+function make_download_file_unpack($filename, $download_location, $name) {
+ $extension = array_pop(explode('.', $filename));
$success = FALSE;
-
- switch (drush_mime_content_type($filename)) {
- case 'application/x-gzip':
- // I'd like to just use tar -z, but apparently it breaks on windoze. Why
- // do they always have to ruin everything?
- $success = make_download_file_unpack_gzip($filename, $download_location, $subtree);
+ switch ($extension) {
+ case 'gz':
+ case 'tgz':
+ // I'd like to just use tar -z, but apparently it breaks on windoze. Why do they always have to ruin everything?
+ $success = make_download_file_unpack_gzip($filename, $download_location);
break;
-
- case 'application/x-tar':
- $success = make_download_file_unpack_tar($filename, $download_location, $subtree);
+ case 'tar':
+ $success = make_download_file_unpack_tar($filename, $download_location);
break;
-
- case 'application/zip':
- $success = make_download_file_unpack_zip($filename, $download_location, $subtree);
+ case 'zip':
+ $success = make_download_file_unpack_zip($filename, $download_location);
break;
-
default:
- // If this is an individual file, and no filename has been specified,
- // assume the original name.
- if (is_file($filename) && !$name) {
- $name = basename($filename);
- }
-
- // The destination directory has already been created by
- // findDownloadLocation().
- $destination = $download_location . ($name ? '/' . $name : '');
- $success = drush_move_dir($filename, $destination, TRUE);
+ $success = drush_shell_exec('mv %s %s', $filename, $download_location . ($name ? '/' . $name : ''));
}
return $success ? $download_location : FALSE;
}
/**
* Unpacks a tar file to the specified location.
*
- * @return boolean
+ * @return
* TRUE or FALSE depending on whether the operation was successful.
*/
-function make_download_file_unpack_tar($filename, $download_location, $subtree = NULL) {
- $tmp_path = drush_tempdir();
+function make_download_file_unpack_tar($filename, $download_location) {
+ $tmp_path = make_tmp();
list($main_directory) = array_reverse(explode('/', $download_location));
- drush_shell_exec('%s -x -C %s -f %s', drush_get_tar_executable(), $tmp_path, $filename);
+ drush_mkdir($tmp_path . '/__unzip__');
+ drush_shell_exec('tar -x -C %s -f %s', $tmp_path . '/__unzip__', $filename);
- return _make_download_file_move($tmp_path, $filename, $download_location, $subtree);
+ return _make_download_file_move($tmp_path, $filename, $download_location);
}
/**
* Unpacks a gzip file to the specified location.
*
- * @return boolean
+ * @return
* TRUE or FALSE depending on whether the operation was successful.
*/
-function make_download_file_unpack_gzip($filename, $download_location, $subtree = NULL) {
- // Find out where contents will end up. Retrieve last column of output using
- // awk.
+function make_download_file_unpack_gzip($filename, $download_location) {
+ // Find out where contents will end up. Retrieve last column of output using awk.
drush_shell_exec("gzip --list %s", $filename);
$info = drush_shell_exec_output();
if ($info) {
if (isset($file)) {
// Unzip it and then delete the tar file.
drush_shell_exec('gzip -d %s', $filename);
- return make_download_file_unpack_tar($file, $download_location, $subtree);
+ return make_download_file_unpack_tar($file, $download_location);
}
}
}
-/**
- * Unpack a zip file.
- */
-function make_download_file_unpack_zip($filename, $download_location, $subtree = NULL) {
- $tmp_path = drush_tempdir();
+function make_download_file_unpack_zip($filename, $download_location) {
+ $tmp_path = make_tmp();
list($main_directory) = array_reverse(explode('/', $download_location));
- $result = drush_shell_exec("unzip %s -d %s", $filename, $tmp_path);
-
- if (!$result) {
- make_error('UNZIP_ERROR', dt('Unable to unzip @filename.', array('@filename' => $filename)));
- return FALSE;
- }
+ drush_mkdir($tmp_path . '/__unzip__');
+ drush_shell_exec("unzip %s -d %s", $filename, $tmp_path . '/__unzip__');
- return _make_download_file_move($tmp_path, $filename, $download_location, $subtree);
+ return _make_download_file_move($tmp_path, $filename, $download_location);
}
-/**
- * Move a downloaded and unpacked file or directory into place.
- *
- * TODO merge with core drush methods.
- */
-function _make_download_file_move($tmp_path, $filename, $download_location, $subtree = NULL) {
- $lines = drush_scan_directory($tmp_path, '/./', array('.', '..'), 0, FALSE, 'filename', 0, TRUE);
+function _make_download_file_move($tmp_path, $filename, $download_location) {
+ drush_shell_exec('ls %s', $tmp_path . '/__unzip__');
+ $lines = drush_shell_exec_output();
$main_directory = basename($download_location);
if (count($lines) == 1) {
$directory = array_shift($lines);
- if ($directory->basename != $main_directory) {
- drush_move_dir($directory->filename, $tmp_path . DIRECTORY_SEPARATOR . $main_directory, TRUE);
+ if ($directory != $main_directory) {
+ drush_shell_exec('mv %s %s', $tmp_path . '/__unzip__/' . $directory, $tmp_path . '/__unzip__/' . $main_directory);
}
- drush_copy_dir($tmp_path . DIRECTORY_SEPARATOR . $main_directory . DIRECTORY_SEPARATOR . $subtree, $download_location, TRUE);
- drush_delete_dir($tmp_path, TRUE);
+ drush_shell_exec('cp -Rf %s %s', $tmp_path . '/__unzip__/' . $main_directory, dirname($download_location));
+ drush_shell_exec('rm -rf %s', $tmp_path . '/__unzip__');
}
elseif (count($lines) > 1) {
- drush_delete_dir($download_location, TRUE);
- drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . $subtree, $download_location, TRUE);
+ drush_shell_exec('rm -rf %s', $download_location);
+ drush_shell_exec('mv %s %s', $tmp_path . '/__unzip__', $download_location);
}
// Remove the tarball.
if (file_exists($filename)) {
- drush_delete_dir($filename, TRUE);
+ drush_shell_exec('rm %s', $filename);
}
- if (file_exists($tmp_path)) {
- drush_delete_dir($tmp_path, TRUE);
+ if (file_exists($tmp_path . '/__unzip__')) {
+ drush_shell_exec('rm -rf %s', $tmp_path . '/__unzip__');
}
return TRUE;
}
-/**
- * For backwards compatibility.
- */
+// Backwards compatibility.
function make_download_get($name, $download, $download_location) {
return make_download_file($name, $download, $download_location);
}
/**
* Checks out a git repository to the specified download location.
*
- * Allowed parameters in $download, in order of precedence:
- * - 'tag'
- * - 'revision'
- * - 'branch'
- *
- * This will also attempt to write out release information to the
- * .info file if the 'no-gitinfofile' option is FALSE. If
- * $download['full_version'] is present, this will be used, otherwise,
- * version will be set in this order of precedence:
- * - 'tag'
- * - 'branch'
- * - 'revision'
- *
- * @return mixed
+ * @return
* The download location on success, FALSE otherwise.
*/
function make_download_git($name, $download, $download_location) {
$tmp_path = make_tmp();
$wc = drush_get_option('working-copy');
+
+ // check if branch option is set in info file, otherwise set default to master branch
+ $download['branch'] = isset($download['branch']) ? $download['branch'] : 'master';
+ // check if tag option is set in info file, otherwise we set it to false
+ $download['tag'] = isset($download['tag']) ? $download['tag'] : FALSE;
+ // check if specific revision is set in info file
+ $download['revision'] = isset($download['revision']) ? $download['revision'] : FALSE;
+
// If no download URL specified, assume anonymous clone from git.drupal.org.
- $download['url'] = isset($download['url']) ? $download['url'] : "http://git.drupal.org/project/$name.git";
+ $download['url'] = isset($download['url']) ? $download['url'] : "git://git.drupal.org/project/$name.git";
// If no working-copy download URL specified, assume it is the same.
$download['wc_url'] = isset($download['wc_url']) ? $download['wc_url'] : $download['url'];
- // If not a working copy, and if --no-cache has not been explicitly
- // declared, create a new git reference cache of the remote repository,
- // or update the existing cache to fetch recent changes.
- // @see package_handler_download_project()
- $cache = !$wc && !drush_get_option('no-cache', FALSE);
- if ($cache && ($git_cache = drush_directory_cache('git'))) {
- $project_cache = $git_cache . '/' . $name . '-' . md5($download['url']);
- // Set up a new cache, if it doesn't exist.
- if (!file_exists($project_cache)) {
- $command = 'git clone --mirror';
- if (drush_get_context('DRUSH_VERBOSE')) {
- $command .= ' --verbose --progress';
- }
- $command .= ' %s %s';
- drush_shell_cd_and_exec($git_cache, $command, $download['url'], $project_cache);
- }
- else {
- // Update the --mirror clone.
- drush_shell_cd_and_exec($project_cache, 'git remote update');
- }
- $git_cache = $project_cache;
- }
-
// Use working-copy download URL if --working-copy specified.
$url = $wc ? $download['wc_url'] : $download['url'];
$tmp_location = drush_tempdir() . '/' . basename($download_location);
- $command = 'git clone %s %s';
- if (drush_get_context('DRUSH_VERBOSE')) {
- $command .= ' --verbose --progress';
- }
- if ($cache) {
- $command .= ' --reference ' . drush_escapeshellarg($git_cache);
- }
-
- // Before we can checkout anything, we need to clone the repository.
- if (!drush_shell_exec($command, $url, $tmp_location)) {
- make_error('DOWNLOAD_ERROR', dt('Unable to clone @project from @url.', array('@project' => $name, '@url' => $url)));
- return FALSE;
- }
-
- drush_log(dt('@project cloned from @url.', array('@project' => $name, '@url' => $url)), 'ok');
+ // clone the given repository
+ if (drush_shell_exec("git clone %s %s", $url, $tmp_location)) {
+ drush_log(dt('@project cloned from @url.', array('@project' => $name, '@url' => $url)), 'ok');
- // Get the current directory (so we can move back later).
- $cwd = getcwd();
- // Change into the working copy of the cloned repo.
- chdir($tmp_location);
+ // GIT Checkout only work on a ready cloned repo. So we switch to branch or to tag (only if we have no branch) after cloneing.
+ if ($download['branch'] !== 'master' || $download['tag'] || $download['revision'] || !empty($download['submodule'])) {
- // We want to use the most specific target possible, so first try a refspec.
- if (!empty($download['refspec'])) {
- if (drush_shell_exec("git fetch %s %s", $url, $download['refspec'])) {
- drush_log(dt("Fetched refspec !refspec.", array('!refspec' => $download['refspec'])), 'ok');
+ // get current directory (for move back later)
+ $cwd = getcwd();
+ // change path to working copy of cloned repo
+ chdir($tmp_location);
- if (drush_shell_exec("git checkout FETCH_HEAD")) {
- drush_log(dt("Checked out FETCH_HEAD."), 'info');
+ // Progress branch / tag / revision download. Ensure that only one option ist set (branch OR tag OR revision)
+ // check if branch a other than master
+ if ($download['branch'] !== 'master' && !$download['tag'] && !$download['revision']) {
+ if (drush_shell_exec("git checkout %s", $download['branch'])) {
+ drush_log(dt("Checked out branch @branch.", array('@branch' => $download['branch'])), 'ok');
+ }
+ elseif (drush_shell_exec("git checkout -b %s %s", $download['branch'], 'origin/' . $download['branch'])) {
+ drush_log(dt("Checked out branch @branch.", array('@branch' => $download['branch'])), 'ok');
+ }
+ else {
+ make_error('DOWNLOAD_ERROR', dt("Unable to check out branch @branch.", array('%branch' => $download['branch'])));
+ }
}
- }
- else {
- make_error('DOWNLOAD_ERROR', dt("Unable to fetch the refspec @refspec from @project.", array('@refspec' => $download['refspec'], '@project' => $name)));
- }
- }
-
- // If there wasn't a refspec, try a tag.
- elseif (!empty($download['tag'])) {
- // @TODO: change checkout to refs path.
- if (drush_shell_exec("git checkout %s", 'refs/tags/' . $download['tag'])) {
- drush_log(dt("Checked out tag @tag.", array('@tag' => $download['tag'])), 'ok');
- }
- else {
- make_error('DOWNLOAD_ERROR', dt("Unable to check out tag @tag.", array('@tag' => $download['tag'])));
- }
- }
-
- // If there wasn't a tag, try a specific revision hash.
- elseif (!empty($download['revision'])) {
- if (drush_shell_exec("git checkout %s", $download['revision'])) {
- drush_log(dt("Checked out revision @revision.", array('@revision' => $download['revision'])), 'ok');
- }
- else {
- make_error('DOWNLOAD_ERROR', dt("Unable to checkout revision @revision", array('@revision' => $download['revision'])));
- }
- }
-
- // If not, see if we at least have a branch.
- elseif (!empty($download['branch'])) {
- if (drush_shell_exec("git checkout %s", $download['branch'])) {
- drush_log(dt("Checked out branch @branch.", array('@branch' => $download['branch'])), 'ok');
- }
- elseif (drush_shell_exec("git checkout -b %s %s", $download['branch'], 'origin/' . $download['branch'])) {
- drush_log(dt('Checked out branch origin/@branch.', array('@branch' => $download['branch'])), 'ok');
- }
- else {
- make_error('DOWNLOAD_ERROR', dt('Unable to check out branch @branch.', array('@branch' => $download['branch'])));
- }
- }
-
- if (!empty($download['submodule'])) {
- $command = 'git submodule update';
- foreach ($download['submodule'] as $option) {
- $command .= ' --%s';
- }
- if (call_user_func_array('drush_shell_exec', array_merge(array($command), $download['submodule']))) {
- drush_log(dt('Initialized registered submodules.'), 'ok');
- }
- else {
- make_error('DOWNLOAD_ERROR', dt('Unable to initialize submodules.'));
- }
- }
-
- // Move back to last current directory (first line).
- chdir($cwd);
-
- // Handle .info file re-writing (if so desired).
- if (!drush_get_option('no-gitinfofile', FALSE)) {
- // Figure out the proper version string to use based on the .make file.
- // Best case is the .make file author told us directly.
- if (!empty($download['full_version'])) {
- $full_version = $download['full_version'];
- }
- // Next best is if we have a tag, since those are identical to versions.
- elseif (!empty($download['tag'])) {
- $full_version = $download['tag'];
- }
- // If we have a branch, append '-dev'.
- elseif (!empty($download['branch'])) {
- $full_version = $download['branch'] . '-dev';
- }
- // Ugh. Not sure what else we can do in this case.
- elseif (!empty($download['revision'])) {
- $full_version = $download['revision'];
- }
- // Probably can never reach this case.
- else {
- $full_version = 'unknown';
+ // progress if: tag is set but not the others
+ elseif ($download['branch'] == 'master' && $download['tag'] && !$download['revision']) {
+ // @TODO: change checkout to refs path
+ if (drush_shell_exec("git checkout %s", 'refs/tags/' . $download['tag'])) {
+ drush_log(dt("Checked out tag @tag.", array('@tag' => $download['tag'])), 'ok');
+ }
+ else {
+ make_error('DOWNLOAD_ERROR', dt("Unable to check out tag @tag.", array('@tag' => $download['tag'])));
+ }
+ }
+ // progress if: revision is set but not the others
+ elseif ($download['branch'] == 'master' && !$download['tag'] && $download['revision']) {
+ if (drush_shell_exec("git checkout %s", $download['revision'])) {
+ drush_log(dt("Checked out revision @revision.", array('@revision' => $download['revision'])), 'ok');
+ }
+ else {
+ make_error('DOWNLOAD_ERROR', dt("Unable to checkout revision @revision", array('@revision' => $download['revision'])));
+ }
+ }
+ // more than one option is set so we throw a error message
+ elseif ($download['branch'] !== 'master' || $download['tag'] || $download['revision']) {
+ make_error('DOWNLOAD_ERROR', dt("You can only specific branch or tag or revision but not combined in make file."));
+ return false;
+ }
+ if (!empty($download['submodule'])) {
+ $command = 'git submodule update';
+ foreach ($download['submodule'] as $option) {
+ $command .= ' --%s';
+ }
+ if (call_user_func_array('drush_shell_exec', array_merge(array($command), $download['submodule']))) {
+ drush_log(dt('Initialized registered submodules.'), 'ok');
+ }
+ else {
+ make_error('DOWNLOAD_ERROR', dt('Unable to initialize submodules.'));
+ }
+ }
+ // move back to last current directory (first line)
+ chdir($cwd);
}
- // If the version string ends in '.x-dev' do the Git magic to figure out
- // the appropriate 'rebuild version' string, e.g. '7.x-1.2+7-dev'.
- $matches = array();
- if (preg_match('/^(.+).x-dev$/', $full_version, $matches)) {
- require_once dirname(__FILE__) . '/../pm/package_handler/git_drupalorg.inc';
- $full_version = drush_pm_git_drupalorg_compute_rebuild_version($tmp_location, $matches[1]);
+ // Remove .git/ directory if working-copy flag was not specified.
+ if (!$wc && file_exists($tmp_location . '/.git')) {
+ drush_shell_exec("rm -rf %s", $tmp_location . '/.git');
}
- require_once dirname(__FILE__) . '/../pm/pm.drush.inc';
- drush_pm_inject_info_file_metadata($tmp_location, $name, $full_version);
+ drush_shell_exec('cp -Rf %s %s', $tmp_location, dirname($download_location));
+ return dirname($tmp_location);
}
-
- // Remove .git/ directory if working-copy flag was not specified.
- if (!$wc && file_exists($tmp_location . '/.git')) {
- drush_delete_dir($tmp_location . '/.git', TRUE);
+ else {
+ make_error('DOWNLOAD_ERROR', dt('Unable to clone @project from @url.', array('@project' => $name, '@url' => $url)));
}
- // Move the directory into the final resting location.
- drush_copy_dir($tmp_location, $download_location, TRUE);
-
- return dirname($tmp_location);
+ return FALSE;
}
/**
* Checks out a Bazaar repository to the specified download location.
*
- * @return mixed
+ * @return
* The download location on success, FALSE otherwise.
*/
function make_download_bzr($name, $download, $download_location) {
array_unshift($args, $command);
if (call_user_func_array('drush_shell_exec', $args)) {
drush_log(dt('@project downloaded from @url.', array('@project' => $name, '@url' => $download['url'])), 'ok');
- drush_copy_dir($tmp_location, $download_location, TRUE);
+ drush_shell_exec('cp -Rf %s %s', $tmp_location, dirname($download_location));
return dirname($download_location);
}
}
$download['url'] = dt("unspecified location");
}
make_error('DOWNLOAD_ERROR', dt('Unable to download @project from @url.', array('@project' => $name, '@url' => $download['url'])));
- drush_delete_dir(dirname($tmp_location), TRUE);
+ drush_shell_exec('rm -rf %s', dirname($tmp_location));
return FALSE;
}
/**
* Checks out an SVN repository to the specified download location.
*
- * @return mixed
+ * @return
* The download location on success, FALSE otherwise.
*/
function make_download_svn($name, $download, $download_location) {
array_unshift($args, $command);
$result = call_user_func_array($function, $args);
if ($result) {
- $args = array(
- '@project' => $name,
- '@command' => $command,
- '@url' => $download['url'],
- );
- drush_log(dt('@project @command from @url.', $args), 'ok');
+ drush_log(dt('@project @command from @url.', array('@project' => $name, '@command' => $command, '@url' => $download['url'])), 'ok');
return $download_location;
}
else {
if (!empty($info[$algo])) {
$hash = _make_hash($algo, $content);
if ($hash !== $info[$algo]) {
- $args = array(
- '@algo' => $algo,
- '@file' => basename($filename),
- '@expected' => $info[$algo],
- '@hash' => $hash,
- );
- make_error('DOWNLOAD_ERROR', dt('Checksum @algo verification failed for @file. Expected @expected, received @hash.', $args));
+ make_error('DOWNLOAD_ERROR', dt('Checksum @algo verification failed for @file. Expected @expected, received @hash.', array('@algo' => $algo, '@file' => basename($filename), '@expected' => $info[$algo], '@hash' => $hash)));
return FALSE;
}
}
<?php
-/**
- * @file
- * Drush Make commands.
- */
-/**
- * Default localization server for downloading translations.
- */
define('MAKE_DEFAULT_L10N_SERVER', 'http://localize.drupal.org/l10n_server.xml');
/**
include_once 'make.project.inc';
/**
- * Implements hook_drush_command().
+ * Implementation of hook_drush_command().
*/
function make_drush_command() {
$items['make'] = array(
'examples' => array(
'drush make example.make example' => 'Build the example.make makefile in the example directory.',
'drush make --no-core --contrib-destination=. installprofile.make' => 'Build an installation profile within an existing Drupal site',
- 'drush make http://example.com/example.make example' => 'Build the remote example.make makefile in the example directory.',
),
'options' => array(
'version' => 'Print the make API version and exit.',
- 'concurrency' => array(
- 'description' => 'Limits the number of concurrent projects that will be processed at the same time. The default is 4.',
- 'example-value' => '1',
- ),
+ 'concurrency' => 'Limits the number of concurrent projects that will be processed at the same time. The default is 4.',
'contrib-destination' => 'Specify a path under which modules and themes should be placed. Defaults to sites/all.',
'force-complete' => 'Force a complete build even if errors occur.',
'ignore-checksums' => 'Ignore md5 checksums for downloads.',
- 'md5' => array(
- 'description' => 'Output an md5 hash of the current build after completion. Use --md5=print to print to stdout.',
- 'example-value' => 'print',
- 'value' => 'optional',
- ),
+ 'md5' => 'Output an md5 hash of the current build after completion. Use --md5=print to print to stdout.',
'make-update-default-url' => 'The default location to load the XML update information from.',
'no-cache' => 'Do not use the pm-download caching (defaults to using this).',
'no-clean' => 'Leave temporary build directories in place instead of cleaning up after completion.',
'no-core' => 'Do not require a Drupal core project to be specified.',
'no-patch-txt' => 'Do not write a PATCHES.txt file in the directory of each patched project.',
- 'no-gitinfofile' => 'Do not modify .info files when cloning from Git.',
'prepare-install' => 'Prepare the built site for installation. Generate a properly permissioned settings.php and files directory.',
'tar' => 'Generate a tar archive of the build. The output filename will be [build path].tar.gz.',
'test' => 'Run a temporary test build and clean up.',
'translations' => 'Retrieve translations for the specified comma-separated list of language(s) if available for all projects.',
'working-copy' => 'Preserves VCS directories, like .git, for projects downloaded using such methods.',
'download-mechanism' => 'How to download files. Should be autodetected, but this is an override if it doesn\'t work. Options are "curl" and "make" (a native download method).',
- 'projects' => array(
- 'description' => 'Restrict the make to this comma-separated list of projects. To specify all projects, pass *.',
- 'example' => 'views,ctools',
- ),
- 'libraries' => array(
- 'description' => 'Restrict the make to this comma-separated list of libraries. To specify all libraries, pass *.',
- 'example' => 'tinymce',
- ),
),
'engines' => array('release_info'),
'topics' => array('docs-make', 'docs-make-example'),
);
// Add docs topic.
- $docs_dir = drush_get_context('DOC_PREFIX', DRUSH_BASE_PATH);
+ $make_dir = dirname(__FILE__);
$items['docs-make'] = array(
'description' => 'Drush Make overview with examples',
'hidden' => TRUE,
'topic' => TRUE,
'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
'callback' => 'drush_print_file',
- 'callback arguments' => array($docs_dir . '/docs/make.txt'),
+ 'callback arguments' => array($make_dir . '/README.txt'),
);
$items['docs-make-example'] = array(
'description' => 'Drush Make example makefile',
'topic' => TRUE,
'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
'callback' => 'drush_print_file',
- 'callback arguments' => array($docs_dir . '/examples/example.make'),
+ 'callback arguments' => array($make_dir . '/EXAMPLE.make'),
);
return $items;
}
/**
- * Implements hook_drush_help().
+ * Implementation of hook_drush_help().
*/
function make_drush_help($section) {
switch ($section) {
case 'drush:make':
- return 'Turns a makefile into a Drupal codebase. For a full description of options and makefile syntax, see docs/make.txt and examples/example.make.';
+ return 'Turns a makefile into a Drupal codebase. For a full description of options and makefile syntax, see the README.txt included with drush make.';
case 'drush:make-generate':
return 'Generate a makefile from the current Drupal site, specifying project version numbers unless not known or otherwise specified. Unversioned projects will be interpreted later by drush make as "most recent stable release"';
}
/**
* Command argument complete callback.
*
- * @return array
+ * @return
* Strong glob of files to complete on.
*/
function make_make_complete() {
return array(
'files' => array(
- 'directories' => array(
- 'pattern' => '*',
- 'flags' => GLOB_ONLYDIR,
- ),
'make' => array(
'pattern' => '*.make',
),
}
$info = make_parse_info_file($makefile);
-
- // Support making just a portion of a make file.
- $include_only = array(
- 'projects' => array_filter(drush_get_option_list('projects')),
- 'libraries' => array_filter(drush_get_option_list('libraries')),
- );
- $info = make_prune_info_file($info, $include_only);
-
if ($info === FALSE || ($info = make_validate_info_file($info)) === FALSE) {
return FALSE;
}
}
/**
- * Implements drush_hook_post_COMMAND() for the make command.
+ * Implementation of drush_hook_post_COMMAND() for the make command.
*/
function drush_make_post_make($makefile = NULL, $build_path = NULL) {
if (drush_get_option('version')) {
make_clean_tmp();
}
-/**
- * Process all projects specified in the make file.
- */
function make_projects($recursion, $contrib_destination, $info, $build_path) {
$projects = array();
if (empty($info['projects'])) {
$project['l10n_url'] = MAKE_DEFAULT_L10N_SERVER;
}
- // For convenience: define $request to be compatible with release_info
- // engine.
- // TODO: refactor to enforce 'make' to internally work with release_info
- // keys.
+ // For convenience: define $request to be compatible with release_info engine.
+ // TODO: refactor to enforce 'make' to internally work with release_info keys.
$request = make_prepare_request($project);
if ($project['location'] != RELEASE_INFO_DEFAULT_URL && !isset($project['type'])) {
foreach ($projects as $type => $type_projects) {
foreach ($type_projects as $project) {
- if (make_project_needs_release_info($project)) {
- // For convenience: define $request to be compatible with release_info
- // engine.
- // TODO: refactor to enforce 'make' to internally work with release_info
- // keys.
+ if ($project['location'] == RELEASE_INFO_DEFAULT_URL && (!isset($project['type']) || !isset($project['download']))) {
+ // For convenience: define $request to be compatible with release_info engine.
+ // TODO: refactor to enforce 'make' to internally work with release_info keys.
$request = make_prepare_request($project, $type);
// Set the cache option based on our '--no-cache' option.
$cache_before = drush_get_option('cache');
if (!isset($project['download'])) {
$project['download'] = array(
'type' => 'pm',
- 'full_version' => $release['version'],
+ 'version' => $release['version'],
'download_link' => $release['download_link'],
- 'status url' => $request['status url'],
);
}
}
);
$common_options = drush_redispatch_get_options();
- // Merge in stdin options since we process makefiles recursively. See http://drupal.org/node/1510180.
- $common_options = array_merge($common_options, drush_get_context('stdin'));
// Package handler should use 'wget'.
$common_options['package-handler'] = 'wget';
// Unless --verbose or --debug are passed, quiter backend output.
if (empty($common_options['verbose']) && empty($common_options['debug'])) {
$backend_options['#output-label'] = FALSE;
- $backend_options['integrate'] = TRUE;
+ $backend_options['integrate'] = FALSE;
}
drush_backend_invoke_concurrent($invocations, $common_options, $backend_options, 'make-process', '@none');
}
return TRUE;
}
-/**
- * Process all libraries specified in the make file.
- */
function make_libraries($contrib_destination, $info, $build_path) {
if (empty($info['libraries'])) {
return;
$ignore_checksums = drush_get_option('ignore-checksums');
foreach ($info['libraries'] as $key => $library) {
if (!is_string($key) || !is_array($library)) {
- // TODO Print a prettier message.
+ // TODO Print a prettier message
continue;
}
// Merge the known data onto the library info.
}
}
-/**
- * The path where the final build will be placed.
- */
function make_build_path($build_path) {
static $saved_path;
if (isset($saved_path)) {
return $build_path;
}
-/**
- * Move the completed build into place.
- */
function make_move_build($build_path) {
$tmp_path = make_tmp();
$ret = TRUE;
if ($build_path == '.') {
- $info = drush_scan_directory($tmp_path . DIRECTORY_SEPARATOR . '__build__', '/./', array('.', '..'), 0, FALSE, 'filename', 0, TRUE);
+ drush_shell_exec('ls -A %s', $tmp_path . '/__build__');
+ $info = drush_shell_exec_output();
foreach ($info as $file) {
- $destination = $build_path . DIRECTORY_SEPARATOR . $file->basename;
- if (file_exists($destination)) {
- // To prevent the removal of top-level directories such as 'modules' or
- // 'themes', descend in a level if the file exists.
- // TODO: This only protects one level of directories from being removed.
- $files = drush_scan_directory($file->filename, '/./', array('.', '..'), 0, FALSE);
- foreach ($files as $file) {
- $ret = $ret && drush_copy_dir($file->filename, $destination . DIRECTORY_SEPARATOR . $file->basename, FILE_EXISTS_MERGE);
- }
- }
- else {
- $ret = $ret && drush_copy_dir($file->filename, $destination);
- }
+ $ret = $ret && drush_shell_exec("cp -Rf %s %s", $tmp_path . '/__build__/' . $file, $build_path);
}
}
else {
drush_mkdir(dirname($build_path));
- $ret = drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . '__build__', $tmp_path . DIRECTORY_SEPARATOR . basename($build_path), TRUE);
- $ret = $ret && drush_copy_dir($tmp_path . DIRECTORY_SEPARATOR . basename($build_path), $build_path);
+ drush_shell_exec("mv %s %s", $tmp_path . '/__build__', $tmp_path . '/' . basename($build_path));
+ drush_shell_exec("cp -Rf %s %s", $tmp_path . '/' . basename($build_path), dirname($build_path));
}
if (!$ret) {
drush_set_error(dt("Cannot move build into place"));
/**
* Create a request array for use with release_info_fetch().
*
- * @param array $project
+ * @param $project
* Project array.
- * @param string $type
+ * @param $type
* 'contrib' or 'core'.
*/
function make_prepare_request($project, $type = 'contrib') {
}
return $request;
}
-
-/**
- * Determine if the release information is required for this
- * project. When it is determined that it is, this potentially results
- * in the use of pm-download to process the project.
- *
- * If the location of the project is not customized (uses d.o), and
- * one of the following is true, then release information is required:
- *
- * - $project['type'] has not been specified
- * - $project['download'] has not been specified
- *
- * @see make_projects()
- */
-function make_project_needs_release_info($project) {
- return isset($project['location'])
- // Only fetch release info if the project type is unknown OR if
- // download attributes are unspecified.
- && (!isset($project['type']) || !isset($project['download']));
-}
<?php
-/**
- * @file
- * Drush Make processing classes.
- */
/**
* The base project class.
protected static $self = array();
/**
- * Default to overwrite to allow recursive builds to process properly.
- *
- * TODO refactor this to be more selective. Ideally a merge would take place
- * instead of an overwrite.
- */
- protected $overwrite = TRUE;
-
- /**
* Set attributes and retrieve project information.
*/
protected function __construct($project) {
/**
* Get an instance for the type and project.
*
- * @param string $type
+ * @param $type
* Type of project: core, library, module, profile, or translation.
- * @param array $project
+ * @param $project
* Project information.
- *
- * @return mixed
+ * @return
* An instance for the project or FALSE if invalid type.
*/
public static function getInstance($type, $project) {
*/
function download() {
$this->downloaded = TRUE;
-
- // In some cases, make_download_factory() is going to need to know the
- // full version string of the project we're trying to download. However,
- // the version is a project-level attribute, not a download-level
- // attribute. So, if we don't already have a full version string in the
- // download array (e.g. if it was initialized via the release history XML
- // for the PM case), we take the version info from the project-level
- // attribute, convert it into a full version string, and stuff it into
- // $this->download so that the download backend has access to it, too.
- if (!empty($this->version) && empty($this->download['full_version'])) {
- $full_version = '';
- $matches = array();
- // Core needs different conversion rules than contrib.
- if (!empty($this->type) && $this->type == 'core') {
- // Generally, the version for core is already set properly.
- $full_version = $this->version;
- // However, it might just be something like '7' or '7.x', in which
- // case we need to turn that into '7.x-dev';
- if (preg_match('/^\d+(\.x)?$/', $this->version, $matches)) {
- // If there's no '.x' already, append it.
- if (empty($matches[1])) {
- $full_version .= '.x';
- }
- $full_version .= '-dev';
- }
- }
- // Contrib.
- else {
- // If the version doesn't already define a core version, prepend it.
- if (!preg_match('/^\d+\.x-\d+.*$/', $this->version)) {
- // Just find the major version from $this->core so we don't end up
- // with version strings like '7.12-2.0'.
- $core_parts = explode('.', $this->core);
- $full_version = $core_parts[0] . '.x-';
- }
- $full_version .= $this->version;
- // If the project-level version attribute is just a number it's a major
- // version.
- if (preg_match('/^\d+(\.x)?$/', $this->version, $matches)) {
- // If there's no '.x' already, append it.
- if (empty($matches[1])) {
- $full_version .= '.x';
- }
- $full_version .= '-dev';
- }
- }
- $this->download['full_version'] = $full_version;
- }
-
if (make_download_factory($this->name, $this->download, $this->download_location) === FALSE) {
$this->downloaded = FALSE;
}
return TRUE;
}
- /**
- * Determine the location to download project to.
- */
function findDownloadLocation() {
$this->path = $this->generatePath();
$this->project_directory = !empty($this->directory_name) ? $this->directory_name : $this->name;
// This directory shouldn't exist yet -- if it does, stop,
// unless overwrite has been set to TRUE.
if (is_dir($this->download_location) && !$this->overwrite) {
- drush_set_error(dt('Directory not empty: !directory', array('!directory' => $this->download_location)));
+ drush_set_error(dt('Directory not empty: %directory', array('%directory' => $this->download_location)));
return FALSE;
}
elseif ($this->download['type'] === 'pm') {
$info = array('url' => $info);
}
// Download the patch.
- if ($filename = _make_download_file($info['url'])) {
+ if ($filename = _make_download_file($info)) {
$patched = FALSE;
$output = '';
// Test each patch style; -p1 is the default with git. See
}
// In some rare cases, git will fail to apply a patch, fallback to using
- // the 'patch' command.
+ // the 'patch -p0' command.
if (!$patched) {
- foreach ($patch_levels as $patch_level) {
- // --no-backup-if-mismatch here is a hack that fixes some
- // differences between how patch works on windows and unix.
- if ($patched = drush_shell_exec("patch %s --no-backup-if-mismatch -d %s < %s", $patch_level, $project_directory, $filename)) {
- break;
- }
- }
+ $patched = drush_shell_exec("patch -p0 -d %s < %s", $project_directory, $filename);
}
if ($output = drush_shell_exec_output()) {
function getTranslations($project_directory) {
static $cache = array();
$langcodes = $this->translations;
- if ($langcodes && in_array($this->type, array('core', 'module', 'profile', 'theme'), TRUE)) {
+ if ($langcodes && $this->version !== drush_get_option('drush-make-version-best') && in_array($this->type, array('core', 'module', 'profile', 'theme'), TRUE)) {
// Support the l10n_path, l10n_url keys from l10n_update. Note that the
// l10n_server key is not supported.
if (isset($this->l10n_path)) {
if ($filename = _make_download_file($l10n_server)) {
$server_info = simplexml_load_string(file_get_contents($filename));
$cache[$l10n_server] = !empty($server_info->update_url) ? $server_info->update_url : FALSE;
+ drush_op('unlink', $filename);
}
}
if ($cache[$l10n_server]) {
$update_url = $cache[$l10n_server];
}
else {
- make_error('XML_ERROR', dt("Could not retrieve l10n update url for !project.", array('!project' => $project['name'])));
+ make_error('XML_ERROR', dt("Could not retrieve l10n update url for %project.", array('%project' => $project['name'])));
return FALSE;
}
}
foreach ($langcodes as $langcode) {
$variables = array(
'%project' => $this->name,
- '%release' => $this->download['full_version'],
+ '%release' => $this->download['version'],
'%core' => $this->core,
'%language' => $langcode,
'%filename' => '%filename',
);
$url = strtr($update_url, $variables);
- // Download the translation file. Since its contents are volatile,
- // cache for only 4 hours.
- if ($filename = _make_download_file($url, 3600 * 4)) {
+ // Download the translation file.
+ if ($filename = _make_download_file($url)) {
// If this is the core project type, download the translation file
- // and place it in every profile and an additional copy in
- // modules/system/translations where it can be detected for import
- // by other non-default install profiles.
+ // and create two copies:
+ // 1. To profiles/default/translations. It must be named
+ // langcode.po to be used properly by the installer.
+ // 2. To modules/system/translations where it can be detected for
+ // import by other non-default install profiles.
if ($this->type === 'core') {
- $profiles = drush_scan_directory($project_directory . '/profiles', '/.*/', array(), 0, FALSE, 'filename', 0, TRUE);
- foreach ($profiles as $profile) {
- if (is_dir($project_directory . '/profiles/' . $profile->basename)) {
- drush_mkdir($project_directory . '/profiles/' . $profile->basename . '/translations');
- drush_copy_dir($filename, $project_directory . '/profiles/' . $profile->basename . '/translations/' . $langcode . '.po');
- }
- }
+ drush_mkdir($project_directory . '/profiles/default/translations');
drush_mkdir($project_directory . '/modules/system/translations');
- drush_copy_dir($filename, $project_directory . '/modules/system/translations/' . $langcode . '.po');
+ drush_shell_exec("cp %s %s", $filename, $project_directory . '/profiles/default/translations/' . $langcode . '.po');
+ drush_shell_exec("mv %s %s", $filename, $project_directory . '/modules/system/translations/' . $langcode . '.po');
}
else {
drush_mkdir($project_directory . '/translations');
- drush_copy_dir($filename, $project_directory . '/translations/' . $langcode . '.po', TRUE);
+ drush_shell_exec("mv %s %s", $filename, $project_directory . '/translations/' . $langcode . '.po');
}
}
else {
drush_log('All translations downloaded for ' . $this->name, 'ok');
}
else {
- drush_log('Unable to download translations for ' . $this->name . ': ' . implode(', ', $failed), 'warning');
+ drush_log('Unable to download translations for '. $this->name .': ' . implode(', ', $failed), 'warning');
}
}
}
/**
* Generate the proper path for this project type.
*
- * @param boolean $base
+ * @param $base
* Whether include the base part (tmp dir). Defaults to TRUE.
*/
protected function generatePath($base = TRUE) {
/**
* Return the proper path for dependencies to be placed in.
*
- * @return string
+ * @return
* The path that dependencies will be placed in.
*/
protected function buildPath($directory) {
return $this->base_contrib_destination;
}
- /**
- * Recurse to process additional makefiles that may be found during
- * processing.
- */
function recurse($path) {
$makefile = $this->download_location . '/' . $this->name . '.make';
if (!file_exists($makefile)) {
return TRUE;
}
}
- drush_log(dt("Found makefile: !makefile", array("!makefile" => basename($makefile))), 'ok');
+ drush_log(dt("Found makefile: %makefile", array("%makefile" => basename($makefile))), 'ok');
$info = make_parse_info_file($makefile);
if (!($info = make_validate_info_file($info))) {
}
}
-/**
- * For processing Drupal core projects.
- */
class DrushMakeProject_Core extends DrushMakeProject {
- /**
- * Override constructor for core to properly set contrib destination.
- */
protected function __construct(&$project) {
parent::__construct($project);
$this->contrib_destination = '';
}
- /**
- * Determine the location to download project to.
- */
function findDownloadLocation() {
$this->path = $this->download_location = $this->generatePath();
$this->project_directory = '';
if (is_dir($this->download_location)) {
- drush_set_error(dt('Directory not empty: !directory', array('!directory' => $this->download_location)));
+ drush_set_error(dt('Directory not empty: %directory', array('%directory' => $this->download_location)));
return FALSE;
}
elseif ($this->download['type'] === 'pm') {
- // pm-download will create the final __build__ directory, so nothing to do
- // here.
+ // pm-download will create the final __build__ directory, so nothing to do here.
}
else {
drush_mkdir($this->download_location);
}
}
-/**
- * For processing libraries.
- */
class DrushMakeProject_Library extends DrushMakeProject {
- /**
- * Override constructor for libraries to properly set contrib destination.
- */
protected function __construct(&$project) {
parent::__construct($project);
// Allow libraries to specify where they should live in the build path.
$this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . $project_path;
}
-
- /**
- * No recursion for libraries, sorry :-(
- */
+ // No recursion for libraries, sorry :-(
function recurse($path) {}
-
- /**
- * No translations for libraries.
- */
+ // Neither translations.
function getTranslations($download_location) {}
}
-/**
- * For processing modules.
- */
class DrushMakeProject_Module extends DrushMakeProject {
- /**
- * Override constructor for modules to properly set contrib destination.
- */
protected function __construct(&$project) {
parent::__construct($project);
$this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'modules';
}
}
-/**
- * For processing installation profiles.
- */
class DrushMakeProject_Profile extends DrushMakeProject {
- /**
- * Override contructor for installation profiles to properly set contrib
- * destination.
- */
protected function __construct(&$project) {
parent::__construct($project);
$this->contrib_destination = (!empty($this->destination) ? $this->destination : 'profiles');
}
- /**
- * Find the build path.
- */
protected function buildPath($directory) {
return $this->generatePath(FALSE) . '/' . $directory;
}
}
-/**
- * For processing themes.
- */
class DrushMakeProject_Theme extends DrushMakeProject {
- /**
- * Override contructor for themes to properly set contrib destination.
- */
protected function __construct(&$project) {
parent::__construct($project);
$this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'themes';
}
}
-/**
- * For processing translations.
- */
class DrushMakeProject_Translation extends DrushMakeProject {
- /**
- * Override constructor for translations to properly set contrib destination.
- */
protected function __construct(&$project) {
parent::__construct($project);
- switch ($project['core']) {
+ switch($project['core']) {
case '5.x':
// Don't think there's an automatic place we can put 5.x translations,
// so we'll toss them in a translations directory in the Drupal root.
$this->contrib_destination = ($this->base_contrib_destination != '.' ? $this->base_contrib_destination . '/' : '') . 'translations';
break;
-
default:
$this->contrib_destination = '';
break;
<?php
-/**
- * @file
- * General utility functions for Drush Make.
- */
/**
* Parse Drupal info file format.
*/
function make_parse_info_file($makefile, $parsed = TRUE) {
if (!($data = make_get_data($makefile))) {
- return drush_set_error(dt('Invalid or empty make file: !makefile', array('!makefile' => $makefile)));
+ return drush_set_error(dt('Invalid or empty make file: %makefile', array('%makefile' => $makefile)));
}
- if (!($info = _drush_drupal_parse_info_file($data))) {
+ if (!($info = _make_parse_info_file($data))) {
return FALSE;
}
if (!empty($info['includes'])) {
if (make_valid_url($include, TRUE) && ($file = make_parse_info_file($include, FALSE))) {
$includes[] = $file;
}
- elseif (file_exists($include_path . '/' . $include) && ($file = make_parse_info_file($include_path . '/' . $include, FALSE))) {
+ else if (file_exists($include_path .'/'. $include) && ($file = make_parse_info_file($include_path .'/'. $include, FALSE))) {
$includes[] = $file;
}
- elseif (file_exists($include) && ($file = make_parse_info_file($include, FALSE))) {
+ else if (file_exists($include) && ($file = make_parse_info_file($include, FALSE))) {
$includes[] = $file;
}
else {
- make_error('BUILD_ERROR', dt("Include file missing: !include", array('!include' => $include)));
+ make_error('BUILD_ERROR', dt("Include file missing: %include", array('%include' => $include)));
}
}
}
}
$includes[] = $data;
$data = implode("\n", $includes);
- $info = _drush_drupal_parse_info_file($data);
+ $info = _make_parse_info_file($data);
}
if ($parsed) {
return $info;
}
}
-/**
- * Remove entries in the info file in accordance with the options passed in.
- * Entries are either explicitly 'allowed' (with the $include_only parameter) in
- * which case all *other* entries will be excluded.
- *
- * @param array $info
- * A parsed info file.
- *
- * @param array $include_only
- * (Optional) Array keyed by entry type (e.g. 'libraries') against an array of
- * allowed keys for that type. The special value '*' means 'all entries of
- * this type'. If this parameter is omitted, no entries will be excluded.
- *
- * @return array
- * The $info array, pruned if necessary.
- */
-function make_prune_info_file($info, $include_only = array()) {
-
- // We may get passed FALSE in some cases.
- // Also we cannot prune an empty array, so no point in this code running!
- if (empty($info)) {
- return $info;
+function _make_parse_info_file($data) {
+ if (!$data) {
+ return FALSE;
}
- // We will accrue an explanation of our activities here.
- $msg = array();
- $msg['scope'] = dt("Drush make restricted to the following entries:");
-
- $pruned = FALSE;
-
- if (count(array_filter($include_only))) {
- $pruned = TRUE;
- foreach ($include_only as $type => $keys) {
-
- // For translating
- // dt("Projects");
- // dt("Libraries");
- $type_title = dt(ucfirst($type));
-
- // Handle the special '*' value.
- if (in_array('*', $keys)) {
- $msg[$type] = dt("!entry_type: <All>", array('!entry_type' => $type_title));
+ if (preg_match_all('
+ @^\s* # Start at the beginning of a line, ignoring leading whitespace
+ ((?:
+ [^=;\[\]]| # Key names cannot contain equal signs, semi-colons or square brackets,
+ \[[^\[\]]*\] # unless they are balanced and not nested
+ )+?)
+ \s*=\s* # Key/value pairs are separated by equal signs (ignoring white-space)
+ (?:
+ ("(?:[^"]|(?<=\\\\)")*")| # Double-quoted string, which may contain slash-escaped quotes/slashes
+ (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
+ ([^\r\n]*?) # Non-quoted string
+ )\s*$ # Stop at the next end of a line, ignoring trailing whitespace
+ @msx', $data, $matches, PREG_SET_ORDER)) {
+ $info = array();
+ foreach ($matches as $match) {
+ // Fetch the key and value string
+ $i = 0;
+ foreach (array('key', 'value1', 'value2', 'value3') as $var) {
+ $$var = isset($match[++$i]) ? $match[$i] : '';
}
+ $value = stripslashes(substr($value1, 1, -1)) . stripslashes(substr($value2, 1, -1)) . $value3;
- // Handle a (possibly empty) array of keys to include/exclude.
- else {
- $info[$type] = array_intersect_key($info[$type], array_fill_keys($keys, 1));
- unset($msg[$type]);
- if (!empty($info[$type])) {
- $msg[$type] = dt("!entry_type: !make_entries", array('!entry_type' => $type_title, '!make_entries' => implode(', ', array_keys($info[$type]))));
+ // Parse array syntax
+ $keys = preg_split('/\]?\[/', rtrim($key, ']'));
+ $last = array_pop($keys);
+ $parent = &$info;
+
+ // Create nested arrays
+ foreach ($keys as $key) {
+ if ($key == '') {
+ $key = count($parent);
}
+ if (!isset($parent[$key]) || !is_array($parent[$key])) {
+ $parent[$key] = array();
+ }
+ $parent = &$parent[$key];
}
- }
- }
- if ($pruned) {
- // Make it clear to the user what's going on.
- drush_log(implode("\n", $msg), 'ok');
+ // Handle PHP constants
+ if (defined($value)) {
+ $value = constant($value);
+ }
- // Throw an error if these restrictions reduced the make to nothing.
- if (empty($info['projects']) && empty($info['libraries'])) {
- // This error mentions the options explicitly to make it as clear as
- // possible to the user why this error has occurred.
- make_error('BUILD_ERROR', dt("All projects and libraries have been excluded. Review the 'projects' and 'libraries' options."));
+ // Insert actual value
+ if ($last == '') {
+ $last = count($parent);
+ }
+ $parent[$last] = $value;
}
+ return $info;
}
-
- return $info;
+ return FALSE;
}
-/**
- * Validate the make file.
- */
function make_validate_info_file($info) {
// Assume no errors to start.
$errors = FALSE;
$info['core'] = $matches[1] . '.x';
}
else {
- make_error('BUILD_ERROR', dt("The 'core' attribute !core has an incorrect format.", array('!core' => $info['core'])));
+ make_error('BUILD_ERROR', dt("The 'core' attribute %core has an incorrect format.", array('%core' => $info['core'])));
$errors = TRUE;
}
$errors = TRUE;
}
else {
- // Filter out entries that have been forcibly removed via [foo] = FALSE.
+ // Filter out entries that have been forcibly removed via [foo] = FALSE
$info['projects'] = array_filter($info['projects']);
foreach ($info['projects'] as $project => $project_data) {
// Project has an attributes array.
if (is_string($project) && is_array($project_data)) {
if (in_array($project, $names)) {
- make_error('BUILD_ERROR', dt("Project !project defined twice (remove the first projects[] = !project).", array('!project' => $project)));
+ make_error('BUILD_ERROR', dt("Project %project defined twice (remove the first projects[] = %project).", array('%project' => $project)));
$errors = TRUE;
}
$names[] = $project;
if (in_array($attribute, array('contrib_destination'))) {
unset($info['projects'][$project][$attribute]);
}
- // Prevent malicious attempts to access other areas of the
- // filesystem.
+ // Prevent malicious attempts to access other areas of the filesystem.
elseif (in_array($attribute, array('subdir', 'directory_name')) && !make_safe_path($value)) {
- $args = array(
- '!path' => $value,
- '!attribute' => $attribute,
- '!project' => $project,
- );
- make_error('BUILD_ERROR', dt("Illegal path !path for '!attribute' attribute in project !project.", $args));
+ make_error('BUILD_ERROR', dt("Illegal path %path for '%attribute' attribute in project %project.", array('%path' => $value, '%attribute' => $attribute, '%project' => $project)));
$errors = TRUE;
}
}
// Cover if there is no project info, it's just a project name.
elseif (is_numeric($project) && is_string($project_data)) {
if (in_array($project_data, $names)) {
- make_error('BUILD_ERROR', dt("Project !project defined twice (remove the first projects[] = !project).", array('!project' => $project_data)));
+ make_error('BUILD_ERROR', dt("Project %project defined twice (remove the first projects[] = %project).", array('%project' => $project_data)));
$errors = TRUE;
}
$names[] = $project_data;
// Convert shorthand project version style to array format.
elseif (is_string($project_data)) {
if (in_array($project, $names)) {
- make_error('BUILD_ERROR', dt("Project !project defined twice (remove the first projects[] = !project).", array('!project' => $project)));
+ make_error('BUILD_ERROR', dt("Project %project defined twice (remove the first projects[] = %project).", array('%project' => $project)));
$errors = TRUE;
}
$names[] = $project;
$info['projects'][$project] = array('version' => $project_data);
}
else {
- make_error('BUILD_ERROR', dt('Project !project incorrectly specified.', array('!project' => $project)));
+ make_error('BUILD_ERROR', dt('Project %project incorrectly specified.', array('%project' => $project)));
$errors = TRUE;
}
}
$errors = TRUE;
}
else {
- // Filter out entries that have been forcibly removed via [foo] = FALSE.
+ // Filter out entries that have been forcibly removed via [foo] = FALSE
$info['libraries'] = array_filter($info['libraries']);
- foreach ($info['libraries'] as $library => $library_data) {
+ foreach($info['libraries'] as $library => $library_data) {
if (is_array($library_data)) {
- foreach ($library_data as $attribute => $value) {
+ foreach($library_data as $attribute => $value) {
// Unset disallowed attributes.
if (in_array($attribute, array('contrib_destination'))) {
unset($info['libraries'][$library][$attribute]);
}
- // Prevent malicious attempts to access other areas of the
- // filesystem.
+ // Prevent malicious attempts to access other areas of the filesystem.
elseif (in_array($attribute, array('contrib-destination', 'directory_name')) && !make_safe_path($value)) {
- $args = array(
- '!path' => $value,
- '!attribute' => $attribute,
- '!library' => $library,
- );
- make_error('BUILD_ERROR', dt("Illegal path !path for '!attribute' attribute in library !library.", $args));
+ make_error('BUILD_ERROR', dt("Illegal path %path for '%attribute' attribute in library %library.", array('%path' => $value, '%attribute' => $attribute, '%library' => $library)));
$errors = TRUE;
}
}
}
foreach (drush_command_implements('make_validate_info') as $module) {
- $function = $module . '_make_validate_info';
+ $function = $module .'_make_validate_info';
$return = $function($info);
if ($return) {
$info = $return;
*/
function make_valid_url($url, $absolute = FALSE) {
if ($absolute) {
- return (bool) preg_match("
+ return (bool)preg_match("
/^ # Start at the beginning of the text
(?:ftp|https?):\/\/ # Look for ftp, http, or https schemes
(?: # Userinfo (optional) which is typically
$/xi", $url);
}
else {
- return (bool) preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url);
+ return (bool)preg_match("/^(?:[\w#!:\.\?\+=&@$'~*,;\/\(\)\[\]\-]|%[0-9a-f]{2})+$/i", $url);
}
}
/**
* Find, and possibly create, a temporary directory.
*
- * @param boolean $set
+ * @param $set
* Must be TRUE to create a directory.
- * @param string $directory
+ * @param $directory
* Pass in a directory to use. This is required if using any
* concurrent operations.
- *
* @todo Merge with drush_tempdir().
*/
function make_tmp($set = TRUE, $directory = NULL) {
else {
$tmp_dir .= '/make_tmp_' . time() . '_' . uniqid();
}
- if (!drush_get_option('no-clean', FALSE)) {
- drush_register_file_for_deletion($tmp_dir);
- }
if (file_exists($tmp_dir)) {
return make_tmp(TRUE);
}
- // Create the directory.
+ // else create it
drush_mkdir($tmp_dir);
}
return $tmp_dir;
}
-/**
- * Removes the temporary build directory. On failed builds, this is handled by
- * drush_register_file_for_deletion().
- */
function make_clean_tmp() {
if (!($tmp_dir = make_tmp(FALSE))) {
return;
}
if (!drush_get_option('no-clean', FALSE)) {
- drush_delete_dir($tmp_dir);
+ drush_shell_exec('rm -rf %s', $tmp_dir);
}
else {
- drush_log(dt('Temporary directory: !dir', array('!dir' => $tmp_dir)), 'ok');
+ drush_log(dt('Temporary directory: %dir', array('%dir' => $tmp_dir)), 'ok');
}
}
-/**
- * Prepare a Drupal installation, copying default.settings.php to settings.php.
- */
function make_prepare_install($build_path) {
$default = make_tmp() . '/__build__/sites/default';
- drush_copy_dir($default . DIRECTORY_SEPARATOR . 'default.settings.php', $default . DIRECTORY_SEPARATOR . 'settings.php', TRUE);
+ drush_shell_exec("cp %s %s", $default . '/default.settings.php', $default . '/settings.php');
drush_mkdir($default . '/files');
- chmod($default . DIRECTORY_SEPARATOR . 'settings.php', 0666);
- chmod($default . DIRECTORY_SEPARATOR . 'files', 0777);
+ drush_shell_exec("chmod a+w %s %s", $default . '/settings.php', $default . '/files');
}
-/**
- * Calculate a cksum on each file in the build, and md5 the resulting hashes.
- */
function make_md5() {
- return drush_dir_md5(make_tmp());
+ if (drush_shell_exec("( find %s -type f -exec cksum {} \; )", make_tmp())) {
+ $hashes = array();
+ foreach (drush_shell_exec_output() as $line) {
+ // Remove the temporary build path which includes a (relatively)
+ // unique timestamp.
+ $line = str_replace(make_tmp(), '', $line);
+ // Trim to sanitize.
+ $line = trim($line);
+ $hashes[] = $line;
+ }
+ sort($hashes);
+ return md5(implode("\n", $hashes));
+ }
}
-/**
+/*
* @todo drush_archive_dump() also makes a tar. Consolidate?
*/
function make_tar($build_path) {
$dirname = basename($build_path, '.tar.gz');
// Move the build directory to a more human-friendly name, so that tar will
// use it instead.
- drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . '__build__', $tmp_path . DIRECTORY_SEPARATOR . $dirname, TRUE);
+ drush_shell_exec("mv %s %s", $tmp_path . '/__build__', $tmp_path . '/' . $dirname);
// Only move the tar file to it's final location if it's been built
// successfully.
- if (drush_shell_exec("%s -C %s -Pczf %s %s", drush_get_tar_executable(), $tmp_path, $tmp_path . '/' . $filename, $dirname)) {
- drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . $filename, $build_path, TRUE);
+ if (drush_shell_exec("tar -C %s -Pczf %s %s", $tmp_path, $tmp_path . '/' . $filename, $dirname)) {
+ drush_shell_exec("mv %s %s", $tmp_path . '/' . $filename, $build_path);
};
// Move the build directory back to it's original location for consistency.
- drush_move_dir($tmp_path . DIRECTORY_SEPARATOR . $dirname, $tmp_path . DIRECTORY_SEPARATOR . '__build__');
+ drush_shell_exec("mv %s %s", $tmp_path . '/' . $dirname, $tmp_path . '/__build__');
}
/**
/**
* Checks an attribute's path to ensure it's not maliciously crafted.
*
- * @param string $path
+ * @param $path
* The path to check.
*/
function make_safe_path($path) {
* This is a helper function to abstract the retrieval of data, so that it can
* come from files, STDIN, etc. Currently supports filepath and STDIN.
*
- * @param string $data_source
+ * @param $data_source
* The path to a file, or '-' for STDIN.
- *
- * @return string
+ * @return
* The raw data as a string.
*/
function make_get_data($data_source) {
}
// Remote file.
else {
- $file = _make_download_file($data_source);
+ $file = _make_download_file(array('url' => $data_source));
$data = file_get_contents($file);
drush_op('unlink', $file);
}
}
/**
- * Helper to provide sys_get_temp_dir if on php < 5.2.1.
+ * Helper to provide sys_get_temp_dir if on php < 5.2.1
*/
if (!function_exists('sys_get_temp_dir')) {
- /**
- * Based on
- * http://www.phpit.net/article/creating-zip-tar-archives-dynamically-php/2/
- */
+ // Based on http://www.phpit.net/
+ // article/creating-zip-tar-archives-dynamically-php/2/
function sys_get_temp_dir() {
- // Try to get from environment variable.
- if (!empty($_ENV['TMP'])) {
+ // Try to get from environment variable
+ if (!empty($_ENV['TMP'])){
return realpath($_ENV['TMP']);
}
- elseif (!empty($_ENV['TMPDIR'])) {
+ else if (!empty($_ENV['TMPDIR'])){
return realpath($_ENV['TMPDIR']);
}
- elseif (!empty($_ENV['TEMP'])) {
+ else if (!empty($_ENV['TEMP'])){
return realpath($_ENV['TEMP']);
}
else {
- // Detect by creating a temporary file.
- // Try to use system's temporary directory as random name
- // shouldn't exist.
+ // Detect by creating a temporary file
+ // Try to use system's temporary directory
+ // as random name shouldn't exist
$temp_file = tempnam(md5(uniqid(rand(), TRUE)), '');
if ($temp_file) {
$temp_dir = realpath(dirname($temp_file));
}
}
}
+
+/**
+ * @todo Consolidate with drush_download_file if possible.
+ * Drush make parallel to drupal_http_request, but writes responses to a file.
+ */
+function make_http_request($url, $destination, $headers = array(), $method = 'GET', $data = NULL, $retry = 5) {
+ global $db_prefix;
+
+ $result = new stdClass();
+
+ // Parse the URL and make sure we can handle the schema.
+ $uri = parse_url($url);
+
+ if ($uri == FALSE) {
+ $result->error = 'unable to parse URL';
+ $result->code = -1001;
+ return $result;
+ }
+
+ if (!isset($uri['scheme'])) {
+ $result->error = 'missing schema';
+ $result->code = -1002;
+ return $result;
+ }
+
+ switch ($uri['scheme']) {
+ case 'http':
+ case 'feed':
+ $port = isset($uri['port']) ? $uri['port'] : 80;
+ $host = $uri['host'] . ($port != 80 ? ':'. $port : '');
+ $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15);
+ break;
+ case 'https':
+ // Note: Only works for PHP 4.3 compiled with OpenSSL.
+ $port = isset($uri['port']) ? $uri['port'] : 443;
+ $host = $uri['host'] . ($port != 443 ? ':'. $port : '');
+ $fp = @fsockopen('ssl://'. $uri['host'], $port, $errno, $errstr, 20);
+ break;
+ default:
+ $result->error = 'invalid schema '. $uri['scheme'];
+ $result->code = -1003;
+ return $result;
+ }
+
+ // Make sure the socket opened properly.
+ if (!$fp) {
+ // When a network error occurs, we use a negative number so it does not
+ // clash with the HTTP status codes.
+ $result->code = -$errno;
+ $result->error = trim($errstr);
+
+ return $result;
+ }
+
+ // Construct the path to act on.
+ $path = isset($uri['path']) ? $uri['path'] : '/';
+ if (isset($uri['query'])) {
+ $path .= '?'. $uri['query'];
+ }
+
+ // Create HTTP request.
+ $defaults = array(
+ // RFC 2616: "non-standard ports MUST, default ports MAY be included".
+ // We don't add the port to prevent from breaking rewrite rules checking the
+ // host that do not take into account the port number.
+ 'Host' => "Host: $host",
+ 'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)',
+ );
+
+ // Only add Content-Length if we actually have any content or if it is a POST
+ // or PUT request. Some non-standard servers get confused by Content-Length in
+ // at least HEAD/GET requests, and Squid always requires Content-Length in
+ // POST/PUT requests.
+ $content_length = strlen($data);
+ if ($content_length > 0 || $method == 'POST' || $method == 'PUT') {
+ $defaults['Content-Length'] = 'Content-Length: '. $content_length;
+ }
+
+ // If the server url has a user then attempt to use basic authentication
+ if (isset($uri['user'])) {
+ $defaults['Authorization'] = 'Authorization: Basic '. base64_encode($uri['user'] . (!empty($uri['pass']) ? ":". $uri['pass'] : ''));
+ }
+
+ // If the database prefix is being used by SimpleTest to run the tests in a copied
+ // database then set the user-agent header to the database prefix so that any
+ // calls to other Drupal pages will run the SimpleTest prefixed database. The
+ // user-agent is used to ensure that multiple testing sessions running at the
+ // same time won't interfere with each other as they would if the database
+ // prefix were stored statically in a file or database variable.
+ if (is_string($db_prefix) && preg_match("/^simpletest\d+$/", $db_prefix, $matches)) {
+ $defaults['User-Agent'] = 'User-Agent: ' . $matches[0];
+ }
+
+ foreach ($headers as $header => $value) {
+ $defaults[$header] = $header .': '. $value;
+ }
+
+ $request = $method .' '. $path ." HTTP/1.0\r\n";
+ $request .= implode("\r\n", $defaults);
+ $request .= "\r\n\r\n";
+ $request .= $data;
+
+ $result->request = $request;
+
+ fwrite($fp, $request);
+
+ $fp_dest = fopen($destination, 'w');
+ $all_headers = FALSE;
+
+ // Fetch response.
+ $split = '';
+ while (!feof($fp) && $chunk = fread($fp, 1024)) {
+ if (strpos($chunk, "\r\n\r\n") !== FALSE && !$all_headers) {
+ $all_headers = TRUE;
+ list($header, $body) = explode("\r\n\r\n", $chunk, 2);
+ $split .= $header;
+ fwrite($fp_dest, $body);
+ }
+ elseif (!$all_headers) {
+ $split .= $chunk;
+ }
+ else {
+ fwrite($fp_dest, $chunk);
+ }
+ }
+ fclose($fp_dest);
+ fclose($fp);
+
+ // Parse response.
+ $split = preg_split("/\r\n|\n|\r/", $split);
+
+ list($protocol, $code, $status_message) = explode(' ', trim(array_shift($split)), 3);
+ $result->protocol = $protocol;
+ $result->status_message = $status_message;
+
+ $result->headers = array();
+
+ // Parse headers.
+ while ($line = trim(array_shift($split))) {
+ list($header, $value) = explode(':', $line, 2);
+ if (isset($result->headers[$header]) && $header == 'Set-Cookie') {
+ // RFC 2109: the Set-Cookie response header comprises the token Set-
+ // Cookie:, followed by a comma-separated list of one or more cookies.
+ $result->headers[$header] .= ','. trim($value);
+ }
+ else {
+ $result->headers[$header] = trim($value);
+ }
+ }
+
+ $responses = array(
+ 100 => 'Continue', 101 => 'Switching Protocols',
+ 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
+ 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect',
+ 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed',
+ 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported'
+ );
+ // RFC 2616 states that all unknown HTTP codes must be treated the same as the
+ // base code in their class.
+ if (!isset($responses[$code])) {
+ $code = floor($code / 100) * 100;
+ }
+
+ $result->code = $code;
+ return $result;
+}
* Command callback. Download Drupal core or any project.
*/
function drush_pm_download() {
- if (!$requests = pm_parse_arguments(func_get_args(), FALSE)) {
+ if (!$requests = pm_parse_arguments(func_get_args(), FALSE, FALSE)) {
$requests = array('drupal');
}
// Get release history for each request and download the project.
$source = drush_get_option('source', RELEASE_INFO_DEFAULT_URL);
- $restrict_to = drush_get_option('dev', FALSE) ? 'dev' : '';
+ $dev = drush_get_option('dev', FALSE);
$select = drush_get_option('select', 'auto');
$all = drush_get_option('all', FALSE);
foreach ($requests as $name => $request) {
$request['status url'] = $source;
- $release = release_info_fetch($request, $restrict_to, $select, $all);
+ $release = release_info_fetch($request, $dev, $select, $all);
if ($release == FALSE) {
continue;
}
// Determine the name of the directory that will contain the project.
// We face here all the asymetries to make it smooth for package handlers.
// For Drupal core: --drupal-project-rename or drupal-x.y
- if (($request['project_type'] == 'core') ||
- (($request['project_type'] == 'profile') && (drush_get_option('variant', 'full') == 'full'))) {
+ if ($request['project_type'] == 'core') {
// Avoid downloading core into existing core.
if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_ROOT) {
if (strpos(realpath(drush_get_option('destination')), DRUPAL_ROOT) !== FALSE) {
if ($rename = drush_get_option('drupal-project-rename', FALSE)) {
if ($rename === TRUE) {
- $request['project_dir'] = $request['name'];
+ $request['project_dir'] = 'drupal';
}
else {
$request['project_dir'] = $rename;
drush_delete_dir($empty_dir, TRUE);
}
}
-
- // Post download actions.
- package_handler_post_download($request, $release);
- drush_command_invoke_all('drush_pm_post_download', $request, $release);
- $version_control->post_download($request);
-
- // Print release notes if --notes option is set.
- if (drush_get_option('notes') && !drush_get_context('DRUSH_PIPE')) {
- release_info_print_releasenotes(array($name . '-' . $release['version']), FALSE);
- }
-
- // Inform the user about available modules a/o themes in the downloaded project.
- drush_pm_extensions_in_project($request);
}
else {
- // We don't `return` here in order to proceed with downloading additional projects.
- drush_set_error('DRUSH_PM_DOWNLOAD_FAILED', dt("Project !project (!version) could not be downloaded to !dest.", array('!project' => $request['name'], '!version' => $release['version'], '!dest' => $request['project_install_location'])));
+ drush_log(dt("Project !project (!version) could not be downloaded to !dest.", array('!project' => $request['name'], '!version' => $release['version'], '!dest' => $request['project_install_location'])), 'error');
+ continue;
}
+
+ // Post download actions.
+ package_handler_post_download($request, $release);
+ drush_command_invoke_all('drush_pm_post_download', $request, $release);
+ $version_control->post_download($request);
+
+ // Print release notes if --notes option is set.
+ if (drush_get_option('notes') && !drush_get_context('DRUSH_PIPE')) {
+ release_info_print_releasenotes(array($name . '-' . $release['version']), FALSE);
+ }
+
+ // Inform the user about available modules a/o themes in the downloaded project.
+ drush_pm_extensions_in_project($request);
}
}
*/
function pm_drush_pm_download_destination_alter(&$project, $release) {
// A module is a pure drush command if it has no .module and contain
- // .drush.inc files. Skip this test for drush itself, though; we do
- // not want to download drush to the ~/.drush folder.
- if (($project['project_type'] == 'module') && ($project['name'] != 'drush')) {
+ // .drush.inc files.
+ if ($project['project_type'] == 'module') {
$drush_command_files = drush_scan_directory($project['full_project_path'], '/.*\.drush.inc/');
if (!empty($drush_command_files)) {
$module_files = drush_scan_directory($project['full_project_path'], '/.*\.module/');
function drush_pm_info() {
$args = pm_parse_arguments(func_get_args());
- $extension_info = drush_get_extensions(FALSE);
+ $extension_info = drush_get_extensions();
_drush_pm_expand_extensions($args, $extension_info);
- // If no extensions are provided, show all.
+ // If no extensions are provided, select all but the hidden ones.
if (count($args) == 0) {
+ foreach ($extension_info as $key => $extension) {
+ if (isset($extension->info['hidden'])) {
+ unset($extension_info[$key]);
+ }
+ }
$args = array_keys($extension_info);
}
}
// Check git_deploy is enabled. Only for bootstrapped sites.
if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_FULL) {
- if (!drush_get_option('gitinfofile') && !module_exists('git_deploy')) {
+ if (!module_exists('git_deploy')) {
drush_log(dt('git package handler needs git_deploy module enabled to work properly.'), 'warning');
}
}
}
if (drush_get_option('gitinfofile', FALSE)) {
- $matches = array();
- if (preg_match('/^(.+).x-dev$/', $release['version'], $matches)) {
- $full_version = drush_pm_git_drupalorg_compute_rebuild_version($project['project_install_location'], $matches[1]);
- }
- else {
- $full_version = $release['version'];
- }
- drush_pm_inject_info_file_metadata($project['project_install_location'], $project['name'], $full_version);
+ package_handler_inject_info($project, $release);
}
}
+function package_handler_inject_info($project, $release) {
+ // Handler for the directory and open directory and walk through the filenames
+ $info_files = drush_scan_directory($project['project_install_location'], "/.*\.info$/");
+ $return = TRUE;
+ // Consider that the d.o. package manager is aware of submodules in parent modules
+ foreach($info_files as $info_file) {
+ $return = $return && package_handler_fix_info_file_version($info_file->filename, $project['name'], $release['version']);
+ }
+ return $return;
+}
+
/**
- * Helper function to compute the rebulid version string for a project.
- *
- * This does some magic in Git to find the latest release tag along
- * the branch we're packaging from, count the number of commits since
- * then, and use that to construct this fancy alternate version string
- * which is useful for the version-specific dependency support in Drupal
- * 7 and higher.
- *
- * NOTE: A similar function lives in git_deploy and in the drupal.org
- * packaging script (see DrupalorgProjectPackageRelease.class.php inside
- * drupalorg/drupalorg_project/plugins/release_packager). Any changes to the
- * actual logic in here should probably be reflected in the other places.
- *
- * @param string $project_dir
- * The full path to the root directory of the project to operate on.
- * @param string $branch
- * The branch that we're using for -dev. This should only include the
- * core version, the dash, and the branch's major version (eg. '7.x-2').
- *
- * @return string
- * The full 'rebuild version string' in the given Git checkout.
+ * Fix the given .info file with the specified version string
+ * Taken straight from: http://drupalcode.org/project/drupalorg.git/blob/refs/heads/6.x-3.x:/drupalorg_project/plugins/release_packager/DrupalorgProjectPackageRelease.class.php#l192
*/
-function drush_pm_git_drupalorg_compute_rebuild_version($project_dir, $branch) {
- $rebuild_version = '';
- $branch_preg = preg_quote($branch);
-
- if (drush_shell_cd_and_exec($project_dir, 'git describe --tags')) {
- $shell_output = drush_shell_exec_output();
- $last_tag = $shell_output[0];
- // Make sure the tag starts as Drupal formatted (for eg.
- // 7.x-1.0-alpha1) and if we are on a proper branch (ie. not master)
- // then it's on that branch.
- if (preg_match('/^(?<drupalversion>' . $branch_preg . '\.\d+(?:-[^-]+)?)(?<gitextra>-(?<numberofcommits>\d+-)g[0-9a-f]{7})?$/', $last_tag, $matches)) {
- // If we found additional git metadata (in particular, number of commits)
- // then use that info to build the version string.
- if (isset($matches['gitextra'])) {
- $rebuild_version = $matches['drupalversion'] . '+' . $matches['numberofcommits'] . 'dev';
- }
- // Otherwise, the branch tip is pointing to the same commit as the
- // last tag on the branch, in which case we use the prior tag and
- // add '+0-dev' to indicate we're still on a -dev branch.
- else {
- $rebuild_version = $last_tag . '+0-dev';
- }
- }
- }
- return $rebuild_version;
+function package_handler_fix_info_file_version($file, $project, $version) {
+ $info = "\n\n; Information added by the drush package handler on " . date('Y-m-d') . "\n";
+ $info .= "version = \"$version\"\n";
+ // .info files started with 5.x, so we don't have to worry about version
+ // strings like "4.7.x-1.0" in this regular expression. If we can't parse
+ // the version (also from an old "HEAD" release), or the version isn't at
+ // least 6.x, don't add any "core" attribute at all.
+ $matches = array();
+ if (preg_match('/^((\d+)\.x)-.*/', $version, $matches) && $matches[2] >= 6) {
+ $info .= "core = \"$matches[1]\"\n";
+ }
+ $info .= "project = \"$project\"\n";
+ $info .= 'datestamp = "'. time() ."\"\n";
+ $info .= "\n";
+
+ if (!$info_fd = fopen($file, 'a+')) {
+ drush_set_error(dt("ERROR: fopen(@file, 'ab') failed", array('@file' => $file)));
+ return false;
+ }
+ if (!fwrite($info_fd, $info)) {
+ drush_set_error(dt("ERROR: fwrite(@file) failed", array('@file' => $file)) . '<pre>' . $info);
+ return false;
+ }
+ return true;
}
// Dev snapshots can be cached for 4 hours and releases for 1 year.
$max_age = strpos($release['download_link'], '-dev') !== FALSE ? 3600*4 : 86400*365;
$filename = basename($release['download_link']);
- $path = drush_download_file($release['download_link'], $request['base_project_path'] . DIRECTORY_SEPARATOR . $filename, $max_age);
+ $path = drush_download_file($release['download_link'], $request['base_project_path'] . $filename, $max_age);
if ($path || drush_get_context('DRUSH_SIMULATE')) {
drush_log("Downloading " . $filename . " was successful.");
}
$file_list = drush_tarball_extract($path, $request['base_project_path'], TRUE);
// Move untarred directory to project_dir, if distinct.
- if (($request['project_type'] == 'core') || (($request['project_type'] == 'profile') && (drush_get_option('variant', 'full') == 'full'))) {
+ if (($request['project_type'] == 'core') || (($request['project_type'] == 'profile') && (drush_get_option('variant', 'core') == 'core'))) {
// Obtain the dodgy project_dir for drupal core.
$project_dir = drush_trim_path($file_list[0]);
if ($request['project_dir'] != $project_dir) {
);
$update_suboptions = array(
'lock' => array(
- 'lock-message' => array(
- 'description' => 'A brief message explaining why a project is being locked; displayed during pm-updatecode. Optional.',
- 'example-value' => 'message',
- ),
+ 'lock-message' => 'A brief message explaining why a project is being locked; displayed during pm-updatecode. Optional.',
'unlock' => 'Remove the persistent lock from the specified projects so that they may be updated again.',
),
);
'description' => 'Show a list of available extensions (modules and themes).',
'callback arguments' => array(array(), FALSE),
'options' => array(
- 'type' => array(
- 'description' => 'Filter by extension type. Choices: module, theme.',
- 'example-value' => 'module',
- ),
- 'status' => array(
- 'description' => 'Filter by extension status. Choices: enabled, disabled and/or \'not installed\'. You can use multiple comma separated values. (i.e. --status="disabled,not installed").',
- 'example-value' => 'disabled',
- ),
+ 'type' => 'Filter by extension type. Choices: module, theme.',
+ 'status' => 'Filter by extension status. Choices: enabled, disable and/or \'not installed\'. You can use multiple comma separated values. (i.e. --status="disabled,not installed").',
'package' => 'Filter by project packages. You can use multiple comma separated values. (i.e. --package="Core - required,Other").',
'core' => 'Filter out extensions that are not in drupal core.',
'no-core' => 'Filter out extensions that are provided by drupal core.',
'projects' => 'A comma delimited list of drupal.org project names, with optional version. Defaults to \'drupal\'',
),
'options' => array(
- 'destination' => array(
- 'description' => 'Path to which the project will be copied. If you\'re providing a relative path, note it is relative to the drupal root (if bootstrapped).',
- 'example-value' => 'path',
- ),
+ 'destination' => 'Path to which the project will be copied. If you\'re providing a relative path, note it is relative to the drupal root (if bootstrapped).',
'use-site-dir' => 'Force to use the site specific directory. It will create the directory if it doesn\'t exist. If --destination is also present this option will be ignored.',
'notes' => 'Show release notes after each project is downloaded.',
- 'variant' => array(
- 'description' => "Only useful for install profiles. Possible values: 'full', 'projects', 'profile-only'.",
- 'example-value' => 'full',
- ),
+ 'variant' => "Only useful for install profiles. Possible values: 'full', 'projects', 'profile-only'.",
'select' => "Select the version to download interactively from a list of available releases.",
'drupal-project-rename' => 'Alternate name for "drupal-x.y" directory when downloading Drupal project. Defaults to "drupal".',
- 'default-major' => array(
- 'description' => 'Specify the default major version of modules to download when there is no bootstrapped Drupal site. Defaults to "7".',
- 'example-value' => '6',
- ),
+ 'default-major' => 'Specify the default major version of modules to download when there is no bootstrapped Drupal site. Defaults to "7".',
'skip' => 'Skip automatic downloading of libraries (c.f. devel).',
'pipe' => 'Returns a list of the names of the extensions (modules and themes) contained in the downloaded projects.',
),
*/
function pm_complete_extensions() {
if (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
- $extension_info = drush_get_extensions(FALSE);
- return array('values' => array_keys($extension_info));
+ $extension_info = drush_get_extensions();
+ foreach ($extension_info as $key => $extension) {
+ if (!isset($extension->info['hidden'])) {
+ $extensions[] = $key;
+ }
+ }
+ return array('values' => $extensions);
}
}
$projects[$project]['extensions'][] = $extension->name;
}
- // Once we have all the extensions for each project, obtain projects' path.
- $reserved = array('modules', 'sites', 'themes');
- foreach ($projects as $name => $project) {
- if ($name == 'drupal') {
- continue;
- }
- drush_log(dt('Obtaining !project project path.', array('!project' => $name)), 'debug');
- $path = _drush_pm_find_common_path($project['type'], $project['extensions']);
- // Prevent from setting a reserved path. For example it may happen in a case
- // where a module and a theme are declared as part of a same project.
- // There's a special case, a project called "sites", this is the reason for
- // the second condition here.
- if ((in_array(basename($path), $reserved)) && (!in_array($name, $reserved))) {
- drush_log(dt('Error while trying to find the common path for enabled extensions of project !project. Extensions are: !extensions.', array('!project' => $name, '!extensions' => implode(', ', $project['extensions']))), 'error');
- }
- else {
- $projects[$name]['path'] = $path;
- }
- }
-
return $projects;
}
/**
- * Helper function to find the common path for a list of extensions in the aim to obtain the project name.
- *
- * @param $project_type
- * Type of project we're trying to find. Valid values: module, theme.
- * @param $extensions
- * Array of extension names.
- */
-function _drush_pm_find_common_path($project_type, $extensions) {
- // Select the first path as the candidate to be the common prefix.
- $extension = array_pop($extensions);
- while (!($path = drupal_get_path($project_type, $extension))) {
- drush_log(dt('Unknown path for !extension !type.', array('!extension' => $extension, '!type' => $project_type)), 'warning');
- $extension = array_pop($extensions);
- }
-
- // If there's only one extension we are done. Otherwise, we need to find
- // the common prefix for all of them.
- if (count($extensions) > 0) {
- // Iterate over the other projects.
- while($extension = array_pop($extensions)) {
- $path2 = drupal_get_path($project_type, $extension);
- if (!$path2) {
- drush_log(dt('Unknown path for !extension !type.', array('!extension' => $extension, '!type' => $project_type)), 'debug');
- continue;
- }
- // Option 1: same path.
- if ($path == $path2) {
- continue;
- }
- // Option 2: $path is a prefix of $path2.
- if (strpos($path2, $path) === 0) {
- continue;
- }
- // Option 3: $path2 is a prefix of $path.
- if (strpos($path, $path2) === 0) {
- $path = $path2;
- continue;
- }
- // Option 4: no one is a prefix of the other. Find the common
- // prefix by iteratively strip the rigthtmost piece of $path.
- // We will iterate until a prefix is found or path = '.', that on the
- // other hand is a condition theorically impossible to reach.
- do {
- $path = dirname($path);
- if (strpos($path2, $path) === 0) {
- break;
- }
- } while ($path != '.');
- }
- }
-
- return $path;
-}
-
-/**
* Returns a list of enabled modules.
*
* This is a simplified version of module_list().
$header[] = dt('Version');
$rows[] = $header;
- $extension_info = drush_get_extensions(FALSE);
+ $extension_info = drush_get_extensions();
uasort($extension_info, '_drush_pm_sort_extensions');
$major_version = drush_drupal_major_version();
unset($extension_info[$key]);
continue;
}
+ if (($major_version >= 6) and (isset($extension->info['hidden']))) {
+ unset($extension_info[$key]);
+ continue;
+ }
// filter out core if --no-core specified
if (drush_get_option('no-core', FALSE)) {
}
/**
- * Validate callback. Determine the modules and themes that the user would like enabled.
+ * Command callback. Enable one or more extensions from downloaded projects.
*/
-function drush_pm_enable_validate() {
+function drush_pm_enable() {
$args = pm_parse_arguments(func_get_args());
$extension_info = drush_get_extensions();
}
}
}
-
- $searchpath = array();
- foreach (array_merge($modules, $themes) as $name) {
- $searchpath[] = dirname($extension_info[$name]->filename);
- }
- // Add all modules that passed validation to the drush
- // list of commandfiles (if they have any). This
- // will allow these newly-enabled modules to participate
- // in the pre-pm_enable and post-pm_enable hooks.
- if (!empty($searchpath)) {
- _drush_add_commandfiles($searchpath);
- }
-
- drush_set_context('PM_ENABLE_EXTENSION_INFO', $extension_info);
- drush_set_context('PM_ENABLE_MODULES', $modules);
- drush_set_context('PM_ENABLE_THEMES', $themes);
-
- return TRUE;
-}
-
-/**
- * Command callback. Enable one or more extensions from downloaded projects.
- * Note that the modules and themes to be enabled were evaluated during the
- * pm-enable validate hook, above.
- */
-function drush_pm_enable() {
- // Get the data built during the validate phase
- $extension_info = drush_get_context('PM_ENABLE_EXTENSION_INFO');
- $modules = drush_get_context('PM_ENABLE_MODULES');
- $themes = drush_get_context('PM_ENABLE_THEMES');
// Inform the user which extensions will finally be enabled.
$extensions = array_merge($modules, $themes);
// Enable modules and pass dependency validation in form submit.
if (!empty($modules)) {
drush_module_enable($modules);
+ drush_system_modules_form_submit(pm_module_list());
}
// Inform the user of final status.
$rsc = drush_db_select('system', array('name', 'status'), 'name IN (:extensions)', array(':extensions' => $extensions));
$problem_extensions = array();
+ $searchpath = array();
while ($extension = drush_db_fetch_object($rsc)) {
if ($extension->status) {
drush_log(dt('!extension was enabled successfully.', array('!extension' => $extension->name)), 'ok');
+ $searchpath[] = dirname($extension_info[$extension->name]->filename);
}
else {
$problem_extensions[] = $extension->name;
}
}
+ // Add all modules that were enabled to the drush
+ // list of commandfiles (if they have any). This
+ // will allow these newly-enabled modules to participate
+ // in the post_pm_enable hook.
+ if (!empty($searchpath)) {
+ _drush_add_commandfiles($searchpath);
+ }
if (!empty($problem_extensions)) {
return drush_set_error('DRUSH_PM_ENABLE_EXTENSION_ISSUE', dt('There was a problem enabling !extension.', array('!extension' => implode(',', $problem_extensions))));
}
- // Return the list of extensions enabled
- return $extensions;
}
/**
foreach ($required as $module) {
if (isset($modules[$module])) {
unset($modules[$module]);
- $info = $extension_info[$module]->info;
// No message for hidden modules.
- if (!isset($info['hidden'])) {
- $explanation = !empty($info['explanation']) ? ' ' . dt('Reason: !explanation', array('!explanation' => strip_tags($info['explanation']))) : '';
- drush_log(dt('!module is a required module and can\'t be disabled.', array('!module' => $module)) . $explanation, 'ok');
+ if (!isset($extension_info[$module]->info['hidden'])) {
+ drush_log(dt('!module is a required module and can\'t be disabled.', array('!module' => $module)), 'ok');
}
}
}
// Disable modules and pass dependency validation in form submit.
if (!empty($modules)) {
drush_module_disable($modules);
+ drush_system_modules_form_submit(pm_module_list());
}
// Inform the user of final status.
}
/**
+ * Completes projects' update data with the path to install location on disk.
+ *
+ * Given an array of release info for available projects, find the path to the install location.
+ */
+function _pm_get_project_path($data, $lookup) {
+ foreach ($data as $name => $release) {
+ if ($name == 'drupal') {
+ continue;
+ }
+ // Array of extensions (modules/themes) within the project.
+ $extensions = array_keys($release[$lookup]);
+ $path = _pm_find_common_path($release['project_type'], $extensions);
+ $reserved = array('modules', 'sites', 'themes');
+ if ((in_array(basename($path), $reserved)) && (!in_array($name, $reserved))) {
+ drush_log(dt('Error while trying to find the common path for enabled extensions of project !project. Extensions are: !extensions.', array('!project' => $name, '!extensions' => implode(', ', $extensions))), 'error');
+ unset($data[$name]);
+ }
+ else {
+ $data[$name]['path'] = $path;
+ }
+ }
+
+ return $data;
+}
+
+/**
+ * Helper function to find the common path for a list of extensions in the aim to obtain the project name.
+ *
+ * @param $project_type
+ * Type of project we're trying to find. Valid values: module, theme.
+ * @param $extensions
+ * Array of extension names.
+ */
+function _pm_find_common_path($project_type, $extensions) {
+ // Select the first path as the candidate to be the common prefix.
+ $path = drupal_get_path($project_type, array_pop($extensions));
+ // If there's only one extension we are done. Otherwise, we need to find
+ // the common prefix for all of them.
+ if (count($extensions) > 0) {
+ // Iterate over the other projects.
+ while($extension = array_pop($extensions)) {
+ $path2 = drupal_get_path($project_type, $extension);
+ // Option 1: same path.
+ if ($path == $path2) {
+ continue;
+ }
+ // Option 2: $path is a prefix of $path2.
+ if (strpos($path2, $path) === 0) {
+ continue;
+ }
+ // Option 3: $path2 is a prefix of $path.
+ if (strpos($path, $path2) === 0) {
+ $path = $path2;
+ continue;
+ }
+ // Option 4: no one is a prefix of the other. Find the common
+ // prefix by iteratively strip the rigthtmost piece of $path.
+ // We will iterate until a prefix is found or path = '.', that on the
+ // other hand is a condition theorically impossible to reach.
+ do {
+ $path = dirname($path);
+ if (strpos($path2, $path) === 0) {
+ break;
+ }
+ } while ($path != '.');
+ }
+ }
+
+ return $path;
+}
+
+/**
* Command callback. Show available releases for given project(s).
*/
function drush_pm_releases() {
/**
* Sanitize user provided arguments to several pm commands.
*
- * Return an array of arguments off a space and/or comma separated values.
+ * Return an array of arguments off a space and/or comma separated values. It also
+ * lowercase arguments and optionally convert dashes to underscores.
*/
-function pm_parse_arguments($args, $dashes_to_underscores = TRUE) {
+function pm_parse_arguments($args, $dashes_to_underscores = TRUE, $lowercase = TRUE) {
$arguments = _convert_csv_to_array($args);
foreach ($arguments as $key => $argument) {
$argument = ($dashes_to_underscores) ? strtr($argument, '-', '_') : $argument;
+ if ($lowercase) {
+ $arguments[$key] = strtolower($argument);
+ }
}
return $arguments;
}
preg_match('/-+(HEAD|(?:(\d+\.x)-+)?(\d+\.[\dx]+.*))$/', $request, $matches);
if (isset($matches[1])) {
// The project is whatever we have prior to the version part of the request.
- $project = trim(substr($request, 0, strlen($request) - strlen($matches[0])), ' -');
+ $project = strtolower(trim(substr($request, 0, strlen($request) - strlen($matches[0])), ' -'));
if ($matches[1] == 'HEAD' || $matches[2] == 'HEAD') {
drush_log('DRUSH_PM_HEAD', 'Can\'t download HEAD releases because Drupal.org project information only provides for numbered release nodes.', 'warning');
}
return $empty_dirs;
}
-
-/**
- * Inject metadata into all .info files for a given project.
- *
- * @param string $project_dir
- * The full path to the root directory of the project to operate on.
- * @param string $project_name
- * The project machine name (AKA shortname).
- * @param string $version
- * The version string to inject into the .info file(s).
- *
- * @return boolean
- * TRUE on success, FALSE on any failures appending data to .info files.
- */
-function drush_pm_inject_info_file_metadata($project_dir, $project_name, $version) {
- $info_files = drush_scan_directory($project_dir, '/.*\.info$/');
- if (!empty($info_files)) {
- // Construct the string of metadata to append to all the .info files.
- // Taken straight from: http://drupalcode.org/project/drupalorg.git/blob/refs/heads/6.x-3.x:/drupalorg_project/plugins/release_packager/DrupalorgProjectPackageRelease.class.php#l192
- $info = "\n\n; Information added by drush on " . date('Y-m-d') . "\n";
- $info .= "version = \"$version\"\n";
- // .info files started with 5.x, so we don't have to worry about version
- // strings like "4.7.x-1.0" in this regular expression. If we can't parse
- // the version (also from an old "HEAD" release), or the version isn't at
- // least 6.x, don't add any "core" attribute at all.
- $matches = array();
- if (preg_match('/^((\d+)\.x)-.*/', $version, $matches) && $matches[2] >= 6) {
- $info .= "core = \"$matches[1]\"\n";
- }
- $info .= "project = \"$project_name\"\n";
- $info .= 'datestamp = "'. time() ."\"\n";
- $info .= "\n";
- foreach ($info_files as $info_file) {
- if (!drush_file_append_data($info_file->filename, $info)) {
- return FALSE;
- }
- }
- }
- return TRUE;
-}
* @param Array &$request
* A project request as returned by pm_parse_project_version(). The array will
* be expanded with the project type.
- * @param String $restrict_to
- * One of:
- * 'dev': Forces choosing a -dev release.
- * 'version': Forces choosing a point release.
- * '': No restriction.
- * Default is ''.
+ * @param Boolean $dev
+ * Forces to choose a -dev release.
* @param String $select
* Strategy for selecting a release, should be one of:
* - auto: Try to select the latest release, if none found allow the user
* @return
* The selected release xml object.
*/
-function release_info_fetch(&$request, $restrict_to = '', $select = 'never', $all = FALSE) {
+function release_info_fetch(&$request, $dev = FALSE, $select = 'never', $all = FALSE) {
if (!in_array($select, array('auto', 'never', 'always'))) {
drush_log(dt("Error: select strategy must be one of: auto, never, always", array(), 'error'));
return FALSE;
if ($select != 'always') {
// Try to identify the most appropriate release.
- $release = updatexml_parse_release($request, $xml, $restrict_to);
+ $release = updatexml_parse_release($request, $xml, $dev);
if ($release) {
return $release;
}
}
$project_info = updatexml_get_releases_from_xml($xml, $request['name']);
- $releases = release_info_filter_releases($project_info['releases'], $all, $restrict_to);
+ $releases = release_info_filter_releases($project_info['releases'], $all, $dev);
$options = array();
foreach($releases as $version => $release) {
$options[$version] = array($version, '-', gmdate('Y-M-d', $release['date']), '-', implode(', ', $release['release_status']));
* @param $all
* Show all releases. If FALSE, shows only the first release that is
* Recommended or Supported or Security or Installed.
- * @param String $restrict_to
- * If set to 'dev', show only development release.
+ * @param $dev
+ * Show only development release.
* @param $show_all_until_installed
* If TRUE, then all releases will be shown until the INSTALLED release
* is found, at which point the algorithm will stop.
*/
-function release_info_filter_releases($releases, $all = FALSE, $restrict_to = '', $show_all_until_installed = TRUE) {
+function release_info_filter_releases($releases, $all = FALSE, $dev = FALSE, $show_all_until_installed = TRUE) {
// Start off by sorting releases by release date.
uasort($releases, '_release_info_compare_date');
// Find version_major for the installed release.
// Now iterate through and filter out the releases we're interested in.
$options = array();
$limits_list = array();
- $dev = $restrict_to == 'dev';
foreach ($releases as $version => $release_info) {
if (!$dev || ((array_key_exists('version_extra', $release_info)) && ($release_info['version_extra'] == 'dev'))) {
$saw_unique_status = FALSE;
// releases found. If this is the case, then we will force ALL to TRUE
// and show everything on the second iteration.
if (($all === FALSE) && ($show_all_until_installed === TRUE)) {
- $options = release_info_filter_releases($releases, empty($options), $restrict_to, FALSE);
+ $options = release_info_filter_releases($releases, empty($options), $dev, FALSE);
}
return $options;
}
/**
- * Pick most appropriate release from XML list or ask the user if no one fits.
+ * Pick most appropriate release from XML list or ask the user if no one fits.
*
* @param array $request
- * An array with project and version strings as returned by
- * pm_parse_project_version().
+ * An array of version specifications as returned by pm_parse_project_version().
* @param resource $xml
* A handle to the XML document.
- * @param String $restrict_to
- * One of:
- * 'dev': Forces a -dev release.
- * 'version': Forces a point release.
- * '': No restriction (auto-selects latest recommended or supported release
- if requested release is not found).
- * Default is ''.
*/
-function updatexml_parse_release($request, $xml, $restrict_to = '') {
+function updatexml_parse_release($request, $xml, $dev = FALSE) {
if (!empty($request['version'])) {
- $matches = array();
- // See if we only have a branch version.
- if (preg_match('/^\d+\.x-(\d+)$/', $request['version'], $matches)) {
- $xpath_releases = "/project/releases/release[status='published'][version_major=" . (string)$matches[1] . "]";
- $releases = @$xml->xpath($xpath_releases);
- }
- else {
- // In some cases, the request only says something like '7.x-3.x' but the
- // version strings include '-dev' on the end, so we need to append that
- // here for the xpath to match below.
- if (substr($request['version'], -2) == '.x') {
- $request['version'] .= '-dev';
- }
- $releases = $xml->xpath("/project/releases/release[status='published'][version='" . $request['version'] . "']");
- if (empty($releases)) {
- if (empty($restrict_to)) {
- drush_log(dt("Could not locate !project version !version, will try to download latest recommended or supported release.", array('!project' => $request['name'], '!version' => $request['version'])), 'warning');
- }
- else {
- drush_log(dt("Could not locate !project version !version.", array('!project' => $request['name'], '!version' => $request['version'])), 'warning');
- return FALSE;
- }
- }
- }
- }
-
- if ($restrict_to == 'dev') {
- $releases = @$xml->xpath("/project/releases/release[status='published'][version_extra='dev']");
+ $releases = $xml->xpath("/project/releases/release[status='published'][version='" . $request['version'] . "']");
if (empty($releases)) {
- drush_print(dt('There is no development release for project !project.', array('!type' => $release_type, '!project' => $request['name'])));
- return FALSE;
+ drush_log(dt("Could not locate specified project version, downloading latest stable version"), 'warning');
}
}
-
// If that did not work, we will get the first published release for the
- // recommended major version or fallback to other supported major versions.
+ // recommended major version.
if (empty($releases)) {
- foreach(array('recommended_major', 'supported_majors') as $release_type) {
- if ($versions = $xml->xpath("/project/$release_type")) {
- $xpath = "/project/releases/release[status='published'][version_major=" . (string)$versions[0] . "]";
- $releases = @$xml->xpath($xpath);
- if (!empty($releases)) {
- break;
- }
- }
+ if ($recommended_major = $xml->xpath("/project/recommended_major")) {
+ $xpath_releases = "/project/releases/release[status='published'][version_major=" . (string)$recommended_major[0] . "]";
+ $releases = @$xml->xpath($xpath_releases);
}
}
-
- // If there are releases found, let's try first to fetch one with no
- // 'version_extra'. Otherwise, use all.
+ // If there are recommended releases (no 'version_extra' elements), then use
+ // only recommended releases. Otherwise, use all; in this case, the
+ // recommended release defaults to the latest published release with the
+ // right recommended major version number.
+ $recommended_releases = array();
if (!empty($releases)) {
- $stable_releases = array();
foreach ($releases as $one_release) {
if (!array_key_exists('version_extra', $one_release)) {
- $stable_releases[] = $one_release;
+ $recommended_releases[] = $one_release;
}
}
- if (!empty($stable_releases)) {
- $releases = $stable_releases;
- }
+ }
+ if (!empty($recommended_releases)) {
+ $releases = $recommended_releases;
+ }
+ $release_type = 'recommended';
+ if ($dev) {
+ $releases = @$xml->xpath("/project/releases/release[status='published'][version_extra='dev']");
+ $release_type = 'development';
}
if (empty($releases)) {
- drush_print(dt('There are no releases for project !project.', array('!project' => $request['name'])));
+ drush_print(dt('There is no !type release for project !project.', array('!type' => $release_type, '!project' => $request['name'])));
return FALSE;
}
- // First published release is just the first value in $releases.
+ // First published release for the recommended major version is just the
+ // first value in $releases.
return (array)$releases[0];
}
function updatexml_determine_project_type($xml) {
$project_types = array(
'core' => 'Drupal core',
- 'profile' => 'Distributions',
+ 'profile' => 'Installation profiles',
'module' => 'Modules',
'theme' => 'Themes',
'theme engine' => 'Theme engines',
unset($data[$project_name]);
continue;
}
- // Discard projects with unknown installation path.
- if ($project_name != 'drupal' && !isset($projects[$project_name]['path'])) {
- unset($data[$project_name]);
- continue;
- }
// Allow to update disabled projects.
if (in_array($project['project_type'], array('module-disabled', 'theme-disabled'))) {
$data[$project_name]['project_type'] = substr($project['project_type'], 0, strpos($project['project_type'], '-'));
}
- // Add some info from the project to $data.
- $data[$project_name] += array(
- 'path' => $projects[$project_name]['path'],
- 'label' => $projects[$project_name]['label'],
- );
+ // Set project label (title + name).
+ $data[$project_name]['label'] = $projects[$project_name]['label'];
+ // Set 'includes' key to all extensions. By default it only contain enabled
+ // extensions and _pm_get_project_path() needs all of them.
+ $data[$project_name]['includes'] = drupal_map_assoc($projects[$project_name]['extensions']);
// Store all releases, not just the ones selected by update.module.
$data[$project_name]['releases'] = $available[$project_name]['releases'];
}
+ $data = _pm_get_project_path($data, 'includes');
return $data;
}
unset($data[$project_name]);
continue;
}
- // Discard projects with unknown installation path.
- if ($project_name != 'drupal' && !isset($projects[$project_name]['path'])) {
- unset($data[$project_name]);
- continue;
- }
// Allow to update disabled projects.
if (in_array($project['project_type'], array('disabled-module', 'disabled-theme'))) {
$data[$project_name]['project_type'] = substr($project['project_type'], strpos($project['project_type'], '-') + 1);
}
- // Add some info from the project to $data.
- $data[$project_name] += array(
- 'path' => $projects[$project_name]['path'],
- 'label' => $projects[$project_name]['label'],
- );
+ // Set project label (title + name).
+ $data[$project_name]['label'] = $projects[$project_name]['label'];
+ // Set 'includes' key to all extensions. By default it only contain enabled
+ // extensions and _pm_get_project_path() needs all of them.
+ $data[$project_name]['includes'] = drupal_map_assoc($projects[$project_name]['extensions']);
}
+ $data = _pm_get_project_path($data, 'includes');
return $data;
}
// If specific project updates were requested then remove releases for all
// others.
- $requested = func_get_args();
- if (!empty($requested)) {
+ if (!empty($requests)) {
foreach ($updateable as $name => $project) {
if (!isset($requests[$name])) {
unset($updateable[$name]);
$skip_list[] = $item;
unset($items_to_test[$item]);
}
- elseif (is_link($item)) {
- $skip_list[] = $item;
- unset($items_to_test[$item]);
- }
}
$project['skip_list'] = $skip_list;
class DrupalServer extends HTTPServer {
// We pass in variables, rather than querying options here, to allow this to
// potentially be used in other commands.
- public $path, $debug, $env, $site;
+ public $site, $path, $conf_inject, $user, $watchdog, $debug, $first_request_complete;
/**
* This is the equivalent of .htaccess, passing requests to files if they
* of CGI environment variables here.
*/
function route_request($request) {
- $cgi_env = $this->env;
+ $cgi_env = array();
+
+ // We pass in the effective base_url to our auto_prepend_script via the cgi
+ // environment. This allows Drupal to generate working URLs to this http
+ // server, whilst finding the correct multisite from the HTTP_HOST header.
+ $cgi_env['RUNSERVER_BASE_URL'] = 'http://' . $this->addr . ':' . $this->port;
+
+ // We pass in an array of $conf overrides using the same approach.
+ // By default we set drupal_http_request_fails to FALSE, as the httpserver
+ // is unable to process simultaneous requests on some systems.
+ // This is available as an option for developers to pass in their own
+ // favorite $conf overrides (e.g. disabling css aggregation).
+ $conf_inject = $this->conf_inject;
+ $cgi_env['RUNSERVER_CONF'] = urlencode(serialize($conf_inject));
+
+ // We pass in the specified user (if set) - should be a fully loaded user
+ // object. This will automatically log this user in the browser during the
+ // first request (but not subsequent requests, to allow logging out).
+ if (!empty($this->user) && $this->user->uid && $this->first_request_complete !== TRUE) {
+ $this->first_request_complete = TRUE;
+ $cgi_env['RUNSERVER_USER'] = urlencode(serialize($this->user));
+ }
// Handle static files and php scripts accessed directly
$uri = $request->uri;
* Override get started event.
*/
function listening() {
+ drush_print(dt('HTTP server listening on !addr, port !port (see http://!hostname:!port/), serving site !site...', array('!addr' => $this->addr, '!hostname' => $this->hostname, '!port' => $this->port, '!site' => $this->site)));
if (!empty($this->path)) {
drush_start_browser($this->path);
}
*/
function request_done($request) {
drush_print(trim($this->get_log_line($request), "\n"));
-
+ $headers = $request->response->headers;
+ if ($this->watchdog && isset($headers['X-Runserver-Watchdog'])) {
+ $results = unserialize(urldecode($headers['X-Runserver-Watchdog']));
+ foreach ($results as $result) {
+ $result = (object)$result;
+ $result->uid = $result->user->uid;
+ $result = core_watchdog_format_result($result, TRUE);
+ drush_print("Watchdog: {$result->date} ({$result->severity}, {$result->type}, {$result->user->name}) {$result->message}", 2);
+ }
+ }
if ($this->debug) {
drush_print_r($request);
}
// We set the base_url so that Drupal generates correct URLs for runserver
// (e.g. http://127.0.0.1:8888/...), but can still select and serve a specific
// site in a multisite configuration (e.g. http://mysite.com/...).
-$base_url = runserver_env('RUNSERVER_BASE_URL');
+$base_url = $_SERVER['RUNSERVER_BASE_URL'];
-// Complete $_GET['q'] for Drupal 6 with built in server
-// - this uses the Drupal 7 method.
-if (!isset($_GET['q']) && isset($_SERVER['REQUEST_URI'])) {
- // This request is either a clean URL, or 'index.php', or nonsense.
- // Extract the path from REQUEST_URI.
- $request_path = strtok($_SERVER['REQUEST_URI'], '?');
- $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/'));
- // Unescape and strip $base_path prefix, leaving q without a leading slash.
- $_GET['q'] = substr(urldecode($request_path), $base_path_len + 1);
-}
-
-// We hijack filter_init (which core filter module does not implement) as
-// a convenient place to affect early changes.
-if (!function_exists('filter_init')) {
+// We hijack system_boot (which core system module does not implement) as
+// a convenient place to affect mid-bootstrap changes.
+if (!function_exists('system_boot')) {
// Check function_exists as a safety net in case it is added in future.
- function filter_init() {
+ function system_boot() {
global $conf, $user;
// Inject values into the $conf array - will apply to all sites.
// This can be a useful place to apply generic development settings.
- $conf_inject = unserialize(urldecode(runserver_env('RUNSERVER_CONF')));
+ $conf_inject = unserialize(urldecode($_SERVER['RUNSERVER_CONF']));
// Merge in the injected conf, overriding existing items.
$conf = array_merge($conf, $conf_inject);
-
- // Log in user if needed.
- if (isset($_GET['login'])) {
- $uid = runserver_env('RUNSERVER_USER');
- if (!empty($uid) && $user->uid !== $uid) {
- // If a user was provided, log in as this user.
- $user = user_load($uid);
- if (function_exists('drupal_session_regenerate')) {
- // Drupal 7
- drupal_session_regenerate();
- }
- else {
- // Drupal 6
- sess_regenerate();
- }
+ if (isset($_SERVER['RUNSERVER_USER'])) {
+ // If a user was provided, log in as this user.
+ $user = unserialize(urldecode($_SERVER['RUNSERVER_USER']));
+ if (function_exists('drupal_session_regenerate')) {
+ // Drupal 7
+ drupal_session_regenerate();
+ }
+ else {
+ // Drupal 6
+ sess_regenerate();
}
- // Refresh the page (in case access denied has been called already).
- drupal_goto($_GET['q']);
}
}
}
if (!function_exists('system_watchdog')) {
// Check function_exists as a safety net in case it is added in future.
function system_watchdog($log_entry = array()) {
- $message = strtr('Watchdog: !message | severity: !severity | type: !type | uid: !uid | !ip | !request_uri | !referer | !link', array(
- '!message' => strip_tags(!isset($log_entry['variables']) ? $log_entry['message'] : strtr($log_entry['message'], $log_entry['variables'])),
- '!severity' => $log_entry['severity'],
- '!type' => $log_entry['type'],
- '!ip' => $log_entry['ip'],
- '!request_uri' => $log_entry['request_uri'],
- '!referer' => $log_entry['referer'],
- '!uid' => $log_entry['user']->uid,
- '!link' => strip_tags($log_entry['link']),
- ));
- error_log($message);
+ static $logs = array();
+ $logs[] = $log_entry;
+ $data = urlencode(serialize($logs));
+ if (function_exists('drupal_add_http_header')) {
+ // Drupal 7
+ drupal_add_http_header('X-Runserver-Watchdog', $data);
+ }
+ else {
+ // Drupal 6
+ drupal_set_header('X-Runserver-Watchdog: ' . $data);
+ }
}
}
-
-function runserver_env($key) {
- if (isset($_SERVER[$key])) {
- return $_SERVER[$key];
- }
- else {
- return getenv($key);
- }
-}
\ No newline at end of file
'addr:port/path' => 'Host IP address and port number to bind to and path to open in web browser. Format is addr:port/path, default 127.0.0.1:8888, all elements optional. See examples for shorthand.',
),
'options' => array(
- 'server' => 'Which http server to use - either: "cgi" for a CGI based httpserver (default, requires php 5.3 and php-cgi binary) or "builtin" for php 5.4 built in http server.',
'php-cgi' => 'Name of the php-cgi binary. If it is not on your current $PATH you should include the full path. You can include command line parameters to pass into php-cgi.',
- 'variables' => 'Key-value array of variables to override in the $conf array for the running site. By default disables drupal_http_request_fails to avoid errors on Windows (which supports only one connection at a time). Comma delimited list of name=value pairs (or array in drushrc).',
+ 'conf-inject' => 'Key-value array of variables to override in the $conf array for the running site. By default disables drupal_http_request_fails to avoid errors on Windows (which supports only one connection at a time). Note that as this is a key-value array, it can only be specified in a drushrc or alias file, and not on the command line.',
'default-server' => 'A default addr:port/path to use for any values not specified as an argument.',
- 'user' => 'If opening a web browser, automatically log in as this user (user ID or username).',
- 'browser' => 'If opening a web browser, which browser to user (defaults to operating system default).',
+ 'user' => 'If opening a web browser, automatically log in as this user (user ID or username)',
+ 'watchdog' => 'Collect and integrate watchdog messages from each request into the log',
'dns' => 'Resolve hostnames/IPs using DNS/rDNS (if possible) to determine binding IPs and/or human friendly hostnames for URLs and browser.',
),
'aliases' => array('rs'),
'drush rs --php-cgi=php5-cgi --dns localhost:8888/user' => 'Start runserver on localhost (using rDNS to determine binding IP), port 8888, and open /user in browser. Use "php5-cgi" as the php-cgi binary.',
'drush rs /' => 'Start runserver on default IP/port (127.0.0.1, port 8888), and open / in browser.',
'drush rs --default-server=127.0.0.1:8080/ -' => 'Use a default (would be specified in your drushrc) that starts runserver on port 8080, and opens a browser to the front page. Set path to a single hyphen path in argument to prevent opening browser for this session.',
- 'drush rs --server=builtin :9000/admin' => 'Start builtin php 5.4 runserver on 127.0.0.1, port 9000, and open /admin in browser. Note that you need a colon when you specify port and path, but no IP.',
+ 'drush rs --watchdog :9000/admin' => 'Start runserver on 127.0.0.1, port 9000, and open /admin in browser, including any watchdog messages for the session in the log. Note that you need a colon when you specify port and path, but no IP.',
),
);
return $items;
*/
function drush_core_runserver_validate() {
if (version_compare(PHP_VERSION, '5.3.0') < 0) {
- return drush_set_error('RUNSERVER_PHP53_VERSION', dt('The runserver command requires php 5.3, which could not be found.'));
+ return drush_set_error('RUNSERVER_PHP_VERSION', dt('The runserver command requires php 5.3, which could not be found.'));
}
- $php_cgi = drush_shell_exec('which ' . drush_get_option('php-cgi', 'php-cgi'));
- $builtin = version_compare(PHP_VERSION, '5.4.0') >= 0;
- $server = drush_get_option('server', FALSE);
- if (!$server) {
- // No server specified, try and find a valid server option, preferring cgi.
- if ($php_cgi) {
- $server = 'cgi';
- drush_set_option('server', 'cgi');
- }
- else if ($builtin) {
- $server = 'builtin';
- drush_set_option('server', 'builtin');
- }
- else {
- return drush_set_error('RUNSERVER_PHP_CGI54', dt('The runserver command requires either the php-cgi binary, or php 5.4 (or higher). Neither could not be found.'));
- }
- }
- else if ($server == 'cgi' && !$php_cgi) {
- // Validate user specified cgi server option.
- return drush_set_error('RUNSERVER_PHP_CGI', dt('The runserver command with the "cgi" server requires the php-cgi binary, which could not be found.'));
- }
- else if ($server == 'builtin' && !$builtin) {
- // Validate user specified builtin server option.
- return drush_set_error('RUNSERVER_PHP_CGI', dt('The runserver command with the "builtin" server requires php 5.4 (or higher), which could not be found.'));
- }
-
- // Update with detected configuration.
- $server = drush_get_option('server', FALSE);
- if ($server == 'cgi') {
- // Fetch httpserver cgi based server to our /lib directory, if needed.
- $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
- $httpserverfile = $lib . '/' . DRUSH_HTTPSERVER_DIR_BASE . substr(DRUSH_HTTPSERVER_VERSION, 0, 7) . '/httpserver.php';
- if (!drush_file_not_empty($httpserverfile)) {
- // Download and extract httpserver, and confirm success.
- drush_lib_fetch(DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION);
- if (!drush_file_not_empty($httpserverfile)) {
- // Something went wrong - the library is still not present.
- return drush_set_error('RUNSERVER_HTTPSERVER_LIB_NOT_FOUND', dt("The runserver command needs a copy of the httpserver library in order to function, and the attempt to download this file automatically failed. To continue you will need to download the package from !url, extract it into the !lib directory, such that httpserver.php exists at !httpserverfile.", array('!version' => DRUSH_HTTPSERVER_VERSION, '!url' => DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION, '!httpserverfile' => $httpserverfile, '!lib' => $lib)));
- }
- }
- }
-
- // Check we have a valid server.
- if (!in_array($server, array('cgi', 'builtin'))) {
- return drush_set_error('RUNSERVER_INVALID', dt('Invalid server specified.'));
+ if (!drush_shell_exec('which ' . drush_get_option('php-cgi', 'php-cgi'))) {
+ return drush_set_error('RUNSERVER_PHP_CGI', dt('The runserver command requires the php-cgi binary, which could not be found.'));
}
}
function drush_core_runserver($uri = NULL) {
global $user;
+ // Fetch httpserver to our /lib directory, if needed.
+ $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
+ $httpserverfile = $lib . '/' . DRUSH_HTTPSERVER_DIR_BASE . substr(DRUSH_HTTPSERVER_VERSION, 0, 7) . '/httpserver.php';
+ if (!drush_file_not_empty($httpserverfile)) {
+ // Download and extract httpserver, and confirm success.
+ drush_lib_fetch(DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION);
+ if (!drush_file_not_empty($httpserverfile)) {
+ // Something went wrong - the library is still not present.
+ return drush_set_error('RUNSERVER_HTTPSERVER_LIB_NOT_FOUND', dt("The runserver command needs a copy of the httpserver library in order to function, and the attempt to download this file automatically failed. To continue you will need to download the package from !url, extract it into the !lib directory, such that httpserver.php exists at !httpserverfile.", array('!version' => DRUSH_HTTPSERVER_VERSION, '!url' => DRUSH_HTTPSERVER_BASE_URL . DRUSH_HTTPSERVER_VERSION, '!httpserverfile' => $httpserverfile, '!lib' => $lib)));
+ }
+ }
+
+ // Include the library and our class that extends it.
+ require_once $httpserverfile;
+ require_once 'runserver-drupal.inc';
+
+ // We pass in the currently logged in user (if set via the --user option),
+ // which will automatically log this user in the browser during the first
+ // request.
+ if (drush_get_option('user', FALSE)) {
+ drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_LOGIN);
+ }
+
// Determine active configuration.
$drush_default = array(
'host' => '127.0.0.1',
}
drush_set_context('DRUSH_URI', 'http://' . $hostname . ':' . $uri['port']);
-
- // We pass in the currently logged in user (if set via the --user option),
- // which will automatically log this user in the browser during the first
- // request.
- if (drush_get_option('user', FALSE)) {
- drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_LOGIN);
- // Add a querystring parameter to indicate that this is the first page request, so that the user can be logged in.
- if (!empty($uri['path'])) {
- if (strpos($uri['path'], '?') === FALSE) {
- $uri['path'] .= '?login';
- }
- else {
- $uri['path'] .= '&login';
- }
- }
- }
-
// We delete any registered files here, since they are not caught by Ctrl-C.
_drush_delete_registered_files();
- // We pass in the effective base_url to our auto_prepend_script via the cgi
- // environment. This allows Drupal to generate working URLs to this http
- // server, whilst finding the correct multisite from the HTTP_HOST header.
- $env['RUNSERVER_BASE_URL'] = 'http://' . $addr . ':' . $uri['port'];
-
- // We pass in an array of $conf overrides using the same approach.
- // By default we set drupal_http_request_fails to FALSE, as the httpserver
- // is unable to process simultaneous requests on some systems.
- // This is available as an option for developers to pass in their own
- // favorite $conf overrides (e.g. disabling css aggregation).
- $current_override = drush_get_option_list('variables', array('drupal_http_request_fails' => FALSE));
- foreach ($current_override as $name => $value) {
- if (is_numeric($name) && (strpos($value, '=') !== FALSE)) {
- list($name, $value) = explode('=', $value, 2);
- }
- $override[$name] = $value;
- }
- $env['RUNSERVER_CONF'] = urlencode(serialize($override));
-
- // We pass in the specified user ID (if set). This will automatically log
- // this user in the browser during the first request (but not subsequent
- // requests, to allow logging out).
- $user_message = '';
- if ($user->uid) {
- $env['RUNSERVER_USER'] = $user->uid;
- $user_message = ', logged in as ' . $user->name;
- }
-
- drush_print(dt('HTTP server listening on !addr, port !port (see http://!hostname:!port!path), serving site !site!user...', array('!addr' => $addr, '!hostname' => $hostname, '!port' => $uri['port'], '!path' => $uri['path'], '!site' => drush_get_context('DRUSH_DRUPAL_SITE', 'default'), '!user' => $user_message)));
- if (drush_get_option('server', FALSE) == 'builtin') {
- // Start php 5.4 builtin server.
- // Store data used by runserver-prepend.php in the shell environment.
- foreach ($env as $key => $value) {
- putenv($key . '=' . $value);
- }
- if (!empty($uri['path'])) {
- // Start a browser if desired. Include a 2 second delay to allow the server to come up.
- drush_start_browser($uri['path'], 2);
- }
- // Start the server using 'php -S'.
- $php = drush_get_option('php');
- drush_shell_exec_interactive($php . ' -S ' . $addr . ':' . $uri['port'] . ' --define auto_prepend_file="' . DRUSH_BASE_PATH . '/commands/runserver/runserver-prepend.php"');
- }
- else {
- // Include the library and our class that extends it.
- $lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
- $httpserverfile = $lib . '/' . DRUSH_HTTPSERVER_DIR_BASE . substr(DRUSH_HTTPSERVER_VERSION, 0, 7) . '/httpserver.php';
- require_once $httpserverfile;
- require_once 'runserver-drupal.inc';
- // Create a new httpserver instance and start it running.
- $server = new DrupalServer(array(
- 'addr' => $addr,
- 'port' => $uri['port'],
- 'path' => $uri['path'],
- 'hostname' => $hostname,
- 'site' => drush_get_context('DRUSH_DRUPAL_SITE', 'default'),
- 'server_id' => 'Drush runserver',
- 'php_cgi' => drush_get_option('php-cgi', 'php-cgi') . ' --define auto_prepend_file="' . DRUSH_BASE_PATH . '/commands/runserver/runserver-prepend.php"',
- 'env' => $env,
- 'debug' => drush_get_context('DRUSH_DEBUG'),
- ));
- $server->run_forever();
- }
+ // Create a new server instance and start it running.
+ $server = new DrupalServer(array(
+ 'addr' => $addr,
+ 'port' => $uri['port'],
+ 'path' => $uri['path'],
+ 'hostname' => $hostname,
+ 'site' => drush_get_context('DRUSH_DRUPAL_SITE', 'default'),
+ 'server_id' => 'Drush runserver',
+ 'php_cgi' => drush_get_option('php-cgi', 'php-cgi') . ' --define auto_prepend_file="' . DRUSH_BASE_PATH . '/commands/runserver/runserver-prepend.php"',
+ 'conf_inject' => drush_get_option('conf-inject', array('drupal_http_request_fails' => FALSE)),
+ 'user' => $user,
+ 'watchdog' => drush_get_option('watchdog', FALSE),
+ 'debug' => drush_get_context('DRUSH_DEBUG'),
+ ));
+ $server->run_forever();
}
/**
unset($uri['host']);
}
return $uri;
-}
+}
\ No newline at end of file
if (drush_drupal_major_version() >= 7) {
$options['target'] = 'The name of a target within the specified database.';
}
- $db_url['db-url'] = array(
- 'description' => 'A Drupal 6 style database URL. Only required for initial install - not re-install.',
- 'example-value' => 'mysql://root:pass@127.0.0.1/db',
- );
+ $db_url['db-url'] = 'A Drupal 6 style database URL. Useful when you don\'t have a configured Drupal instance.';
$items['sql-drop'] = array(
'description' => 'Drop all tables in a given database.',
'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
'options' => array(
'yes' => 'Skip confirmation and proceed.',
- 'result-file' => array(
- 'description' => 'Save to a file. The file should be relative to Drupal root. Recommended.',
- 'example-value' => '/path/to/file',
- ),
+ 'result-file' => 'Save to a file. The file should be relative to Drupal root. Recommended.',
) + $options + $db_url,
'topics' => array('docs-policy'),
);
'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
'examples' => array(
'drush sql-dump --result-file=../18.sql' => 'Save SQL dump to the directory above Drupal root.',
- 'drush sql-dump --skip-tables-key=common' => 'Skip standard tables. @see example.drushrc.php',
+ 'drush sql-dump --skip-tables-key=common' => 'Skip standard tables. @see example.drushrc.com',
),
'options' => array(
- 'result-file' => array(
- 'description' => 'Save to a file. The file should be relative to Drupal root. If --result-file is provided with no value, then date based filename will be created under ~/drush-backups directory.',
- 'example-value' => '/path/to/file',
- 'value' => 'optional',
- ),
+ 'result-file' => 'Save to a file. The file should be relative to Drupal root. If --result-file is provided with no value, then date based filename will be created under ~/drush-backups directory.',
'skip-tables-key' => 'A key in the $skip_tables array. @see example.drushrc.php. Optional.',
'structure-tables-key' => 'A key in the $structure_tables array. @see example.drushrc.php. Optional.',
'tables-key' => 'A key in the $tables array. Optional.',
'tables-list' => 'A comma-separated list of tables to transfer. Optional.',
'ordered-dump' => 'Use this option to output ordered INSERT statements in the sql-dump.Useful when backups are managed in a Version Control System. Optional.',
- 'create-db' => array('hidden' => TRUE, 'description' => 'Omit DROP TABLE statements. Postgres and Oracle only. Used by sql-sync, since including the DROP TABLE statements interfere with the import when the database is created.'),
- 'data-only' => 'Dump data without statements to create any of the schema.',
+ 'create-db' => 'Wipe existing tables.',
+ 'data-only' => 'Omit CREATE TABLE statements. Postgres only.',
'ordered-dump' => 'Order by primary key and add line breaks for efficient diff in revision control. Also, faster rsync. Slows down the dump. Mysql only.',
'gzip' => 'Compress the dump using the gzip program which must be in your $PATH.',
) + $options + $db_url,
'drush sql-query "SELECT * FROM users WHERE uid=1"' => 'Browse user record. Table prefixes, if used, must be added to table names by hand.',
'drush sql-query --db-prefix "SELECT * FROM {users} WHERE uid=1"' => 'Browse user record. Table prefixes are honored. Caution: curly-braces will be stripped from all portions of the query.',
'`drush sql-connect` < example.sql' => 'Import sql statements from a file into the current database.',
- 'drush sql-query --file=example.sql' => 'Alternate way to import sql statements from a file.',
+ 'drush sql-query --input-file=example.sql' => 'Alternate way to import sql statements from a file.',
),
'arguments' => array(
'query' => 'An SQL query. Ignored if \'file\' is provided.',
),
'options' => array(
- 'result-file' => array(
- 'description' => 'Save to a file. The file should be relative to Drupal root. Optional.',
- 'example-value' => '/path/to/file',
- ),
- 'file' => 'Path to a file containing the SQL to be run.',
+ 'result-file' => 'Save to a file. The file should be relative to Drupal root. Optional.',
+ 'input-file' => 'Path to a file containing the SQL to be run.',
'extra' => 'Add custom options to the mysql command.',
'db-prefix' => 'Enable replacement of braces in your query.',
) + $options + $db_url,
'sub-options' => array(
'sanitize' => array(
'sanitize-password' => 'The password to assign to all accounts in the sanitization operation, or "no" to keep passwords unchanged. Default is "password".',
- 'sanitize-email' => 'The pattern for test email addresses in the sanitization operation, or "no" to keep email addresses unchanged. May contain replacement patterns %uid, %mail or %name. Default is "user+%uid@localhost".',
+ 'sanitize-email' => 'The username for test email addresses in the sanitization operation, or "no" to keep email addresses unchanged. May contain replacement patterns %uid, %mail or %name. Default is "user+%uid@localhost".',
'confirm-sanitizations' => 'Prompt yes/no after importing the database, but before running the sanitizations',
),
),
- 'topics' => array('docs-aliases', 'docs-policy', 'docs-example-sync-via-http', 'docs-example-sync-extension'),
+ 'topics' => array('docs-aliases', 'docs-policy'),
);
if (drush_drupal_major_version() >= 7) {
'hidden' => TRUE,
'options' => array(
'sanitize-password' => 'The password to assign to all accounts in the sanitization operation, or "no" to keep passwords unchanged. Default is "password".',
- 'sanitize-email' => 'The pattern for test email addresses in the sanitization operation, or "no" to keep email addresses unchanged. May contain replacement patterns %uid, %mail or %name. Default is "user+%uid@localhost".',
+ 'sanitize-email' => 'The username for test email addresses in the sanitization operation, or "no" to keep email addresses unchanged. May contain replacement patterns %uid, %mail or %name. Default is "user+%uid@localhost".',
) + $db_url,
'aliases' => array('sqlsan'),
);
case 'sqlsrv':
$command = 'sqlcmd';
break;
- case 'oracle':
- // use rlwrap if available for readline support
- if ($handle = popen('rlwrap -v', 'r')) {
- $command = 'rlwrap sqlplus';
- pclose($handle);
- }
- else {
- $command = 'sqlplus';
- }
- break;
}
$command .= _drush_sql_get_credentials($db_spec);
return $command;
drush_sql_bootstrap_further();
list($exec, $file) = drush_sql_dump();
// Avoid the php memory of the $output array in drush_shell_exec().
-
if (!$return = drush_op_system($exec)) {
if ($file) {
drush_log(dt('Database dump saved to !path', array('!path' => $file)), 'success');
if ($file) {
if ($file === TRUE) {
// User did not pass a specific value for --result-file. Make one.
- $backup = drush_include_engine('version_control', 'backup');
+ $backup = drush_include_engine('version-control', 'backup');
$backup_dir = $backup->prepare_backup_dir($db_spec['database']);
if (empty($backup_dir)) {
$backup_dir = "/tmp";
}
switch (_drush_sql_get_scheme($db_spec)) {
- case 'mysqli':
case 'mysql':
$exec = 'mysqldump';
if ($file) {
}
break;
case 'sqlsrv':
- // Change variable '$file' by reference in order to get drush_log() to report.
- if (!$file) {
- $file = $db_spec['database'] . '_' . date('Ymd_His') . '.bak';
- }
- $exec = "sqlcmd -U \"" . $db_spec['username'] . "\" -P \"" . $db_spec['password'] . "\" -S \"" . $db_spec['host'] . "\" -Q \"BACKUP DATABASE [" . $db_spec['database'] . "] TO DISK='" . $file . "'\"";
- break;
- case 'oracle':
- $create_db = drush_get_option('create-db');
- $exec = 'exp ' . _drush_sql_get_credentials($db_spec);
- // Change variable '$file' by reference in order to get drush_log() to report.
- if (!$file) {
- $file = $db_spec['username'] . '.dmp';
- }
- $exec .= ' file=' . $file;
-
- if (!empty($tables))
- $exec .= ' tables="(' . implode(',', $tables) . ')"';
- else
- $exec .= ' owner=' . $db_spec['username'];
-
+ $exec = "echo 'sqlsrv sql-dump not yet supported'";
break;
}
if (drush_get_option('gzip')) {
if ($file) {
- $escfile = drush_escapeshellarg($file);
- if (drush_get_context('DRUSH_AFFIRMATIVE')) {
- // Gzip the result-file without Gzip confirmation
- $exec .= " && gzip -f $escfile";
- $file .= '.gz';
- }
- else {
- // Gzip the result-file
- $exec .= " && gzip $escfile";
- $file .= '.gz';
- }
+ // Gzip the result-file
+ $exec .= "; gzip $file";
+ $file .= '.gz';
}
else {
// gzip via pipe since user has not specified a file.
* Command callback. Executes the given SQL query on the Drupal database.
*/
-function drush_sql_query($query = NULL) {
+function drush_sql_query($query) {
drush_sql_bootstrap_further();
$filename = drush_get_option('file', NULL);
- // Enable prefix processing when db-prefix option is used.
- if (drush_get_option('db-prefix')) {
- drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_DATABASE);
- }
return _drush_sql_query($query, NULL, $filename);
}
* A path to a file containing the SQL to be executed.
*/
function _drush_sql_query($query, $db_spec = NULL, $filename = NULL) {
- $suffix = '';
$scheme = _drush_sql_get_scheme($db_spec);
// Inject table prefixes as needed.
}
}
- // is this an oracle query
- if ($scheme == 'oracle') {
- $query = drush_sql_format_oracle($query);
- $suffix = '.sql';
- }
-
// Convert mysql 'show tables;' query into something pgsql understands
if (($scheme == 'pgsql') && ($query == 'show tables;')) {
$query = drush_sql_show_tables_pgsql();
// Save $query to a tmp file if needed. We will redirect it in.
if (!$filename) {
- $filename = drush_save_data_to_temp_file($query, $suffix);
+ $filename = drush_save_data_to_temp_file($query);
}
$exec = drush_sql_build_exec($db_spec, $filename);
if ($output_file = drush_get_option('result-file')) {
if (drush_get_context('DRUSH_SIMULATE')) {
drush_print('sql-query: ' . $query);
}
- if ($exec) {
- $return = drush_op_system($exec) == 0;
- }
- else {
- $return = drush_set_error('DRUSH_SQL_NO_QUERY', 'No query provided');
- }
+ $return = drush_op_system($exec) == 0;
return $return;
}
function _drush_sql_drop($db_spec = NULL) {
// TODO: integrate with _drush_sql_get_table_list?
- $suffix = '';
$scheme = _drush_sql_get_scheme($db_spec);
switch ($scheme) {
case 'pgsql':
case 'sqlsrv':
$query = 'SELECT TABLE_NAME FROM information_schema.tables';
break;
- case 'oracle':
- $query = "SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME NOT IN ('BLOBS','LONG_IDENTIFIERS')";
- $suffix = '.sql';
- break;
default:
$query = 'SHOW TABLES;';
}
- $filename = drush_save_data_to_temp_file($query, $suffix);
+ $filename = drush_save_data_to_temp_file($query);
$exec = drush_sql_build_exec($db_spec, $filename);
// Actually run this prep query no matter if in SIMULATE.
elseif (($databases = drush_get_option('databases')) && (array_key_exists($database, $databases)) && (array_key_exists($target, $databases[$database]))) {
return $databases[$database][$target];
}
- elseif (drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION)) {
+ elseif (drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION)) {
switch (drush_drupal_major_version()) {
case 6:
if ($url = isset($GLOBALS['db_url']) ? $GLOBALS['db_url'] : drush_get_option('db-url', NULL)) {
if (!isset($dump_dir)) {
// If this is a remote site, try to find a writable tmpdir.
if (isset($site_record['remote-host'])) {
- $result = drush_invoke_process($site_record, 'ev', array('drush_print(drush_find_tmp())'), array(), array('integrate' => FALSE));
+ $result = drush_invoke_process($site_record, 'ev', array('drush_print(drush_find_tmp())'));
// If the call to invoke process does not work for some reason (e.g. drush not
// installed on the target machine), then we will just presume that the tmp dir is '/tmp'.
if (!array_key_exists('output', $result) || empty($result['output'])) {
$host = empty($db_spec['server']) ? '.\SQLEXPRESS' : $db_spec['server'];
return ' -S ' . $host . ' -d ' . $database . ' -U ' . $db_spec['username'] . ' -P ' . $db_spec['password'];
break;
-
-
- case 'oracle':
- // Return an Oracle connection string
- return ' ' . $db_spec['username'] .'/' . $db_spec['password'] . ($db_spec['host']=='USETNS' ? '@' . $db_spec['database'] : '@//' . $db_spec['host'] . ':' . ($db_spec['port'] ? $db_spec['port'] : '1521') . '/' . $db_spec['database']);
- break;
-
}
// Turn each parameter into a valid parameter string.
return "select tablename from pg_tables where schemaname='public';";
}
-// Format queries to work with Oracle and SqlPlus
-function drush_sql_format_oracle($query) {
-
- // remove trailing semicolon from query if we have it
- $query = preg_replace('/\;$/', '', $query);
-
- // some sqlplus settings
- $settings[] = "set TRIM ON";
- $settings[] = "set FEEDBACK OFF";
- $settings[] = "set UNDERLINE OFF";
- $settings[] = "set PAGES 0";
- $settings[] = "set PAGESIZE 50000";
-
- // are we doing a describe ?
- if (!preg_match('/^ *desc/i', $query)) {
- $settings[] = "set LINESIZE 32767";
- }
-
- // are we doing a show tables ?
- if (preg_match('/^ *show tables/i', $query)) {
- $settings[] = "set HEADING OFF";
- $query = "select object_name from user_objects where object_type='TABLE' order by object_name asc";
- }
-
- // create settings string
- $sqlp_settings = implode("\n", $settings)."\n";
-
- // important for sqlplus to exit correctly
- return "${sqlp_settings}${query};\nexit;\n";
-}
-
-
/*
* Drop all tables (if DB exists) or CREATE target database.
*
case 'sqlsrv':
// TODO: untested, but the gist is here.
$sql = "if db_id('$database') IS NOT NULL print 1";
- drush_shell_exec("$connect_no_db -Q \"$sql\"");
+ drush_shell_exec("$connect_no_db -q \"$sql\"");
$output = drush_shell_exec_output();
return $output[0] == 1;
}
function drush_sql_build_exec($db_spec, $filepath) {
$scheme = _drush_sql_get_scheme($db_spec);
- $exec = '';
switch ($scheme) {
case 'mysql':
$exec = 'mysql';
$exec .= _drush_sql_get_credentials($db_spec);
$exec .= ' ' . drush_get_option('extra');
- $exec .= " < " . drush_escapeshellarg($filepath);
+ $exec .= " < $filepath";
break;
case 'pgsql':
$exec = 'psql -q ';
$exec .= _drush_sql_get_credentials($db_spec);
$exec .= ' ' . (drush_get_option('extra') ? drush_get_option('extra') : "--no-align --field-separator='\t' --pset footer=off");
- $exec .= " --file " . drush_escapeshellarg($filepath);
+ $exec .= " --file $filepath";
break;
case 'sqlite':
$exec = 'sqlite3';
$exec .= ' ' . drush_get_option('extra');
$exec .= _drush_sql_get_credentials($db_spec);
- $exec .= " < " . drush_escapeshellarg($filepath);
+ $exec .= " < $filepath";
break;
case 'sqlsrv':
$exec = 'sqlcmd';
$exec .= _drush_sql_get_credentials($db_spec);
$exec .= ' -h -1 -i "' . $filepath . '"';
break;
- case 'oracle':
- $exec = 'sqlplus';
- $exec .= ' ' . drush_get_option('extra');
- $exec .= _drush_sql_get_credentials($db_spec);
- $exec .= " @" . drush_escapeshellarg($filepath);
- break;
}
return $exec;
}
// After preflight, get source and destination settings
$source_settings = drush_sitealias_get_record($source);
$destination_settings = drush_sitealias_get_record($destination);
- // Apply command-specific options.
- drush_sitealias_command_default_options($source_settings, 'source-');
- drush_sitealias_command_default_options($destination_settings, 'target-');
// Insure that we have database records for the source and destination
// alias records. sitealias_get_databases_from_record will cache the
$email_map = array('%uid' => "', uid, '", '%mail' => "', replace(mail, '@', '_'), '", '%name' => "', replace(name, ' ', '_'), '");
$newmail = "concat('" . str_replace(array_keys($email_map), array_values($email_map), $newemail) . "')";
}
- $user_table_updates[] = "mail = $newmail";
- }
- else {
- $user_table_updates[] = "mail = '$newemail'";
}
+ $user_table_updates[] = "mail = $newmail";
$message_list[] = 'email addresses';
}
}
// If the 'cache' option is set, then we will set the no-dump option iff the
// target file exists and its modification date is less than "cache" hours.
- // If --no-sync or --no-dump are already set, then this check is unnecessary.
- if (isset($cache) && !isset($no_sync) && !isset($no_dump)) {
+ if (isset($cache)) {
if (file_exists($local_file) && (filesize($local_file) > 0)) {
if ((time() - filemtime($local_file)) < ($cache * 60 * 60)) {
drush_log(dt('Modification time of local dump file !file is less than !cache hours old. Use the --no-cache option to force a refresh.', array('!file' => $local_file, '!cache' => $cache)), 'warning');
// from different users sharing the same dump file.
if (!drush_is_windows($source_os) && (!$source_settings['dump-is-temp'])) {
$source_intermediate = $source_dump . '-' . date("U");
- $mv_intermediate = '&& mv -f ' . $source_intermediate . ' ' . $source_dump;
+ $mv_intermediate = '; mv -f ' . $source_intermediate . ' ' . $source_dump;
}
list($dump_exec, $dump_file) = drush_sql_build_dump_command($table_selection, $source_db_url, $source_intermediate);
$dump_exec .= $mv_intermediate;
if (!isset($no_sync)) {
// If the source file is a temporary file, then we will have rsync
// delete it for us (remove-source-files option set above).
- if (!drush_core_call_rsync($source_remote_user . $source_at . $source_db_url['remote-host'] . ':' . $source_dump, drush_correct_absolute_path_for_exec($local_file, "RSYNC"), $source_rsync_options)) {
+ if (!drush_core_call_rsync($source_remote_user . $source_at . $source_db_url['remote-host'] . ':' . $source_dump, $local_file, $source_rsync_options)) {
return FALSE;
}
}
$target_remote_pass = array_key_exists('remote-pass', $destination_settings) ? ':' . $destination_settings['remote-pass'] : '';
}
- if (!drush_core_call_rsync(drush_correct_absolute_path_for_exec($local_file, "RSYNC"), $target_remote_user . $target_at . $target_db_url['remote-host'] . ':' . $target_dump, $target_rsync_options)) {
+ if (!drush_core_call_rsync($local_file, $target_remote_user . $target_at . $target_db_url['remote-host'] . ':' . $target_dump, $target_rsync_options)) {
return FALSE;
}
- $import_exec = $pre_import_commands . $import_command . ' < ' . drush_escapeshellarg($target_dump, $target_os);
+ $import_exec = $pre_import_commands . $import_command . ' < ' . $target_dump;
// Delete the remote target file if it is a temporary file.
if (!drush_is_windows($target_os) && $destination_settings['dump-is-temp']) {
$import_exec .= '; rm -f ' . drush_escapeshellarg($target_dump, $target_os);
// TODO: make sure that the remote tmp file is deleted on remote Windows machines.
}
else {
- $import_exec = $pre_import_commands . $import_command . ' < ' . drush_escapeshellarg($local_file);
+ $import_exec = $pre_import_commands . $import_command . ' < ' . $local_file;
}
$import_exec = _drush_backend_generate_command($destination_settings, $import_exec);
),
'required-arguments' => TRUE,
'options' => array(
- 'password' => array(
- 'description' => 'The new password for the account.',
- 'required' => TRUE,
- 'example-value' => 'foo',
- ),
+ 'password' => array('description' => 'The new password for the account.'),
),
'examples' => array(
'drush user-password someuser --password="correct horse battery staple"' =>
return $items;
}
+// Implementation of hook_drush_init().
+function user_drush_init() {
+ $command_info = drush_get_command();
+ $command = $command_info['command'];
+ $needs_parse_args = array('user-block', 'user-unblock', 'user-add-role', 'user-remove-role');
+ if (in_array($command, $needs_parse_args)) {
+ // parse args and call drush_set_option for --uids
+ $users = array();
+ foreach (array('uid', 'name', 'mail' ) as $user_attr) {
+ if ($arg = drush_get_option($user_attr)) {
+ foreach(explode(',', $arg) as $search) {
+ $uid_query = FALSE;
+ switch ($user_attr) {
+ case 'uid':
+ if (drush_drupal_major_version() >= 7) {
+ $uid_query = db_query("SELECT uid FROM {users} WHERE uid = :uid", array(':uid' => $search));
+ }
+ else {
+ $uid_query = db_query("SELECT uid FROM {users} WHERE uid = %d", $search);
+ }
+ break;
+ case 'name':
+ if (drush_drupal_major_version() >= 7) {
+ $uid_query = db_query("SELECT uid FROM {users} WHERE name = :name", array(':name' => $search));
+ }
+ else {
+ $uid_query = db_query("SELECT uid FROM {users} WHERE name = '%s'", $search);
+ }
+ break;
+ case 'mail':
+ if (drush_drupal_major_version() >= 7) {
+ $uid_query = db_query("SELECT uid FROM {users} WHERE mail = :mail", array(':mail' => $search));
+ }
+ else {
+ $uid_query = db_query("SELECT uid FROM {users} WHERE mail = '%s'", $search);
+ }
+ break;
+ }
+ if ($uid_query !== FALSE) {
+ if ($uid = drush_db_result($uid_query)) {
+ $users[] = $uid;
+ }
+ else {
+ drush_set_error("Could not find a uid for $user_attr = $search");
+ }
+ }
+ }
+ }
+ }
+ if (!empty($users)) {
+ drush_set_option('uids', $users);
+ }
+ }
+}
+
/**
* Prints information about the specified user(s).
*/
function drush_user_information($users) {
- $uids = _drush_user_get_users_from_arguments($users);
- foreach($uids as $uid) {
- _drush_user_print_info($uid);
+ $users = explode(',', $users);
+ foreach($users as $user) {
+ $uid = _drush_user_get_uid($user);
+ if ($uid !== FALSE) {
+ _drush_user_print_info($uid);
+ }
}
}
* Block the specified user(s).
*/
function drush_user_block($users = '') {
- $uids = _drush_user_get_users_from_options_and_arguments($users);
+ $uids = drush_get_option('uids');
+ if ($users !== '') {
+ $users = explode(',', $users);
+ foreach($users as $user) {
+ $uid = _drush_user_get_uid($user);
+ if ($uid !== FALSE) {
+ $uids[] = $uid;
+ }
+ }
+ }
if (!empty($uids)) {
drush_op('user_user_operations_block', $uids);
}
* Unblock the specified user(s).
*/
function drush_user_unblock($users = '') {
- $uids = _drush_user_get_users_from_options_and_arguments($users);
+ $uids = drush_get_option('uids');
+ if ($users !== '') {
+ $users = explode(',', $users);
+ foreach($users as $user) {
+ $uid = _drush_user_get_uid($user);
+ if ($uid !== FALSE) {
+ $uids[] = $uid;
+ }
+ }
+ }
if (!empty($uids)) {
drush_op('user_user_operations_unblock', $uids);
}
* Add a role to the specified user accounts.
*/
function drush_user_add_role($role, $users = '') {
- $uids = _drush_user_get_users_from_options_and_arguments($users);
+ $uids = drush_get_option('uids');
+ if ($users !== '') {
+ $users = explode(',', $users);
+ foreach($users as $user) {
+ $uid = _drush_user_get_uid($user);
+ if ($uid !== FALSE) {
+ $uids[] = $uid;
+ }
+ }
+ }
if (drush_drupal_major_version() >= 7) {
$rid_query = db_query("SELECT rid FROM {role} WHERE name = :role", array(':role' => $role));
}
if ($rid = drush_db_result($rid_query)) {
drush_op('user_multiple_role_edit', $uids, 'add_role', $rid);
foreach($uids as $uid) {
- drush_log(dt("Added the !role role to uid !uid", array('!role' => $role, '!uid' => $uid)), 'success');
+ drush_log(dt("Added the %role role to uid %uid", array('%role' => $role, '%uid' => $uid)), 'success');
}
}
else {
- return drush_set_error(dt("There is no role named: !role", array('!role' => $role)));
+ return drush_set_error("There is no role named: \"$role\"!");
}
}
else {
* Remove a role from the specified user accounts.
*/
function drush_user_remove_role($role, $users = '') {
- $uids = _drush_user_get_users_from_options_and_arguments($users);
+ $uids = drush_get_option('uids');
+ if ($users !== '') {
+ $users = explode(',', $users);
+ foreach($users as $user) {
+ $uid = _drush_user_get_uid($user);
+ if ($uid !== FALSE) {
+ $uids[] = $uid;
+ }
+ }
+ }
if (drush_drupal_major_version() >= 7) {
$rid_query = db_query("SELECT rid FROM {role} WHERE name = :role", array(':role' => $role));
}
if ($rid = drush_db_result($rid_query)) {
drush_op('user_multiple_role_edit', $uids, 'remove_role', $rid);
foreach($uids as $uid) {
- drush_log(dt("Removed the !role role from uid !uid", array('!role' => $role, '!uid' => $uid)), 'success');
+ drush_log(dt("Removed the %role role from uid %uid", array('%role' => $role, '%uid' => $uid)), 'success');
}
}
else {
- return drush_set_error(dt("There is no role named: !role", array('!role' => $role)));
+ return drush_set_error("There is no role named: \"$role\"!");
}
}
else {
}
/**
- * Given a comma-separated list of users, return uids
- * for users that match either by uid or email address.
- */
-function _drush_user_get_users_from_arguments($users) {
- $uids = array();
- if ($users !== '') {
- $users = explode(',', $users);
- foreach($users as $user) {
- $uid = _drush_user_get_uid($user);
- if ($uid !== FALSE) {
- $uids[] = $uid;
- }
- }
- }
- return $uids;
-}
-
-/**
- * Return the list of matching uids given
- */
-function _drush_user_get_users_from_options_and_arguments($users) {
- $uids = drush_get_option_list('uids');
-
- foreach (array('uid', 'name', 'mail' ) as $user_attr) {
- if ($arg = drush_get_option($user_attr)) {
- foreach(explode(',', $arg) as $search) {
- $uid_query = FALSE;
- switch ($user_attr) {
- case 'uid':
- if (drush_drupal_major_version() >= 7) {
- $uid_query = db_query("SELECT uid FROM {users} WHERE uid = :uid", array(':uid' => $search));
- }
- else {
- $uid_query = db_query("SELECT uid FROM {users} WHERE uid = %d", $search);
- }
- break;
- case 'name':
- if (drush_drupal_major_version() >= 7) {
- $uid_query = db_query("SELECT uid FROM {users} WHERE name = :name", array(':name' => $search));
- }
- else {
- $uid_query = db_query("SELECT uid FROM {users} WHERE name = '%s'", $search);
- }
- break;
- case 'mail':
- if (drush_drupal_major_version() >= 7) {
- $uid_query = db_query("SELECT uid FROM {users} WHERE mail = :mail", array(':mail' => $search));
- }
- else {
- $uid_query = db_query("SELECT uid FROM {users} WHERE mail = '%s'", $search);
- }
- break;
- }
- if ($uid_query !== FALSE) {
- if ($uid = drush_db_result($uid_query)) {
- $uids[] = $uid;
- }
- else {
- drush_set_error("Could not find a uid for $user_attr = $search");
- }
- }
- }
- }
- }
-
- return array_merge($uids, _drush_user_get_users_from_arguments($users));
-}
-
-/**
* Get uid(s) from a uid, user name, or email address.
* Returns a uid, or FALSE if none found.
*/
<ol>
<li>Create a command file called COMMANDFILE.drush.inc
+<li>Implement the function COMMANDFILE_drush_help(). Optional.
+
<li>Implement the function COMMANDFILE_drush_command()
<li>Implement the functions that your commands will call.
<li>The ".drush" folder in the user's HOME folder.
-<li>sites/all/drush in the current Drupal installation
-
-<li>All enabled modules in the current Drupal installation
+<li>All modules in the current Drupal installation
<li>Folders and files containing other versions of drush in their names will
be *skipped* (e.g. devel.drush4.inc or drush4/devel.drush.inc). Names
level. Usually, when working with a Drupal site, drush will
bootstrap to DRUSH_BOOTSTRAP_FULL; in this case, only the drush
commandfiles in enabled modules will be considered eligible for
-loading. If drush only bootstraps to DRUSH_BOOTSTRAP_SITE,
+loading. If a drush only bootstraps to DRUSH_BOOTSTRAP_SITE,
though, then all drush commandfiles will be considered, whether the
module is enabled or not. See `drush topic docs-bootstrap` for
more information on bootstrapping.
COMMANDFILE_drush_load() in the file COMMANDFILE.drush.load.inc.
If this function returns FALSE, then the commandfile will not be loaded.
+<h2>Implement COMMANDFILE_drush_help()</h2>
+<p>
+The drush_help hook is an optional place to describe a command in long form. If
+the command only requires a brief description, use the description key in
+COMMANDFILE_drush_command(). The drush_help hook for the 'sandwich' commandfile looks
+like this:
+<pre>
+ function sandwich_drush_help($section) {
+ switch ($section) {
+ case 'drush:make-me-a-sandwich':
+ return dt("... brief help summary goes here ...");
+ }
+ }
+</pre><p>
+Note that the command name is prepended with 'drush:' in
+the drush_help hook. Your commandfile may implement
+multiple commands; to do so, just add more 'case' statements
+to the switch, one for each command.
+
<h2>Implement COMMANDFILE_drush_command()</h2>
<p>
The drush_command hook is the most important part of the
<ul>
<li>'description': A description of the option.
- <li>'example_value': An example value to show in help.
- <li>'value': optional|required.
- <li>'required': This option must be passed.
+ <li>'required': The option must be specified, or an error is returned.
<li>'hidden': The option is not shown in the help output (rare).
</ul>
<li>'topics':
Provides a list of topic commands that are related in
- some way to this command. Used by `drush help`.
+ some way to this command. Used by `drush help` only.
<li>'topic':
* order, for the command "COMMAND":
*
* 0. drush_COMMAND_init()
- * 1. drush_hook_COMMAND_pre_validate()
- * 2. drush_hook_COMMAND_validate()
- * 3. drush_hook_pre_COMMAND()
- * 4. drush_hook_COMMAND()
- * 5. drush_hook_post_COMMAND()
+ * 1. drush_hook_COMMAND_validate()
+ * 2. drush_hook_pre_COMMAND()
+ * 3. drush_hook_COMMAND()
+ * 4. drush_hook_post_COMMAND()
*
* For example, here are the hook opportunities for a mysite.drush.inc file
* that wants to hook into the `pm-download` command.
*
- * 1. drush_mysite_pm_download_pre_validate()
- * 2. drush_mysite_pm_download_validate()
- * 3. drush_mysite_pre_pm_download()
- * 4. drush_mysite_pm_download()
- * 5. drush_mysite_post_pm_download()
+ * 1. drush_mysite_pm_download_validate()
+ * 2. drush_mysite_pre_pm_download()
+ * 3. drush_mysite_pm_download()
+ * 4. drush_mysite_post_pm_download()
*
* Note that the drush_COMMAND_init() hook is only for use by the
* commandfile that defines the command.
* 2. drush_mysite_pm_download_rollback()
* 3. drush_mysite_pre_pm_download_rollback()
* 4. drush_mysite_pm_download_validate_rollback()
- * 5. drush_mysite_pm_download_pre_validate_rollback()
*
* Before any command is called, hook_drush_init() is also called.
* hook_drush_exit() is called at the very end of command invocation.
*
* @see hook_drush_init()
* @see drush_COMMAND_init()
- * @see drush_hook_COMMAND_pre_validate()
* @see drush_hook_COMMAND_validate()
* @see drush_hook_pre_COMMAND()
* @see drush_hook_COMMAND()
* @see drush_hook_COMMAND_rollback()
* @see drush_hook_pre_COMMAND_rollback()
* @see drush_hook_COMMAND_validate_rollback()
- * @see drush_hook_COMMAND_pre_validate_rollback()
* @see hook_drush_exit()
*/
}
/**
- * Run before a specific command validates.
- *
- * Logging an error stops command execution, and the rollback function (if any)
- * for each hook implementation is invoked.
- *
- * @see drush_hook_COMMAND_pre_validate_rollback()
- */
-function drush_hook_COMMAND_pre_validate() {
-
-}
-
-/**
* Run before a specific command executes.
*
* Logging an error stops command execution, and the rollback function (if any)
}
/**
- * Automatically download project dependencies at pm-enable time.
- * Use a pre-pm_enable hook to download before your module is enabled,
- * or a post-pm_enable hook (drush_hook_post_pm_enable) to run after
- * your module is enabled.
- *
- * Your hook will be called every time pm-enable is executed; you should
- * only download dependencies when your module is being enabled. Respect
- * the --skip flag, and take no action if it is present.
+ * Add information to the upgrade project map; this information
+ * will be shown to the user when upgrading Drupal to the next
+ * major version if the module containing this hook is enabled.
+ *
+ * @see drush_upgrade_project_map().
*/
-function drush_hook_pre_pm_enable() {
- // Get the list of modules being enabled; only download dependencies if our module name appears in the list
- $modules = drush_get_context('PM_ENABLE_MODULES');
- if (in_array('hook', $modules) && !drush_get_option('skip')) {
- $url = 'http://server.com/path/MyLibraryName.tgz';
- $path = drush_get_context('DRUSH_DRUPAL_ROOT');
- if (module_exists('libraries')) {
- $path .= '/' . libraries_get_path('MyLibraryName') . '/MyLibraryName.tgz';
- }
- else {
- $path .= '/'. drupal_get_path('module', 'hook') . '/MyLibraryName.tgz';
- }
- drush_download_file($url, $path) && drush_tarball_extract($path);
- }
+function hook_drush_upgrade_project_map_alter(&$project_map) {
+ $project_map['warning']['hook'] = dt("You need to take special action before upgrading this module. See http://mysite.com/mypage for more information.");
}
/**
}
/**
+ * Take action before modules are disabled in a major upgrade.
+ * Note that when this hook fires, it will be operating on a
+ * copy of the database.
+ */
+function drush_hook_pre_site_upgrade_prepare() {
+ // site upgrade prepare will disable contrib_extensions and
+ // uninstall the uninstall_extension
+ $contrib_extensions = func_get_args();
+ $uninstall_extensions = explode(',', drush_get_option('uninstall', ''));
+}
+
+
+/**
* Add help components to a command
*/
function hook_drush_help_alter(&$command) {
+++ /dev/null
-<h1>Strict Option Handling</h1>
-<p>
-Some Drush commands use strict option handling; these commands require
-that all Drush global option appear on the command line before the Drush
-command name.
-<p>
-One example of this is the core-rsync command:
-<p>
-<pre>
- drush --simulate core-rsync -v @site1 @site2
-</pre>
-
-The --simulate option is a Drush global option that causes Drush to
-print out what it would do if the command is executed, without actually
-taking any action. Commands such as core-rsync that use strict option
-handling require that --simulate, if used, must appear before the command
-name. Most Drush commands allow the --simulate to be placed anywhere,
-such as at the end of the command line.
-<p>
-The -v option above is an rsync option. In this usage, it will cause
-the rsync command to run in verbose mode. It will not cause Drush to
-run in verbose mode, though, because it appears after the core-rsync
-command name. Most Drush commands would be run in verbose mode if a
--v option appeared in the same location.
-<p>
-The advantage of strict option handling is that it allows Drush to
-pass options and arguments through to a shell command. Some shell commands,
-such as rsync and ssh, either have options that cannot be represented
-in Drush. For example, rsync allows the --exclude option to appear multiple
-times on the command line, but Drush only allows one instance of an
-option at a time for most Drush commands. Strict option handling overcomes
-this limitation, plus possible conflict between Drush options and
-shell command options with the same name, at the cost of greater restriction
-on where global options can be placed.
-
rem set TEMP=H:/drush
REM See http://drupal.org/node/506448 for more information.
-@php.exe "%~dp0drush.php" --php="php.exe" %*
+@php.exe "%~dp0drush.php" %* --php="php.exe"
-drush_version=6.0-dev
+drush_version=5.0-dev
}
if (!$command_found) {
- // If we reach this point, command doesn't fit requirements or we have not
- // found either a valid or matching command.
-
- // If no command was found check if it belongs to a disabled module.
- if (!$command) {
- $command = drush_command_belongs_to_disabled_module();
- }
-
- // Set errors related to this command.
+ // If we reach this point, we have not found either a valid or matching command.
$args = implode(' ', drush_get_arguments());
if (isset($command) && is_array($command)) {
foreach ($command['bootstrap_errors'] as $key => $error) {
}
return $return;
}
-
-/**
- * Check if the given command belongs to a disabled module
- *
- * @return
- * Array with a command-like bootstrap error or FALSE if Drupal was not
- * bootstrapped fully or the command does not belong to a diabled module.
- */
-function drush_command_belongs_to_disabled_module() {
- if (drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
- _drush_find_commandfiles(DRUSH_BOOTSTRAP_DRUPAL_SITE, DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION);
- $commands = drush_get_commands();
- $command_name = array_shift(drush_get_arguments());
- if (isset($commands[$command_name])) {
- // We found it. Load its module name and set an error.
- if (is_array($commands[$command_name]['drupal dependencies']) && count($commands[$command_name]['drupal dependencies'])) {
- $modules = implode(', ', $commands[$command_name]['drupal dependencies']);
- } else {
- // The command does not define Drupal dependencies. Derive them.
- $command_files = drush_get_context('DRUSH_COMMAND_FILES', array());
- $command_path = $commands[$command_name]['path'] . DIRECTORY_SEPARATOR . $commands[$command_name]['commandfile'] . '.drush.inc';
- $modules = array_search($command_path, $command_files);
- }
- return array(
- 'bootstrap_errors' => array(
- 'DRUSH_COMMAND_DEPENDENCY_ERROR' =>
- dt('Command !command needs the following module(s) enabled to run: !dependencies.', array('!command' => $command_name, '!dependencies' => $modules)),
- )
- );
- }
- }
-
- return FALSE;
-}
* on the command line.
* 2. In one of the default locations:
* a. /etc/drush
- * b. $HOME/.drush
- * c. The sites/all/drush folder for the current Drupal site
+ * b. In the drush installation folder
+ * c. Inside the 'aliases' folder in the drush installation folder
+ * d. $HOME/.drush
+ * e. The sites/all/drush folder for the current Drupal site
* 3. Inside the sites folder of any bootstrapped Drupal site,
* or any local Drupal site indicated by an alias used as
* a parameter to a command
* is not set, and 'Linux' (or $options['remote-os']) if it is.
* - 'ssh-options': If the target requires special options, such as a non-
* standard port, alternative identity file, or alternative
- * authentication method, ssh-options can contain a string of extra
+ * authentication method, ssh- options can contain a string of extra
* options that are used with the ssh command, eg "-p 100"
* - 'parent': The name of a parent alias (e.g. '@server') to use as a basis
* for this alias. Any value of the parent will appear in the child
# Aliases for drush commands that work on the current drupal site:
#
# cc - drush cache-clear
-# cca - drush cache-clear all
# dis - drush pm-disable
# en - drush pm-enable
# i - drush pm-info
# unin - drush pm-uninstall
# up - drush pm-update
# upc - drush pm-updatecode
-# updb - drush updatedb
+# updb - drush uptatedb
# q - drush sql-query
#
# Provides several common shell commands to work better with drush:
# Aliases for drush commands that work on the current drupal site
alias cc='drush cache-clear'
-alias cca='drush cache-clear all'
alias dis='drush pm-disable'
alias en='drush pm-enable'
alias pmi='drush pm-info'
alias unin='drush pm-uninstall'
alias up='drush pm-update'
alias upc='drush pm-updatecode'
-alias updb='drush updatedb'
+alias updb='drush uptatedb'
alias q='drush sql-query'
# Overrides for standard shell commands. Uncomment to enable. Alias
fi
}
-# Copy files from or to @site or @site:%files, etc; local sites only.
+# Copy from or two @site or @site:%files, etc; local sites only.
function cpd() {
p=()
for a in "$@" ; do
# $options['shell-aliases']['unsuck'] = 'pm-disable -y overlay,dashboard';
# $options['shell-aliases']['offline'] = 'variable-set -y --always-set maintenance_mode 1';
# $options['shell-aliases']['online'] = 'variable-delete -y --exact maintenance_mode';
-# $options['shell-aliases']['dis-all'] = '!drush -y dis $(drush pml --status=enabled --type=module --no-core --pipe)';
-// You can create a local cache of all projects checked out using
-// --package-handler=git_drupalorg; this can be faster for repeated
-// downloads, but can be dangerous. See: http://randyfay.com/node/119
-# $options['cache'] = TRUE;
+// Add a 'pm-clone' to simplify (cached) git cloning from drupal.org.
+# $options['shell-aliases']['pm-clone'] = 'pm-download --gitusername=YOURUSERNAME --package-handler=git_drupalorg --cache';
/**
* Historical (deprecated) aliases:
# $options['no-label'] = TRUE;
/**
- * An explicit list of tables which should be included in sql-dump and sql-sync.
- */
-# $options['tables']['common'] = array('user', 'permissions', 'role_permission', 'role');
-
-/**
* List of tables whose *data* is skipped by the 'sql-dump' and 'sql-sync'
* commands when the "--structure-tables-key=common" option is provided.
* You may add specific tables to the existing array or add a new element.
* git repository. Example script below by grayside. Customize as desired.
* @see: http://grayside.org/node/93.
*/
-#exec('git rev-parse --show-toplevel 2> /dev/null', $output);
+#exec('git rev-parse --git-dir 2> /dev/null', $output);
#if (!empty($output)) {
# $repo = $output[0];
-# $options['config'] = $repo . '/drush/drushrc.php';
-# $options['include'] = $repo . '/drush/commands';
-# $options['alias-path'] = $repo . '/drush/aliases';
+# $options['config'] = $repo . '/../drush/drushrc.php';
+# $options['include'] = $repo . '/../drush/commands';
+# $options['alias-path'] = $repo . '/../drush/aliases';
#}
}
/**
- * Implement hook help alter.
- *
- * When a hook extends a command with additional options, it must
- * implement help alter and declare the option(s). Doing so will add
- * the option to the help text for the modified command, and will also
- * allow the new option to be specified on the command line. Without
- * this, Drush will fail with an error when a user attempts to use
- * the option.
+ * Implementation of drush_hook_help_alter
*/
-function policy_drush_help_alter($command) {
+function drush_policy_help_alter($command) {
+ // If a command hook (e.g. drush_hook_COMMAND_validate, below)
+ // calls drush_get_option('abc'), then it is required to add
+ // the new option to the 'global-options' command (or, alternatively,
+ // to the single command hooked). Otherwise, the user will get
+ // an "unknown option" error when they try to specify the option on the
+ // commandline.
if ($command['command'] == 'updatedb') {
$command['options']['token'] = 'Per site policy, you must specify a token in the --token option for all commands.';
}
'filling' => 'The type of the sandwich (turkey, cheese, etc.). Defaults to ascii.',
),
'options' => array(
- 'spreads' => array(
- 'description' => 'Comma delimited list of spreads.',
- 'example-value' => 'mayonnaise,mustard',
- ),
+ 'spreads' => 'Comma delimited list of spreads (e.g. mayonnaise, mustard)',
),
'examples' => array(
'drush mmas turkey --spreads=ketchup,mustard' => 'Make a terrible-tasting sandwich that is lacking in pickles.',
+++ /dev/null
-<?php
-
-/**
- * Sync_enable adds options to sql-sync to enable and disable
- * modules after an sql-sync operation. One use case for this
- * is to use Drush site aliases to automatically enable your
- * development modules whenever you sync from your live site to
- * your dev site.
- *
- * For example:
- *
- * $aliases['dev'] = array (
- * 'root' => '/srv/www/drupal',
- * 'uri' => 'site.com',
- * 'target-command-specific' => array(
- * 'sql-sync' => array(
- * 'enable' => array('devel', 'hacked'),
- * 'disable' => array('securepages'),
- * ),
- * ),
- * );
- *
- * To use this feature, copy the 'target-command-specific'
- * item from the example alias above, place it in your development
- * site aliases, and customize the development module list
- * to suit. You must also copy the sync_enable.drush.inc
- * file to a location where Drush will find it, such as
- * $HOME/.drush. See `drush topic docs-commands` for more
- * information.
- */
-
-/**
- * Implement hook help alter.
- *
- * When a hook extends a command with additional options, it must
- * implement help alter and declare the option(s). Doing so will add
- * the option to the help text for the modified command, and will also
- * allow the new option to be specified on the command line. Without
- * this, Drush will fail with an error when a user attempts to use
- * the option.
- */
-function sync_enable_drush_help_alter(&$command) {
- if ($command['command'] == 'sql-sync') {
- $command['options']['enable'] = "Enable the specified modules in the target database after the sync operation has completed.";
- $command['options']['disable'] = "Disable the specified modules in the target database after the sync operation has completed.";
- }
-}
-
-/**
- * Implement hook post sql sync.
- *
- * The post hook is only called if the sql-sync operation completes
- * without an error. When called, we check to see if the user specified
- * any modules to enable/disable. If so, we will call pm-enable/pm-disable on each module.
- */
-function drush_sync_enable_post_sql_sync($source = NULL, $destination = NULL) {
- $modules_to_enable = drush_get_option_list('enable');
- if (!empty($modules_to_enable)) {
- drush_log(dt("Enable !modules post-sql-sync", array('!modules' => implode(',', $modules_to_enable))), 'ok');
- drush_invoke_process($destination, 'pm-enable', $modules_to_enable, array('yes' => TRUE));
- }
- $modules_to_disable = drush_get_option_list('disable');
- if (!empty($modules_to_disable)) {
- drush_log(dt("Disable !modules post-sql-sync", array('!modules' => implode(',', $modules_to_disable))), 'ok');
- drush_invoke_process($destination, 'pm-disable', $modules_to_disable, array('yes' => TRUE));
- }
-}
+++ /dev/null
-<?php
-
-/**
- * Sync_via_http allows you to sql-sync your database using HTTP
- * (e.g. wget or curl) instead of rsync. This is helpful for
- * exporting your database to colaborators without shell access
- * to the production or staging server.
- *
- * For example:
- *
- * $aliases['staging'] = array (
- * 'root' => '/srv/www/drupal',
- * 'uri' => 'staging.site.com',
- * 'source-command-specific' => array(
- * 'sql-sync' => array(
- * 'http-sync' => 'https://staging.site.com/protected-directory/site-database-dump.sql',
- * 'http-sync-user' => 'wwwadmin',
- * 'http-sync-password' => 'secretsecret',
- * ),
- * ),
- * );
- *
- * To use this feature, copy the 'source-command-specific'
- * item from the example alias above, place it in your staging
- * site aliases, and custom the access credentials as
- * necessary. You must also copy the sync_via_http.drush.inc
- * file to a location where Drush will find it, such as
- * $HOME/.drush. See `drush topic docs-commands` for more
- * information.
- *
- * IMPORTANT NOTE: This example does not cause the sql dump
- * to be performed; it is presumed that the dump file already
- * exists at the provided URL. For a full solution, a web page
- * that initiated an sql-dump (or perhaps a local sql-sync followed
- * by an sql-sanitize and then an sql-dump) would be necessary.
- */
-
-/**
- * Implement hook help alter.
- *
- * When a hook extends a command with additional options, it must
- * implement help alter and declare the option(s). Doing so will add
- * the option to the help text for the modified command, and will also
- * allow the new option to be specified on the command line. Without
- * this, Drush will fail with an error when a user attempts to use
- * the option.
- */
-function sync_via_http_drush_help_alter(&$command) {
- if ($command['command'] == 'sql-sync') {
- $command['options']['http-sync'] = "Copy the database via http instead of rsync. Value is the url that the existing database dump can be found at.";
- $command['sub-options']['http-sync']['http-sync-user'] = "Username for the protected directory containing the sql dump.";
- $command['sub-options']['http-sync']['http-sync-password'] = "Password for the same directory.";
- }
-}
-
-/**
- * Implement hook pre sql sync.
- *
- * During the pre hook, determine if the http-sync option has been
- * specified. If it has been, then disable the normal ssh + rsync
- * dump-and-transfer that sql-sync usually does, and transfer the
- * database dump via an http download.
- */
-function drush_sync_via_http_pre_sql_sync($source = NULL, $destination = NULL) {
- $sql_dump_download_url = drush_get_option('http-sync');
- if (!empty($sql_dump_download_url)) {
- $user = drush_get_option('http-sync-user', FALSE);
- $password = drush_get_option('http-sync-password', FALSE);
- $source_dump_file = _drush_sync_via_http_download_file($sql_dump_download_url, $user, $password);
- if ($source_dump_file === FALSE) {
- return drush_set_error('DRUSH_CANNOT_DOWNLOAD', dt("The URL !url could not be downloaded.", array('!url' => $sql_dump_download_url)));
- }
- drush_set_option('target-dump', $source_dump_file);
- drush_set_option('no-dump', TRUE);
- drush_set_option('no-sync', TRUE);
- }
-}
-
-/**
- * Download a file, optionaly with user authentication, using either wget or
- * curl, as available.
- */
-function _drush_sync_via_http_download_file($url, $user = FALSE, $password = FALSE, $destination = FALSE, $overwrite = TRUE) {
- static $use_wget;
- if ($use_wget === NULL) {
- $use_wget = drush_shell_exec('which wget');
- }
-
- $destination_tmp = drush_tempnam('download_file');
- if ($use_wget) {
- if ($user && $password) {
- drush_shell_exec("wget -q --timeout=30 --user=%s --password=%s -O %s %s", $user, $password, $destination_tmp, $url);
- }
- else {
- drush_shell_exec("wget -q --timeout=30 -O %s %s", $destination_tmp, $url);
- }
- }
- else {
- if ($user && $password) {
- drush_shell_exec("curl -s -L --connect-timeout 30 --user %s:%s -o %s %s", $user, $password, $destination_tmp, $url);
- }
- else {
- drush_shell_exec("curl -s -L --connect-timeout 30 -o %s %s", $destination_tmp, $url);
- }
- }
- if (!drush_get_context('DRUSH_SIMULATE')) {
- if (!drush_file_not_empty($destination_tmp) && $file = @file_get_contents($url)) {
- @file_put_contents($destination_tmp, $file);
- }
- if (!drush_file_not_empty($destination_tmp)) {
- // Download failed.
- return FALSE;
- }
- }
- if ($destination) {
- drush_move_dir($destination_tmp, $destination, $overwrite);
- return $destination;
- }
- return $destination_tmp;
-}
* Whether output has already been handled.
*/
function _drush_backend_integrate($data, $backend_options, $outputted) {
- // In 'integrate' mode, logs and errors have already been handled
- // by drush_backend_packet (sender) drush_backend_parse_packets (reciever - us)
- // during incremental output. We therefore do not need to call drush_set_error
- // or drush_log here. The exception is if the sender is an older version of
- // Drush (version 4.x) that does not send backend packets, then we will
- // not have processed the log entries yet, and must print them here.
- $received_packets = drush_get_context('DRUSH_RECEIVED_BACKEND_PACKETS', FALSE);
- if (is_array($data['log']) && $backend_options['log'] && (!$received_packets)) {
+ 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 (isset($backend_options['#output-label'])) {
}
/**
- * Supress log message output during backend integrate.
- */
-function _drush_backend_integrate_log($entry) {
-}
-
-/**
* Call an external command using proc_open.
*
* @param cmds
* 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_backend_proc_open($cmds, $process_limit, $context = NULL) {
+function _drush_proc_open($cmds, $process_limit, $context = NULL) {
$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
+ 2 => array("pipe", "w") // stderr is a pipe the child will write to
);
+ if (drush_is_windows()) {
+ /* http://bg2.php.net/manual/en/function.proc-open.php#97012 */
+ $descriptorspec[2][1] = "a";
+ }
+
$open_processes = array();
$bucket = array();
$process_limit = max($process_limit, 1);
- $is_windows = drush_is_windows();
- // Loop through processes until they all close, having a nap as needed.
- $nap_time = 0;
+ // Loop through processes until they all close.
while (sizeof($open_processes) || sizeof($cmds)) {
- $nap_time++;
if (sizeof($cmds) && (sizeof($open_processes) < $process_limit)) {
// Pop the site and command (key / value) from the cmds array
end($cmds);
// this open longer.
fclose($process['pipes'][0]);
- $process['info'] = stream_get_meta_data($process['pipes'][1]);
- stream_set_blocking($process['pipes'][1], FALSE);
- stream_set_timeout($process['pipes'][1], 1);
+ foreach (array(1, 2) as $pipe) {
+ $process['info'] = stream_get_meta_data($process['pipes'][$pipe]);
+ stream_set_blocking($process['pipes'][$pipe], FALSE);
+ stream_set_timeout($process['pipes'][$pipe], 1);
+ }
$bucket[$site]['cmd'] = $c;
$bucket[$site]['output'] = '';
$bucket[$site]['backend-options'] = $backend_options;
$bucket[$site]['outputted'] = FALSE;
$open_processes[$site] = $process;
}
- // Reset the $nap_time variable as there might be output to process next
- // time around:
- $nap_time = 0;
- }
- // Set up to call stream_select(). See:
- // http://php.net/manual/en/function.stream-select.php
- // We can't use stream_select on Windows, because it doesn't work for
- // streams returned by proc_open.
- if (!$is_windows) {
- $ss_result = 0;
- $read_streams = array();
- $write_streams = array();
- $except_streams = array();
- foreach ($open_processes as $site => &$current_process) {
- if (isset($current_process['pipes'][1])) {
- $read_streams[] = $current_process['pipes'][1];
- }
- }
- // Wait up to 2s for data to become ready on one of the read streams.
- if (sizeof($read_streams)) {
- $ss_result = stream_select($read_streams, $write_streams, $except_streams, 2);
- // If stream_select returns a error, then fallback to using $nap_time.
- if ($ss_result !== FALSE) {
- $nap_time = 0;
- }
- }
}
-
foreach ($open_processes as $site => &$current_process) {
- if (isset($current_process['pipes'][1])) {
- // Collect output from stdout
- $bucket[$site][1] = '';
- $info = stream_get_meta_data($current_process['pipes'][1]);
-
- if (!feof($current_process['pipes'][1]) && !$info['timed_out']) {
- $string = fread($current_process['pipes'][1], 4096);
- $output_end_pos = strpos($string, 'DRUSH_BACKEND_OUTPUT_START>>>');
- if ($output_end_pos !== FALSE) {
- $trailing_string = substr($string, 0, $output_end_pos);
- if (!empty($trailing_string)) {
- drush_backend_parse_packets($trailing_string, $bucket[$site]['backend-options']);
- _drush_backend_print_output($trailing_string, $bucket[$site]['backend-options']);
- $bucket[$site]['outputted'] = TRUE;
+ if (isset($current_process['pipes'][1]) || isset($current_process['pipes'][2])) {
+ // Collect output from stdout and stderr
+ foreach (array(1, 2) as $pipe) {
+ if (isset($current_process['pipes'][$pipe])) {
+ $bucket[$site][$pipe] = '';
+ $info = stream_get_meta_data($current_process['pipes'][$pipe]);
+
+ if (!feof($current_process['pipes'][$pipe]) && !$info['timed_out']) {
+ $string = fread($current_process['pipes'][$pipe], 4096);
+ $output_end_pos = strpos($string, 'DRUSH_BACKEND_OUTPUT_START>>>');
+ if ($output_end_pos !== FALSE) {
+ $trailing_string = substr($string, 0, $output_end_pos);
+ if (!empty($trailing_string)) {
+ drush_backend_parse_packets($trailing_string, $bucket[$site]['backend-options']);
+ _drush_backend_print_output($trailing_string, $bucket[$site]['backend-options']);
+ $bucket[$site]['outputted'] = TRUE;
+ }
+ $bucket[$site]['end_of_output'] = TRUE;
+ }
+ if (!$bucket[$site]['end_of_output']) {
+ drush_backend_parse_packets($string, $bucket[$site]['backend-options']);
+ // Pass output through.
+ _drush_backend_print_output($string, $bucket[$site]['backend-options']);
+ if (!empty($string)) {
+ $bucket[$site]['outputted'] = TRUE;
+ }
+ }
+ $bucket[$site][$pipe] .= $string;
+ $bucket[$site]['output'] .= $string;
+ $info = stream_get_meta_data($current_process['pipes'][$pipe]);
+ flush();
}
- $bucket[$site]['end_of_output'] = TRUE;
- }
- if (!$bucket[$site]['end_of_output']) {
- drush_backend_parse_packets($string, $bucket[$site]['backend-options']);
- // Pass output through.
- _drush_backend_print_output($string, $bucket[$site]['backend-options']);
- if (!empty($string)) {
- $bucket[$site]['outputted'] = TRUE;
+ else {
+ fclose($current_process['pipes'][$pipe]);
+ unset($current_process['pipes'][$pipe]);
+ // close the pipe , set a marker
+
+ if (drush_is_windows() && ($pipe == 1)) {
+ /* On windows STDERR doesn't give EOF even if STDOUT gets closed
+ * Because of this this loop goes indefinitely
+ * and this manual unset is needed on windows in order the script to work
+ */
+ fclose($current_process['pipes'][2]);
+ unset($current_process['pipes'][2]);
+ }
+
}
}
- $bucket[$site][1] .= $string;
- $bucket[$site]['output'] .= $string;
- $info = stream_get_meta_data($current_process['pipes'][1]);
- flush();
-
- // Reset the $nap_time variable as there might be output to process
- // next time around:
- if (!empty($string)) {
- $nap_time = 0;
- }
- }
- else {
- fclose($current_process['pipes'][1]);
- unset($current_process['pipes'][1]);
- // close the pipe , set a marker
-
- // Reset the $nap_time variable as there might be output to process
- // next time around:
- $nap_time = 0;
}
}
else {
// if both pipes are closed for the process, remove it from active loop and add a new process to open.
$bucket[$site]['code'] = proc_close($current_process['process']);
unset($open_processes[$site]);
-
- // Reset the $nap_time variable as there might be output to process next
- // time around:
- $nap_time = 0;
}
}
-
- // We should sleep for a bit if we need to, up to a maximum of 1/10 of a
- // second.
- if ($nap_time > 0) {
- usleep(max($nap_time * 500, 100000));
- }
}
return $bucket;
// TODO: Handle bad proc handles
//return FALSE;
}
-
-
/**
* Print the output received from a call to backend invoke,
* adding the label to the head of each line if necessary.
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)) {
- drush_set_context('DRUSH_RECEIVED_BACKEND_PACKETS', TRUE);
foreach ($match[1] as $packet_data) {
$entry = (array) json_decode($packet_data);
if (is_array($entry) && isset($entry['packet'])) {
'%drush-script' => NULL,
);
- $site = (array_key_exists('#name', $site_record) && !array_key_exists($site_record['#name'], $invocation_options)) ? $site_record['#name'] : $index++;
+ $site = array_key_exists('#name', $site_record) ? $site_record['#name'] : $index++;
$invocation_options[$site] = array(
'site-record' => $site_record,
'command' => $command,
$post_options = array();
$commandline_options = array();
$drush_global_options = array();
- $drush_local_options = array();
$additional_backend_options = array();
foreach ($site_record as $key => $value) {
if (!in_array($key, drush_sitealias_site_selection_keys())) {
}
}
}
- if (array_key_exists('drush-local-options', $backend_options)) {
- $drush_local_options = $backend_options['drush-local-options'];
- $command_options += $drush_local_options;
- }
- if (!empty($backend_options['backend']) && empty($backend_options['interactive'])) {
- $drush_global_options['backend'] = '2';
- }
- if (!empty($backend_options['interactive'])) {
- $drush_global_options['invoke'] = TRUE;
- }
foreach ($command_options as $key => $value) {
$global = array_key_exists($key, $global_option_list);
$propagate = TRUE;
*
* @return
* If the command could not be completed successfully, FALSE.
- * If one command was executed, this will return an associative array containing
- * the data from drush_backend_output().
- * If multiple commands were executed, this will return an associative array
- * containing one item, 'concurrent', which will contain a list of the different
- * backend invoke results from each concurrent command.
+ * If the command was completed, this will return an associative array containing the data from drush_backend_output().
*/
function _drush_backend_invoke($cmds, $common_backend_options = array(), $context = NULL) {
if (drush_get_context('DRUSH_SIMULATE') && !array_key_exists('override-simulated', $common_backend_options)) {
foreach ($cmds as $cmd) {
drush_log(dt('Backend invoke: !cmd', array('!cmd' => $cmd['cmd'])), 'command');
}
- if (array_key_exists('interactive', $common_backend_options) || array_key_exists('fork', $common_backend_options)) {
+ if (array_key_exists('interactive', $common_backend_options)) {
foreach ($cmds as $cmd) {
- $exec_cmd = $cmd['cmd'];
- if (array_key_exists('fork', $common_backend_options)) {
- $exec_cmd .= ' --quiet &';
- }
- drush_log(dt("executing !cmd", array('!cmd' => $exec_cmd)));
- $ret = drush_shell_proc_open($exec_cmd);
+ drush_log(dt("executing !cmd", array('!cmd' => $cmd['cmd'])));
+ $ret = drush_shell_proc_open($cmd['cmd']);
}
return $ret;
}
else {
$process_limit = drush_get_option_override($common_backend_options, 'concurrency', 4);
- $procs = _drush_backend_proc_open($cmds, $process_limit, $context);
+ $procs = _drush_proc_open($cmds, $process_limit, $context);
$procs = is_array($procs) ? $procs : array($procs);
- $ret = array();
foreach ($procs as $site => $proc) {
+ $ret = array();
if (($proc['code'] == DRUSH_APPLICATION_ERROR) && isset($common_backend_options['integrate'])) {
drush_set_error('DRUSH_APPLICATION_ERROR', dt("The external command could not be executed due to an application error."));
}
if (empty($ret)) {
$ret = $values;
}
- elseif (!array_key_exists('concurrent', $ret)) {
- $ret = array('concurrent' => array($ret, $values));
+ elseif (!array_key_exists('multiple', $ret)) {
+ $ret = array('multiple' => array($ret['site'] => $ret, $values['site'] => $values));
}
else {
- $ret['concurrent'][] = $values;
+ $ret['multiple'][$values['site']] = $values;
}
}
else {
- $ret = drush_set_error('DRUSH_FRAMEWORK_ERROR', dt("The command could not be executed successfully (returned: !return, code: !code)", array("!return" => $proc['output'], "!code" => $proc['code'])));
+ $ret = drush_set_error('DRUSH_FRAMEWORK_ERROR', dt("The command could not be executed successfully (returned: !return, code: %code)", array("!return" => $proc['output'], "%code" => $proc['code'])));
}
}
}
if (!empty($option_str)) {
$cmd[] = " " . $option_str;
}
+ if (!empty($backend_options['backend']) && empty($backend_options['interactive'])) {
+ $cmd[] = "--backend=2";
+ }
+ if (!empty($backend_options['interactive'])) {
+ $cmd[] .= '--invoke';
+ }
$command = implode(' ', array_filter($cmd, 'strlen'));
if (!is_null($hostname)) {
if (drush_is_windows($os)) {
/**
* Determine whether a given bootstrap phase has been completed
*
- * This function name has a typo which makes me laugh so we choose not to
- * fix it. Take a deep breath, and smile. See
- * http://en.wikipedia.org/wiki/HTTP_referer
- *
- *
* @param phase
* The bootstrap phase to test
*
drush_load_config('site');
}
}
- // Check to see if we 'use'd a site alias with site-set
- drush_sitealias_check_site_env();
_drush_bootstrap_global_options();
}
'cron_safe_threshold' => 0,
);
$current_override = drush_get_option_list('variables');
- foreach ($current_override as $name => $value) {
- if (is_numeric($name) && (strpos($value, '=') !== FALSE)) {
- list($name, $value) = explode('=', $value, 2);
- }
+ foreach ( $current_override as $pair) {
+ list($name, $value) = explode('=', $pair, 2);
$override[$name] = $value;
}
$conf = is_array($conf) ? array_merge($conf, $override) : $conf;
*/
function drush_cache_set($cid, $data, $bin = 'default', $expire = DRUSH_CACHE_PERMANENT) {
$ret = _drush_cache_get_object($bin)->set($cid, $data, $expire);
- if ($ret) drush_log(dt("Cache SET cid: !cid", array('!cid' => $cid)), 'debug');
+ if ($ret) drush_log(dt("Cache SET cid: %cid", array('%cid' => $cid)), 'debug');
return $ret;
}
* database information for sql-sync via sql-conf.
* 'interactive'
* Overrides the backend invoke process to run commands interactively.
- * 'fork'
- * Overrides the backend invoke process to run non blocking commands in
- * the background. Forks a new process by adding a '&' at the end of the
- * command. The calling process does not receive any output from the child
- * process. The fork option is used to spawn a process that outlives its
- * parent.
*
* @return
* If the command could not be completed successfully, FALSE.
* Do not change the signature of this function! drush_invoke_process
* is one of the key Drush APIs. See http://drupal.org/node/1152908
*/
-function drush_invoke_process($site_alias_record, $command_name, $commandline_args = array(), $commandline_options = array(), $backend_options = TRUE) {
+function drush_invoke_process($site_alias_record, $command_name, $commandline_args = array(), $commandline_options = array(), $backend_options = FALSE) {
if (is_array($site_alias_record) && array_key_exists('site-list', $site_alias_record)) {
$site_alias_records = drush_sitealias_resolve_sitespecs($site_alias_record['site-list']);
$site_alias_records = drush_sitealias_simplify_names($site_alias_records);
'args' => $commandline_args);
$invoke_multiple = drush_get_option_override($backend_options, 'invoke-multiple', 0);
if ($invoke_multiple) {
- $invocations = array_fill(0, $invoke_multiple, $invocations[0]);
+ $invocations = array_fill(0, $invoke_multiple, $invocations);
}
}
return drush_backend_invoke_concurrent($invocations, $commandline_options, $backend_options);
}
/**
- * Invoke Drush API calls, including all hooks.
+ * Invoke drush api calls, including all hooks.
*
* This is an internal function; it is called from drush_dispatch via
* drush_command, but only if the command does not specify a 'callback'
$all_available_hooks = array();
// Iterate through the different hook variations
- $variations = array($hook . "_pre_validate", $hook . "_validate", "pre_$hook", $hook, "post_$hook");
+ $variations = array($hook . "_validate", "pre_$hook", $hook, "post_$hook");
foreach ($variations as $var_hook) {
// Get the list of command files.
// We re-fetch the list every time through
if (($defined_in_commandfile . "_" == substr($hook . "_",0,strlen($defined_in_commandfile)+ 1))) {
$default_command_hook = sprintf("drush_%s", $hook);
}
- $dt_args = array(
- '!command' => $command,
- '!default_func' => $default_command_hook,
- );
- $message = "No hook functions were found for !command. The primary hook function is !default_func(). Please implement this function. Run with --show-invoke to see all available hooks.";
- $return = drush_set_error('DRUSH_FUNCTION_NOT_FOUND', dt($message, $dt_args));
+ drush_log(dt("No hook functions were found for !command.", array('!command' => $command)), 'warning');
+ drush_log(dt("The primary drush_invoke() hook for !command is !default_func(). Please implement this function. Run with --show-invoke to see all available hooks.", array('!command' => $command, '!default_func' => $default_command_hook)), 'warning');
}
if (drush_get_option('show-invoke')) {
// We show all available hooks up to and including the one that failed (or all, if there were no failures)
drush_log(dt("Available rollback hooks for !command: !rollback", array('!command' => $command, '!rollback' => "\n" . implode("\n", $available_rollbacks))), 'ok');
}
- // Something went wrong, we need to undo.
+ // something went wrong, we need to undo
if ($rollback) {
if (drush_get_option('confirm-rollback', FALSE)) {
// Optionally ask for confirmation, --yes and --no are ignored from here on as we are about to finish this process.
// are not represented in our options list.
$cli_options = array_keys(drush_get_context('cli'));
$allowed_options = _drush_flatten_options($options);
- $allowed_options = drush_append_negation_options($allowed_options);
$disallowed_options = array_diff($cli_options, $allowed_options);
if (!empty($disallowed_options)) {
$unknown = count($disallowed_options) > 1 ? dt('Unknown options') : dt('Unknown option');
return TRUE;
}
-function drush_append_negation_options($allowed_options) {
- $new_allowed = $allowed_options;
- foreach ($allowed_options as $option) {
- $new_allowed[] = 'no-' . $option;
- }
- return $new_allowed;
-}
-
function _drush_verify_cli_arguments($command) {
// Check to see if all of the required arguments
// are specified.
'options' => 'Options',
),
'arguments' => array(),
+ 'argument-description' => array(), // a copy of 'arguments'
'required-arguments' => FALSE,
'options' => array(),
'sub-options' => array(),
'drupal dependencies' => array(),
'drush dependencies' => array(),
'handle-remote-commands' => FALSE,
- 'strict-option-handling' => FALSE,
+ 'mask-local-cli-options' => FALSE,
'bootstrap_errors' => array(),
'topics' => array(),
'hidden' => FALSE,
* String or array.
*/
function _drush_command_translate($source) {
- return is_array($source) ? call_user_func_array('dt', $source) : dt($source);
+ return is_array($source)?call_user_func_array('dt', $source):dt($source);
}
/**
* locally rather than remotely dispatched. When this mode is set, the target site
* can be obtained via:
* drush_get_context('DRUSH_TARGET_SITE_ALIAS')
- * - strict-option-handling: set to TRUE if drush should strictly separate local command
+ * - mask-local-cli-options: set to TRUE if drush should strictly separate local command
* cli options from the global options. Usually, drush allows global cli options and
* command cli options to be interspersed freely on the commandline. For commands where
* this flag is set, options are separated, with global options comming before the
* drush --global-options command --command-options
* In this mode, the command options are no longer available via drush_get_option();
* instead, they can be retrieved via:
- * $args = drush_get_original_cli_args_and_options();
* $args = drush_get_context('DRUSH_COMMAND_ARGS', array());
- * In this case, $args will contain the command args and options literally, exactly as they
- * were entered on the command line, and in the same order as they appeared.
+ * In this case, $args will contain the command options literally, exactly as they
+ * were entered on the command line.
*/
function drush_parse_command() {
$args = drush_get_arguments();
// User commands, specified by 'include' option
if ($include = drush_get_context('DRUSH_INCLUDE', FALSE)) {
foreach ($include as $path) {
- if (is_dir($path)) {
- drush_log('Include ' . $path, 'notice');
- $searchpath[] = $path;
- }
+ drush_log('Include ' . $path, 'notice');
+ $searchpath[] = $path;
}
}
* Substrings to ignore during commandfile searching.
*/
function drush_filename_blacklist() {
- return array_diff(array('.', '..', 'drush_make', 'examples', 'tests', 'disabled', 'gitcache', 'cache', 'drush4', 'drush5', 'drush6', 'drush7'), array('drush' . DRUSH_MAJOR_VERSION));
+ return array_diff(array('.', '..', 'drush_make', 'examples', 'tests', 'disabled', 'gitcache', 'drush4', 'drush5', 'drush6', 'drush7'), array('drush' . DRUSH_MAJOR_VERSION));
}
/**
* Conditionally include default options based on the command used.
*/
function drush_command_default_options($command = NULL) {
- $command_default_options = drush_get_context('command-specific');
- return drush_command_set_command_specific($command_default_options);
-}
-
-function drush_sitealias_command_default_options($site_record, $prefix, $command = NULL) {
- if (isset($site_record) && array_key_exists($prefix . 'command-specific', $site_record)) {
- return drush_command_set_command_specific($site_record[$prefix . 'command-specific'], $command);
- }
- return FALSE;
-}
-
-function drush_command_set_command_specific($command_default_options, $command = NULL) {
if (!$command) {
$command = drush_get_command();
}
// Look for command-specific options for this command
// keyed both on the command's primary name, and on each
// of its aliases.
- $options_were_set = _drush_command_set_default_options($command_default_options, $command['command']);
+ $options_were_set = _drush_command_set_default_options($command['command']);
if (isset($command['aliases']) && count($command['aliases'])) {
foreach ($command['aliases'] as $alias) {
- $options_were_set += _drush_command_set_default_options($command_default_options, $alias);
+ if (_drush_command_set_default_options($alias) === TRUE) {
+ $options_were_set = TRUE;
+ }
}
}
// If we set or cleared any options, go back and re-bootstrap any global
// options such as -y and -v.
- if (!empty($options_were_set)) {
+ if ($options_were_set) {
_drush_bootstrap_global_options();
}
- // If the command uses strict option handling, back out any global
- // options that were set.
- if ($command['strict-option-handling']) {
- $global_options = drush_get_global_options();
- foreach ($options_were_set as $key) {
- if (array_key_exists($key, $global_options)) {
- if (!array_key_exists('context', $global_options[$key])) {
- $strict_options_warning =& drush_get_context('DRUSH_STRICT_OPTIONS_WARNING', array());
- if (!array_key_exists($key, $strict_options_warning)) {
- drush_log(dt("Global option --!option not supported in command-specific options for command !command due to a limitation in strict option handling.", array('!option' => $key, '!command' => $command['command'])), 'warning');
- $strict_options_warning[$key] = TRUE;
- }
- }
- drush_unset_option($key, 'specific');
- }
- }
- }
}
}
-function _drush_command_set_default_options($command_default_options, $command) {
- $options_were_set = array();
+function _drush_command_set_default_options($command) {
+ $options_were_set = FALSE;
+ $command_default_options = drush_get_context('command-specific');
if (array_key_exists($command, $command_default_options)) {
foreach ($command_default_options[$command] as $key => $value) {
// We set command-specific options in their own context
// context, but lower than command-line options.
if (!drush_get_option('no-' . $key, FALSE)) {
drush_set_option($key, $value, 'specific');
- $options_were_set[] = $key;
+ $options_were_set = TRUE;
}
}
}
}
/**
- * Return the original cli args and options, exactly as they
- * appeared on the command line, and in the same order.
- * Any command-specific options that were set will also
- * appear in this list, appended at the very end.
- *
- * The args and options returned are raw, and must be
- * escaped as necessary before use.
- */
-function drush_get_original_cli_args_and_options($command = NULL) {
- $args = drush_get_context('DRUSH_COMMAND_ARGS', array());
- $command_specific_options = drush_get_context('specific');
- if ($command == NULL) {
- $command = drush_get_command();
- }
- $command_options = ($command == NULL) ? array() : _drush_get_command_options($command);
- foreach ($command_specific_options as $key => $value) {
- if (!array_key_exists($key, $command_options)) {
- $args[] = "--$key=$value";
- }
- }
- return $args;
-}
-
-/**
* Determine whether a command file implements a hook.
*
* @param $module
'!command' => $command['command'],
'!dependency' => "$dependency.drush.inc",
);
- $command['bootstrap_errors']['DRUSH_COMMANDFILE_DEPENDENCY_ERROR'] = dt('Command !command needs the following drush command file to run: !dependency.', $dt_args);
+ $command['bootstrap_errors']['DRUSH_COMMAND_DEPENDENCY_ERROR'] = dt('Command !command needs the following drush command file to run: !dependency.', $dt_args);
return FALSE;
}
}
_drush_bootstrap_base_environment();
// Check for and record any site alias.
drush_sitealias_check_arg();
- drush_sitealias_check_site_env();
// Return the new argv for easy reference.
return $argv;
drush_log(dt('Cannot open drushrc "!config", ignoring.', array('!config' => realpath($config))), 'warning');
return FALSE;
}
- if (!empty($options) || !empty($aliases) || !empty($command_specific) || !empty($override)) {
+ if (!empty($options) || !empty($aliases) || !empty($command_specific)) {
$options = array_merge(drush_get_context($context), $options);
$options['config-file'] = realpath($config);
$query .= " ORDER BY $order_by_field $order_by_direction";
}
if (!is_null($length)) {
- $db_spec = _drush_sql_get_db_spec();
- $db_scheme = _drush_sql_get_scheme($db_spec);
- if ($db_scheme == 'oracle')
- return db_query_range($query, $start, $length);
- else {
- $limit = " LIMIT $length";
- if (!is_null($start)) {
- $limit .= " OFFSET $start";
- }
- $query .= $limit;
+ $limit = " LIMIT $length";
+ if (!is_null($start)) {
+ $limit .= " OFFSET $start";
}
+ $query .= $limit;
}
return db_query($query, $args);
/**
* @} End of "defgroup dbfunctions".
- */
+ */
\ No newline at end of file
return TRUE;
}
-
-/**
- * Parse Drupal info file format.
- *
- * Copied with modifications from includes/common.inc.
- *
- * @see drupal_parse_info_file
- */
-function drush_drupal_parse_info_file($filename) {
- if (!file_exists($filename)) {
- return array();
- }
-
- $data = file_get_contents($filename);
- return _drush_drupal_parse_info_file($data);
-}
-
-/**
- * Parse the info file.
- */
-function _drush_drupal_parse_info_file($data) {
- if (!$data) {
- return FALSE;
- }
-
- if (preg_match_all('
- @^\s* # Start at the beginning of a line, ignoring leading whitespace
- ((?:
- [^=;\[\]]| # Key names cannot contain equal signs, semi-colons or square brackets,
- \[[^\[\]]*\] # unless they are balanced and not nested
- )+?)
- \s*=\s* # Key/value pairs are separated by equal signs (ignoring white-space)
- (?:
- ("(?:[^"]|(?<=\\\\)")*")| # Double-quoted string, which may contain slash-escaped quotes/slashes
- (\'(?:[^\']|(?<=\\\\)\')*\')| # Single-quoted string, which may contain slash-escaped quotes/slashes
- ([^\r\n]*?) # Non-quoted string
- )\s*$ # Stop at the next end of a line, ignoring trailing whitespace
- @msx', $data, $matches, PREG_SET_ORDER)) {
- $info = array();
- foreach ($matches as $match) {
- // Fetch the key and value string.
- $i = 0;
- foreach (array('key', 'value1', 'value2', 'value3') as $var) {
- $$var = isset($match[++$i]) ? $match[$i] : '';
- }
- $value = stripslashes(substr($value1, 1, -1)) . stripslashes(substr($value2, 1, -1)) . $value3;
-
- // Parse array syntax.
- $keys = preg_split('/\]?\[/', rtrim($key, ']'));
- $last = array_pop($keys);
- $parent = &$info;
-
- // Create nested arrays.
- foreach ($keys as $key) {
- if ($key == '') {
- $key = count($parent);
- }
- if (!isset($parent[$key]) || !is_array($parent[$key])) {
- $parent[$key] = array();
- }
- $parent = &$parent[$key];
- }
-
- // Handle PHP constants.
- if (defined($value)) {
- $value = constant($value);
- }
-
- // Insert actual value.
- if ($last == '') {
- $last = count($parent);
- }
- $parent[$last] = $value;
- }
- return $info;
- }
- return FALSE;
-}
-
define('DRUSH_DRUPAL_KILOBYTE', 1024);
/**
- * Default amount of time, in seconds, to cache downloads via
- * drush_download_file(). One day is 86400 seconds.
- */
-define('DRUSH_CACHE_LIFETIME_DEFAULT', 86400);
-
-/**
* Include a file, selecting a version specific file if available.
*
* For example, if you pass the path "/var/drush" and the name
* - description: The help text for this item. displayed by `drush help`.
*/
function drush_get_global_options($brief = FALSE) {
- $options['root'] = array('short-form' => 'r', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => "Drupal root directory to use (default: current directory).", 'example-value' => 'path');
+ $options['root'] = array('short-form' => 'r', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => "Drupal root directory to use (default: current directory).", 'example-value' => '<path>');
$options['uri'] = array('short-form' => 'l', 'short-has-arg' => TRUE, 'never-post' => TRUE, 'description' => 'URI of the drupal site to use (only needed in multisite environments or when running on an alternate port).', 'example-value' => 'http://example.com:8888');
$options['verbose'] = array('short-form' => 'v', 'context' => 'DRUSH_VERBOSE', 'description' => 'Display extra information about the command.');
$options['debug'] = array('short-form' => 'd', 'context' => 'DRUSH_DEBUG', 'description' => 'Display even more information, including internal messages.');
$options['pipe'] = array('short-form' => 'p', 'description' => "Emit a compact representation of the command for scripting.");
$options['help'] = array('short-form' => 'h', 'description' => "This help system.");
$options['version'] = array('description' => "Show drush version.");
- $options['php'] = array('description' => "The absolute path to your PHP intepreter, if not 'php' in the path.", 'example-value' => '/path/to/file');
+ $options['php'] = array('description' => "The absolute path to your PHP intepreter, if not 'php' in the path.");
$options['interactive'] = array('short-form' => 'ia', 'description' => "Force interactive mode for commands run on multiple targets (e.g. `drush @site1,@site2 cc --ia`).");
if (!$brief) {
$options['quiet'] = array('short-form' => 'q', 'description' => 'Suppress non-error messages.');
- $options['include'] = array('short-form' => 'i', 'short-has-arg' => TRUE, 'context' => 'DRUSH_INCLUDE', 'local-context-only' => TRUE, 'never-post' => TRUE, 'propagate-cli-value' => TRUE, 'merge-pathlist' => TRUE, 'description' => "A list of additional directory paths to search for drush commands.", 'example-value' => '/path/to/directory');
+ $options['include'] = array('short-form' => 'i', 'short-has-arg' => TRUE, 'context' => 'DRUSH_INCLUDE', 'local-context-only' => TRUE, 'never-post' => TRUE, 'propagate-cli-value' => TRUE, 'merge-pathlist' => TRUE, 'description' => "A list of additional directory paths to search for drush commands.");
$options['config'] = array('short-form' => 'c', 'short-has-arg' => TRUE, 'context' => 'DRUSH_CONFIG', 'local-context-only' => TRUE, 'merge-pathlist' => TRUE, 'description' => "Specify an additional config file to load. See example.drushrc.php.");
- $options['user'] = array('short-form' => 'u', 'short-has-arg' => TRUE, 'propagate-cli-value' => TRUE, 'description' => "Specify a Drupal user to login with. May be a name or a number.", 'example-value' => 'name_or_number');
+ $options['user'] = array('short-form' => 'u', 'short-has-arg' => TRUE, 'propagate-cli-value' => TRUE, 'description' => "Specify a Drupal user to login with. May be a name or a number.");
$options['backend'] = array('short-form' => 'b', 'never-propagate' => TRUE, 'description' => "Hide all output and return structured data (internal use only).");
$options['invoke'] = array('hidden' => TRUE, 'description' => 'Invoked from a script; skip option verification.');
- $options['choice'] = array('description' => "Provide an answer to a multiple-choice prompt.", 'example-value' => 'number');
- $options['variables'] = array('description' => "Comma delimited list of name=value pairs. These values take precedence even over settings.php variable overrides.", 'example-value' => 'foo=bar,baz=yaz');
- $options['search-depth'] = array('description' => "Control the depth that drush will search for alias files.", 'example-value' => 'number');
+ $options['choice'] = array('description' => "Provide an answer to a multiple-choice prompt.");
+ $options['variables'] = array('description' => "Comma delimited list of name=value pairs. These values take precedence even over settings.php variable overrides.");
+ $options['search-depth'] = array('description' => "Control the depth that drush will search for alias files.");
$options['ignored-modules'] = array('description' => "Exclude some modules from consideration when searching for drush command files.");
$options['no-label'] = array('description' => "Remove the site label that drush includes in multi-site command output(e.g. `drush @site1,@site2 status`).");
$options['nocolor'] = array('context' => 'DRUSH_NOCOLOR', 'propagate-cli-value' => TRUE, 'description' => "Suppress color highlighting on log messages.");
$options['site-aliases'] = array('hidden' => TRUE, 'merge-associative' => TRUE, 'description' => 'List of site aliases.');
$options['shell-aliases'] = array('hidden' => TRUE, 'merge' => TRUE, 'never-propagate' => TRUE, 'description' => 'List of shell aliases.');
$options['path-aliases'] = array('hidden' => TRUE, 'never-propagate' => TRUE, 'description' => 'Path aliases from site alias.');
- $options['ssh-options'] = array('never-propagate' => TRUE, 'description' => 'A string of extra options that will be passed to the ssh command (e.g. "-p 100")');
$command = array(
'options' => $options,
return $selections;
}
// If the user selected too many options, drop the oldest selection.
- if (isset($max) && count($selections) > $max) {
+ if (count($selections) > $max) {
array_pop($selections);
}
}
*/
function drush_download_file($url, $destination = FALSE, $cache_duration = 0) {
if (drush_get_option('cache') && $cache_duration !== 0 && $cache_dir = drush_directory_cache('download')) {
- $cache_name = str_replace(array(':', '/', '?'), '-', $url);
+ $cache_name = str_replace(array(':', '/'), '-', $url);
$cache_file = $cache_dir . "/" . $cache_name;
// Check for cached, unexpired file.
if (file_exists($cache_file) && filectime($cache_file) > ($_SERVER['REQUEST_TIME']-$cache_duration)) {
drush_log(dt('!name retrieved from cache.', array('!name' => $cache_name)));
+ return $cache_file;
}
else {
if (_drush_download_file($url, $cache_file, TRUE)) {
// Cache was set just by downloading file to right location.
+ return $cache_file;
}
elseif (file_exists($cache_file)) {
drush_log(dt('!name retrieved from an expired cache since refresh failed.', array('!name' => $cache_name)), 'warning');
+ return $cache_file;
}
- else {
- $cache_file = FALSE;
- }
- }
-
- if ($cache_file && copy($cache_file, $destination)) {
- // Copy cached file to the destination
- return $destination;
}
}
elseif ($return = _drush_download_file($url, $destination)) {
$remote_host = drush_get_option('remote-host');
// Get the command early so that we can allow commands to directly handle remote aliases if they wish
$command = drush_parse_command();
- if (isset($command)) {
- drush_command_default_options($command);
- }
- // If the command sets the 'strict-option-handling' flag, then we will remove
+ // If the command sets the 'mask-local-cli-options' flag, then we will remove
// any cli options that appear after the command name form the 'cli' context.
// The cli options that appear before the command name are stored in the
// 'DRUSH_GLOBAL_CLI_OPTIONS' context, so we will just overwrite the cli context
// with this, after doing the neccessary fixup from short-form to long-form options.
- // After we do that, we put back any local drush options identified by $command['options'].
- if (is_array($command) && !empty($command['strict-option-handling'])) {
- $cli_options = drush_get_context('DRUSH_GLOBAL_CLI_OPTIONS', array());
- // Now we are going to sort out any options that exist in $command['options'];
- // we will remove these from DRUSH_COMMAND_ARGS and put them back into the
- // cli options.
- $cli_context = drush_get_context('cli');
- $remove_from_command_args = array();
- foreach ($command['options'] as $option => $info) {
- if (array_key_exists($option, $cli_context)) {
- $cli_options[$option] = $cli_context[$option];
- $remove_from_command_args[$option] = $option;
- }
- }
- if (!empty($remove_from_command_args)) {
- $drush_command_args = array();
- foreach (drush_get_context('DRUSH_COMMAND_ARGS') as $arg) {
- if (!_drush_should_remove_command_arg($arg, $remove_from_command_args)) {
- $drush_command_args[] = $arg;
- }
- }
- drush_set_context('DRUSH_COMMAND_ARGS', $drush_command_args);
- }
- drush_expand_short_form_options($cli_options);
- drush_set_context('cli', $cli_options);
+ if (is_array($command) && !empty($command['mask-local-cli-options'])) {
+ $global_cli_options = drush_get_context('DRUSH_GLOBAL_CLI_OPTIONS', array());
+ drush_expand_short_form_options($global_cli_options);
+ drush_set_context('cli', $global_cli_options);
_drush_bootstrap_global_options();
}
// If the command sets the 'handle-remote-commands' flag, then we will short-circuit
}
/**
- * Determine whether or not an argument should be removed from the
- * DRUSH_COMMAND_ARGS context. This method is used when a Drush
- * command has set the 'strict-option-handling' flag indicating
- * that it will pass through all commandline arguments and any
- * additional options (not known to Drush) to some shell command.
- *
- * Take as an example the following call to core-rsync:
- *
- * drush --yes core-rsync -v -az --exclude-paths='.git:.svn' local-files/ @site:%files
- *
- * In this instance:
- *
- * --yes is a global Drush option
- *
- * -v is an rsync option. It will make rsync run in verbose mode,
- * but will not make Drush run in verbose mode due to the fact that
- * core-rsync sets the 'strict-option-handling' flag.
- *
- * --exclude-paths is a local Drush option. It will be converted by
- * Drush into --exclude='.git' and --exclude='.svn', and then passed
- * on to the rsync command.
- *
- * The parameter $arg passed to this function is one of the elements
- * of DRUSH_COMMAND_ARGS. It will have values such as:
- * -v
- * -az
- * --exclude-paths='.git:.svn'
- * local-files/
- * @site:%files
- *
- * Our job in this function is to determine if $arg should be removed
- * by virtue of appearing in $removal_list. $removal_list is an array
- * that will contain values such as 'exclude-paths'. Both the key and
- * the value of $removal_list is the same.
- */
-function _drush_should_remove_command_arg($arg, $removal_list) {
- foreach ($removal_list as $candidate) {
- if (($arg == "-$candidate") ||
- ($arg == "--$candidate") ||
- (substr($arg,0,strlen($candidate)+3) == "--$candidate=") ) {
- return TRUE;
- }
- }
- return FALSE;
-}
-
-/**
* Used by functions that operate on lists of sites, moving
* information from the source to the destination. Currenlty
* this includes 'drush rsync' and 'drush sql sync'.
if (!is_string($entry['message'])) {
$entry['message'] = implode("\n", (array)$entry['message']);
}
- $entry['message'] = $entry['message'];
$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.
$entry['message'] = $backend_options['#output-label'] . $entry['message'];
}
- // If integrate is FALSE, then log messages are stored in DRUSH_LOG,
- // but are -not- printed to the console.
- if ($backend_options['integrate']) {
- $callback = drush_get_context('DRUSH_LOG_CALLBACK', '_drush_print_log');
- return $callback($entry);
- }
+ return $callback($entry);
}
/**
*/
function drush_environment_lib() {
$lib = drush_get_option('lib', DRUSH_BASE_PATH . '/lib');
- // We tell drush_mkdir that $lib is not required, because otherwise it
- // will throw an error if the folder exists but is not writable. It
- // is okay with us if the $lib dir is not writable by the current
- // user, as it may have already been set up by the user who installed Drush.
- drush_mkdir($lib, FALSE);
+ drush_mkdir($lib);
if (!is_dir($lib)) {
return FALSE;
}
*/
function drush_conf_path($server_uri, $require_settings = TRUE) {
$drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
- if(empty($drupal_root) || empty($server_uri)) {
+ if(!isset($drupal_root) || !isset($server_uri)) {
return NULL;
}
$parsed_uri = parse_url($server_uri);
return FALSE;
}
$type = $creds['driver'];
-
switch (drush_drupal_major_version()) {
case 6:
// Check availability of db extension in PHP and also Drupal support.
break;
case 7:
default:
- $type = ( $type=='oracle' ? 'oci' : $type); // fix PDO driver name, should go away in Drupal 8
// Drupal >=7 requires PDO and drush requires php 5.2, that ships with PDO
// but it may be compiled with --disable-pdo.
if (!class_exists('PDO')) {
}
$constr = sprintf("%s:database=%s;server=%s", $type, $creds['name'], $server);
}
- elseif ($type === 'oci') {
-
- if (empty($creds['port']))
- $creds['port'] = 1521;
-
- if ($creds['host'] == 'USETNS')
- $constr = 'oci:dbname=' . $creds['database'] . ';charset=AL32UTF8';
- else
- $constr = 'oci:dbname=//' . $creds['host'] . ':' . $creds['port'] . '/' . $creds['database'] . ';charset=AL32UTF8';
-
- }
else {
$constr = sprintf("%s:dbname=%s", $type, $creds['name']);
// Use unix_socket if set instead of host:port.
}
/*
- * Return tar executable name specific for the current OS
+ * Return tar executable name specific for the current OS
*/
function drush_get_tar_executable() {
return drush_is_windows() ? "bsdtar.exe" : "tar";
* @return
* An array containing info for all available extensions.
*/
-function drush_get_extensions($include_hidden = TRUE) {
+function drush_get_extensions() {
drush_include_engine('drupal', 'environment');
- $extensions = array_merge(drush_get_modules($include_hidden), drush_get_themes($include_hidden));
+ $extensions = array_merge(drush_get_modules(), drush_get_themes());
foreach ($extensions as $name => $extension) {
$extensions[$name]->label = $extension->info['name'].' ('.$extension->name.')';
if (empty($extension->info['package'])) {
*/
function drush_op_system($exec) {
if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
- drush_print("Calling system($exec);", 0, STDERR);
+ drush_print("Calling system($exec);");
}
+
if (drush_get_context('DRUSH_SIMULATE')) {
return 0;
}
* @see drush_shell_exec.
*/
function _drush_shell_exec($args, $interactive = FALSE) {
- // Do not change the command itself, just the parameters.
+ //do not change the command itself, just the parameters.
for ($x = 1; $x < sizeof($args); $x++) {
$args[$x] = drush_escapeshellarg($args[$x]);
}
$command = call_user_func_array('sprintf', $args);
if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
- drush_print('Executing: ' . $command, 0, STDERR);
+ drush_print('Executing: ' . $command);
}
+
if (!drush_get_context('DRUSH_SIMULATE')) {
if ($interactive) {
$result = drush_shell_proc_open($command);
* @param string $command
* An optional bash fragment.
* @param string $cd
- * An optional directory to change into before executing the $command. Set to
- * boolean TRUE to change into $site['root'] if available.
+ * An optional directory to change into before executing the $command.
* @return string
* A string suitable for execution with drush_shell_remote_exec().
*/
*/
function drush_shell_proc_open($cmd) {
if (drush_get_context('DRUSH_VERBOSE') || drush_get_context('DRUSH_SIMULATE')) {
- drush_print("Calling proc_open($cmd);", 0, STDERR);
+ drush_print("Calling proc_open($cmd);");
}
if (!drush_get_context('DRUSH_SIMULATE')) {
$process = proc_open($cmd, array(0 => STDIN, 1 => STDOUT, 2 => STDERR), $pipes);
// Double up existing backslashes
$arg = preg_replace('/\\\/', '\\\\\\\\', $arg);
- // Double up double quotes
- $arg = preg_replace('/"/', '""', $arg);
+ // Escape double quotes.
+ $arg = preg_replace('/"/', '^"', $arg);
- // Double up percents.
- $arg = preg_replace('/%/', '%%', $arg);
+ // Escape single quotes.
+ $arg = preg_replace('/\'/', '^\'', $arg);
// Add surrounding quotes.
$arg = '"' . $arg . '"';
* TRUE if browser was opened, FALSE if browser was disabled by the user or a,
* default browser could not be found.
*/
-function drush_start_browser($uri = NULL, $sleep = FALSE) {
+function drush_start_browser($uri = NULL) {
if (!parse_url($uri, PHP_URL_HOST)) {
$site = drush_get_context('DRUSH_URI');
$host = parse_url($site, PHP_URL_HOST);
$browser = FALSE;
}
}
- $prefix = '';
- if ($sleep) {
- $prefix = 'sleep ' . $sleep . ' && ';
- }
if ($browser && !drush_get_context('DRUSH_SIMULATE')) {
$pipes = array();
drush_log(dt('Opening browser at !uri', array('!uri' => $uri)));
- proc_close(proc_open($prefix . $browser . ' ' . drush_escapeshellarg($uri) . ' 2> ' . drush_bit_bucket() . ' &', array(), $pipes));
+ proc_close(proc_open($browser . ' ' . drush_escapeshellarg($uri) . ' 2> ' . drush_bit_bucket() . ' &', array(), $pipes));
return TRUE;
}
}
* @{
*/
-/**
- * Behavior for drush_copy_dir() and drush_move_dir() when destinations exist.
- */
-define('FILE_EXISTS_ABORT', 0);
-define('FILE_EXISTS_OVERWRITE', 1);
-define('FILE_EXISTS_MERGE', 2);
-
/**
* Determines whether the provided path is absolute or not
* on the specified O.S. -- starts with "/" on *nix, or starts
}
/**
- * Makes sure the path has only path separators native for the current operating system
- */
-function drush_normalize_path($path) {
- if (drush_is_windows()) {
- $path = str_replace('/', '\\', strtolower($path));
- }
- else {
- $path = str_replace('\\', '/', $path);
- }
- return drush_trim_path($path);
-}
-
-/**
- * Calculates a single md5 hash for all files a directory (incuding subdirectories)
- */
-function drush_dir_md5($dir) {
- $flist = drush_scan_directory($dir, '/./', array('.', '..'), 0, TRUE, 'filename', 0, TRUE);
- $hashes = array();
- foreach ($flist as $f) {
- $sum = array();
- exec('cksum ' . escapeshellarg($f->filename), $sum);
- $hashes[] = trim(str_replace(array($dir), array(''), $sum[0]));
- }
- sort($hashes);
- return md5(implode("\n", $hashes));
-}
-
-/**
* Deletes the provided file or folder and everything inside it.
* Usually respects read-only files and folders. To do a forced delete use
* drush_delete_tmp_dir() or set the parameter $forced.
if ($item == '.' || $item == '..') {
continue;
}
- if ($force) {
- @chmod($dir, 0777);
- }
if (!drush_delete_dir($dir . '/' . $item, $force)) {
return FALSE;
}
* $src = "/b/a" and $dest = "/c/a". To copy "a" to "/c" and rename
* it to "d", then $dest = "/c/d".
* @param $overwrite
- * Action to take if destination already exists.
- * - FILE_EXISTS_OVERWRITE - completely removes existing directory.
- * - FILE_EXISTS_ABORT - aborts the operation.
- * - FILE_EXISTS_MERGE - Leaves existing files and directories in place.
+ * If TRUE, the destination will be deleted if it exists.
* @return
* TRUE on success, FALSE on failure.
*/
-function drush_copy_dir($src, $dest, $overwrite = FILE_EXISTS_ABORT) {
+function drush_copy_dir($src, $dest, $overwrite = FALSE) {
// Preflight based on $overwrite if $dest exists.
if (file_exists($dest)) {
- if ($overwrite === FILE_EXISTS_OVERWRITE) {
+ if ($overwrite) {
drush_op('drush_delete_dir', $dest, TRUE);
}
- elseif ($overwrite === FILE_EXISTS_ABORT) {
+ else {
return drush_set_error('DRUSH_DESTINATION_EXISTS', dt('Destination directory !dest already exists.', array('!dest' => $dest)));
}
- elseif ($overwrite === FILE_EXISTS_MERGE) {
- // $overwrite flag may indicate we should merge instead.
- drush_log(dt('Merging existing !dest directory', array('!dest' => $dest)));
- }
}
// $src readable?
if (!is_readable($src)) {
return TRUE;
}
- return drush_set_error('DRUSH_COPY_DIR_FAILURE', dt('Unable to copy !src to !dest.', array('!src' => $src, '!dest' => $dest)));
+ return drush_set_error('DRUSH_COPY_DIR_FAILURE', dt('Unable to copy !src to !dest.', array('src' => $src, 'dest' => $dest)));
}
/**
return FALSE;
}
- // Preserve execute permission.
+ // Preserve permissions
if (!drush_is_windows()) {
- // Get execute bits of $src.
- $execperms = fileperms($src) & 0111;
- // Apply execute permissions if any.
- if ($execperms > 0) {
- $perms = fileperms($dest) | $execperms;
- chmod($dest, $perms);
- }
+ chmod($dest, intval(fileperms($src), 8));
}
return TRUE;
* The temporary file will be automatically deleted when drush exits.
*
* @param string $data
- * @param string $suffix
- * Append string to filename. use of this parameter if is discouraged. @see
- * drush_tempnam().
* @return string
* A path to the file.
*/
-function drush_save_data_to_temp_file($data, $suffix = NULL) {
+function drush_save_data_to_temp_file($data) {
static $fp;
- $file = drush_tempnam('drush_', NULL, $suffix);
+ $file = drush_tempnam('drush_');
$fp = fopen($file, "w");
fwrite($fp, $data);
$meta_data = stream_get_meta_data($fp);
* it will be deleted when drush exits. Whenever possible,
* drush_save_data_to_temp_file() should be used instead
* of this function.
- *
- * @param string suffix
- * Append this suffix to the filename. Use of this parameter is discouraged as
- * it can break the guarantee of tempname(). See http://www.php.net/manual/en/function.tempnam.php#42052.
- * Originally added to support Oracle driver.
*/
-function drush_tempnam($pattern, $tmp_dir = NULL, $suffix = '') {
+function drush_tempnam($pattern, $tmp_dir = NULL) {
if ($tmp_dir == NULL) {
$tmp_dir = drush_find_tmp();
}
$tmp_file = tempnam($tmp_dir, $pattern);
drush_register_file_for_deletion($tmp_file);
- return $tmp_file . $suffix;
+ return $tmp_file;
}
/**
}
/**
- * Simple helper function to append data to a given file.
- *
- * @param string $file
- * The full path to the file to append the data to.
- * @param string $data
- * The data to append.
- *
- * @return boolean
- * TRUE on success, FALSE in case of failure to open or write to the file.
- */
-function drush_file_append_data($file, $data) {
- if (!$fd = fopen($file, 'a+')) {
- drush_set_error(dt("ERROR: fopen(@file, 'ab') failed", array('@file' => $file)));
- return FALSE;
- }
- if (!fwrite($fd, $data)) {
- drush_set_error(dt("ERROR: fwrite(@file) failed", array('@file' => $file)) . '<pre>' . $data);
- return FALSE;
- }
- return TRUE;
-}
-
-/**
* @} End of "defgroup filesystemfunctions".
*/
case 'json':
$output = drush_json_encode($input);
break;
+ case 'properties':
+ $output = drush_format_properties($input);
+ break;
case 'print_r':
default:
if (is_string($input)) {
*/
function dt($string, $args = array()) {
if (function_exists('t') && (drush_drupal_version() >= 7 || function_exists('theme'))) {
- if (function_exists('drupal_classloader')) {
- drupal_classloader();
- }
return t($string, $args);
}
else {
}
/**
+ * Fold input structure into array of dot separated key path = value
+ *
+ * Having output like this gives users the possibility to grep the output
+ * and still see the context.
+ *
+ * Output looks like
+ *
+ * node.bundles.page.label = Basic page
+ * node.bundles.page.admin.path = admin/structure/types/manage/%node_type
+ *
+ * @staticvar array $object_hash_list
+ * Prevent object property recursion
+ * @param array $input
+ * (sub)tree to process
+ * @param type $recurse
+ * Prevent recursion
+ * @return type array
+ * Array of key => value (x.y.z => string)
+ */
+function drush_format_properties(array $input, $recurse = FALSE) {
+ // We need to prevent recursive object parsing
+ static $object_hash_list;
+ if (!$recurse) {
+ // Reset hash
+ $object_hash_list = array();
+ }
+
+ if ($recurse == FALSE && !is_array($input)) {
+ return $input;
+ }
+ $keyed_results = array();
+ foreach ($input as $key => $value) {
+ if (is_array($value) || _drush_visit_object(&$object_hash_list, $value)) {
+ $temp = drush_format_properties((array) $value, TRUE);
+ foreach ($temp as $sub => $sub_value) {
+ $keyed_results["$key.$sub"] = $sub_value;
+ }
+ }
+ else {
+ $keyed_results["$key"] = $value;
+ }
+ }
+ if (!$recurse) {
+ drush_log("Format properties: Objects encountered: " . count($object_hash_list));
+ // We're done
+ $text = '';
+ foreach ($keyed_results as $key => $value) {
+ $text .= "$key = $value\n";
+ }
+ return $text;
+ }
+ return $keyed_results;
+}
+
+/**
+ * Helper to prevent recursion on objects
+ *
+ * @see drush_format_properties().
+ *
+ * @param type $cache
+ * Reference array
+ * @param type $value
+ * A value of any kind
+ * @return boolean
+ * The current $value is not an object or not visited yet
+ */
+function _drush_visit_object(&$object_hash_list, $value) {
+ if (!is_array($object_hash_list)) {
+ $object_hash_list = array();
+ }
+ if (is_object($value)) {
+ $hash = spl_object_hash($value);
+ if (isset($object_hash_list[$hash])) {
+ return FALSE;
+ }
+ $object_hash_list[$hash] = $hash;
+ return TRUE;
+ }
+ else if (is_array($value)) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
* @} End of "defgroup outputfunctions".
*/
drush_set_arguments($args);
return TRUE;
}
- // Return false to indicate that no site alias was specified.
- return FALSE;
-}
-function drush_sitealias_check_site_env() {
- $site = drush_get_context('DRUSH_TARGET_SITE_ALIAS');
- if (empty($site)) {
- $site_env = drush_sitealias_site_get();
- if (!empty($site_env) && (_drush_sitealias_set_context_by_name($site_env))) {
- drush_set_context('DRUSH_TARGET_SITE_ALIAS', $site_env);
- return TRUE;
- }
+ $site_env = drush_sitealias_site_get();
+ if (!empty($site_env) && (_drush_sitealias_set_context_by_name($site_env))) {
+ return TRUE;
}
// Return false to indicate that no site alias was specified.
return FALSE;
// Cases 1.) - 4.):
// We will check for a site specification if the alias has at least
// two characters from the set '@', '/', '#'.
- if ((strpos($alias, '@') === FALSE ? 0 : 1) + ((strpos($alias, '/') === FALSE && strpos($alias, '\\') === FALSE) ? 0 : 1) + (strpos($alias, '#') === FALSE ? 0 : 1) >= 2) {
- if ((substr($alias,0,7) != 'http://') && !drush_is_absolute_path($alias)) {
+ if ((strpos($alias, '@') === FALSE ? 0 : 1) + (strpos($alias, '/') === FALSE ? 0 : 1) + (strpos($alias, '#') === FALSE ? 0 : 1) >= 2) {
+ if ((substr($alias,0,7) != 'http://') && (substr($alias,0,1) != '/')) {
// Add on a scheme so that "user:pass@server" will always parse correctly
$parsed = parse_url('http://' . $alias);
}
- else if (drush_is_windows() && drush_is_absolute_path($alias)) {
- // On windows if alias begins with a filesystem path we must add file:// scheme to make it parse correcly
- $parsed = parse_url('file:///' . $alias);
- }
else {
$parsed = parse_url($alias);
}
if ($add_path != NULL) {
if (!is_array($add_path)) {
- $add_path = explode(PATH_SEPARATOR, $add_path);
- }
- // Normalize path to make sure we don't add the same path twice on
- // windows due to different spelling. e.g. c:\tmp and c:/tmp
- foreach($add_path as &$path) {
- $path = drush_normalize_path($path);
+ $add_path = explode(':', $add_path);
}
$site_paths = array_unique(array_merge($site_paths, $add_path));
}
// Only aliases--those named entities that begin with '@'--can be loaded this way.
// We also skip any alias that has already been loaded.
if ((substr($alias,0,1) == '@') && !array_key_exists($alias,$all_site_aliases)) {
+ drush_log(dt('Load alias !alias', array('!alias' => $alias)));
$aliasname = substr($alias,1);
$result = _drush_sitealias_find_and_load_alias($aliasname, $alias_path_context);
if (!empty($result)) {
$alias_options = array('site-aliases' => array($aliasname => $result));
_drush_sitealias_add_inherited_values($alias_options['site-aliases']);
drush_set_config_special_contexts($alias_options);
- drush_log(dt('Loaded alias !alias from file !file', array('!alias' => $alias, '!file' => $result['#file'])));
}
}
}
}
/**
- * Get the name of the current bootstrapped site
- */
-function drush_sitealias_bootstrapped_site_name() {
- $site_name = NULL;
- $self_record = drush_sitealias_get_record('@self');
- if (array_key_exists('#name', $self_record)) {
- $site_name = $self_record['#name'];
- }
- if (!isset($site_name) || ($site_name == '@self')) {
- $drupal_root = drush_get_context('DRUSH_SELECTED_DRUPAL_ROOT');
- if (isset($drupal_root)) {
- $drupal_uri = drush_get_context('DRUSH_SELECTED_URI', 'default');
- $drupal_uri = str_replace('http://', '', $drupal_uri);
- // TODO: Maybe use _drush_sitealias_find_local_alias_name?
- $site_name = $drupal_root . '#' . $drupal_uri;
- }
- }
- return $site_name;
-}
-
-/**
* If there are any path aliases (items beginning with "%") in the test
* string, then resolve them as path aliases and add them to the provided
* alias record.
// Optimization: if we're already bootstrapped to the
// site specified by $alias_record, then we can just
// call _core_site_status_table() rather than use backend invoke.
- if (drush_sitealias_is_bootstrapped_site($alias_record) && drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) {
+ if (drush_sitealias_is_bootstrapped_site($alias_record)) {
+ // Make sure that we are bootstrapped at least to the 'site'
+ // level, and include file.inc to insure that we have access
+ // to the %file path.
+ if (drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_SITE)) {
+ include_once DRUSH_DRUPAL_CORE . '/includes/file.inc';
+ }
$status_values = _core_site_status_table($project_list);
}
else {
* A directory, such as domain.com.8080.drupal
*/
function drush_sitealias_uri_to_site_dir($uri) {
- $uri = str_replace('http://', '', $uri);
- if (drush_is_windows()) {
- // Handle absolute paths on windows
- $uri = str_replace(array(':/', ':\\'), array('.', '.'), $uri);
- }
- return str_replace(array('/', ':', '\\'), array('.', '.', '.'), $uri);
+ return str_replace(array('http://', '/', ':'), array('', '.', '.'), $uri);
}
/**
* with the path to pass to rsync (including the machine specifier)
* in the 'evaluated-path' item.
*/
-function drush_sitealias_evaluate_path($path, &$additional_options, $local_only = FALSE, $os = NULL, $command_specific_prefix = '') {
+function drush_sitealias_evaluate_path($path, &$additional_options, $local_only = FALSE, $os = NULL) {
$site_alias_settings = array();
$path_aliases = array();
$remote_user = '';
// not want to use it here. The paths passed to rsync should always be
// escaped per the LOCAL rules, without regard to the remote platform type.
$site_alias_settings = drush_sitealias_get_record($alias);
- if (!empty($command_specific_prefix)) {
- drush_sitealias_command_default_options($site_alias_settings, $command_specific_prefix);
- }
}
if (!empty($site_alias_settings)) {
}
}
- // TOD: The code below is a little rube-goldberg-ish, and needs to be
- // reworked. core-rsync will call this function twice: once to
- // evaluate the destination, and then again to evaluate the source. Things
- // get odd with --exclude-paths, especially in conjunction with command-specific
- // and the --exclude-files option. @see testCommandSpecific()
-
// If the --exclude-other-sites option is specified, then
- // convert that into --include-paths='%site' and --exclude-sites.
+ // convert that into --include-path='%site' and --exclude-sites.
if (drush_get_option_override($additional_options, 'exclude-other-sites', FALSE) && !drush_get_option_override($additional_options, 'exclude-other-sites-processed', FALSE, 'process')) {
- $include_path_option = drush_get_option_override($additional_options, 'include-paths', '');
- $additional_options['include-paths'] = '%site';
- if (!empty($include_path_option)) {
- // We use PATH_SEPARATOR here because we are later going to explicitly explode() this variable using PATH_SEPARATOR.
- $additional_options['include-paths'] .= PATH_SEPARATOR . $include_path_option;
- }
+ $additional_options['include-path'] = '%site,' . drush_get_option_override($additional_options, 'include-path', '');
$additional_options['exclude-sites'] = TRUE;
$additional_options['exclude-other-sites-processed'] = TRUE;
}
- else {
- unset($additional_options['include-paths']);
- }
// If the --exclude-files option is specified, then
- // convert that into --exclude-paths='%files'.
+ // convert that into --exclude-path='%files'.
if (drush_get_option_override($additional_options, 'exclude-files', FALSE) && !drush_get_option_override($additional_options, 'exclude-files-processed', FALSE, 'process')) {
- $exclude_path_option = drush_get_option_override($additional_options, 'exclude-paths', '');
- $additional_options['exclude-paths'] = '%files';
- if (!empty($exclude_path_option)) {
- // We use PATH_SEPARATOR here because we are later going to explicitly explode() this variable using PATH_SEPARATOR.
- $additional_options['exclude-paths'] .= PATH_SEPARATOR . $exclude_path_option;
- }
+ $additional_options['exclude-path'] = '%files,' . drush_get_option_override($additional_options, 'exclude-path', '');
$additional_options['exclude-files-processed'] = TRUE;
}
- else {
- unset($additional_options['exclude-paths']);
- }
// If there was no site specification given, and the
// machine is local, then try to look
// in it, so that resolution is only done if the path alias is referenced.
// Therefore, we can concatenate without worrying too much about the structure of
// this variable's contents.
- $include_path = drush_get_option_override($additional_options, 'include-paths', '');
- $exclude_path = drush_get_option_override($additional_options, 'exclude-paths', '');
+ $include_path = drush_get_option_override($additional_options, 'include-path', '');
+ $exclude_path = drush_get_option_override($additional_options, 'exclude-path', '');
$resolve_path = $path . $include_path . $exclude_path;
// Resolve path aliases such as %files, if any exist in the path
if (!empty($resolve_path)) {
// Fill in path aliases in the path, the include path and the exclude path.
$path = str_replace(array_keys($full_path_aliases), array_values($full_path_aliases), $path);
if (!empty($include_path)) {
- drush_set_option('include-paths', str_replace(array_keys($path_aliases), array_values($path_aliases), $include_path));
+ drush_set_option('include-path', str_replace(array_keys($path_aliases), array_values($path_aliases), $include_path));
}
if (!empty($exclude_path)) {
- drush_set_option('exclude-paths', str_replace(array_keys($path_aliases), array_values($path_aliases), $exclude_path));
+ drush_set_option('exclude-path', str_replace(array_keys($path_aliases), array_values($path_aliases), $exclude_path));
}
// Next make the rsync path, which includes the machine
// and path components together.
sql-cli:
sql-connect:
-sql-query: FAIR. Implicit by site-install
+sql-query:
sql-dump: FAIR. Implicitly tested by siteUpgradeTest and sqlSyncTest.
sql-sync: GOOD. testLocalSqlSync(). Implicitly tested by siteUpgradeTest.
need test: %dump, %dump-dir, --dump, --dump-dir, --source-dump, --target-dump, --source-dump-dir and --target-dump-dir and permutations of same used together.
-sql-drop: FAIR. Implicit by site-install
+sql-drop:
updatedb: GOOD. Implicitly tested siteUpgradeTest.
'destination' => $dump_dest,
);
$this->drush('archive-dump', array($uri), $options);
- $exec = sprintf('file %s%s%s', UNISH_SANDBOX, DIRECTORY_SEPARATOR, $dump_dest);
+ $exec = sprintf('file %s/%s', UNISH_SANDBOX, $dump_dest);
$this->execute($exec);
$output = $this->getOutput();
- $sep = self::is_windows() ? ';' : ':';
- $expected = UNISH_SANDBOX . DIRECTORY_SEPARATOR . "dump.tar.gz$sep gzip compressed data, from";
-
- $this->assertStringStartsWith($expected, $output);
+ $expected = UNISH_SANDBOX . "/dump.tar.gz: gzip compressed data, from Unix";
+ $this->assertEquals($expected, $output);
// Untar it, make sure it looks right.
- $untar_dest = UNISH_SANDBOX . DIRECTORY_SEPARATOR . 'untar';
- $tar = self::get_tar_executable();
- $exec = sprintf("mkdir %s && cd %s && $tar -xzf %s%s%s", $untar_dest, $untar_dest, UNISH_SANDBOX, DIRECTORY_SEPARATOR, $dump_dest);
+ $exec = sprintf('tar -xzf %s/%s', UNISH_SANDBOX, $dump_dest);
+ $untar_dest = UNISH_SANDBOX . '/untar';
+ $exec = sprintf('mkdir %s && cd %s && tar xzf %s/%s', $untar_dest, $untar_dest, UNISH_SANDBOX, $dump_dest);
$this->execute($exec);
if (strpos(UNISH_DB_URL, 'mysql') !== FALSE) {
$this->execute(sprintf('head %s/unish_%s.sql | grep "MySQL dump"', $untar_dest, $uri));
* General handling of site aliases will be in sitealiasTest.php.
*/
function testOrigin() {
- $exec = sprintf('%s %s version arg1 arg2 --simulate --ssh-options=%s | grep ssh', UNISH_DRUSH, self::escapeshellarg('user@server/path/to/drupal#sitename'), self::escapeshellarg('-i mysite_dsa'));
+ $exec = sprintf('%s %s version arg1 arg2 --simulate --ssh-options=%s 2>/dev/null | grep ssh', self::escapeshellarg(UNISH_DRUSH), self::escapeshellarg('user@server/path/to/drupal#sitename'), self::escapeshellarg('-i mysite_dsa'));
$this->execute($exec);
- $bash = $this->escapeshellarg('drush --invoke --simulate --uri=sitename --root=/path/to/drupal version arg1 arg2 2>&1');
- $expected = "Simulating backend invoke: ssh -i mysite_dsa user@server $bash 2>&1";
+ // $expected might be different on non unix platforms. We shall see.
+ $expected = "Simulating backend invoke: ssh -i mysite_dsa user@server 'drush --simulate --uri=sitename --root=/path/to/drupal version arg1 arg2 --invoke 2>&1' 2>&1";
$output = $this->getOutput();
$this->assertEquals($expected, $output, 'Expected ssh command was built');
}
*/
function testTarget() {
$stdin = json_encode(array('filter'=>'sql'));
- $exec = sprintf('echo %s | %s version --backend', self::escapeshellarg($stdin), UNISH_DRUSH);
+ $exec = sprintf('echo %s | %s help --backend 2>/dev/null', self::escapeshellarg($stdin), self::escapeshellarg(UNISH_DRUSH));
$this->execute($exec);
$parsed = parse_backend_output($this->getOutput());
$this->assertTrue((bool) $parsed, 'Successfully parsed backend output');
$this->assertArrayHasKey('object', $parsed);
$this->assertEquals(self::EXIT_SUCCESS, $parsed['error_status']);
// This assertion shows that `help` was called and that stdin options were respected.
- $this->assertStringStartsWith('drush ', $parsed['output']);
+ $this->assertStringStartsWith('SQL commands', $parsed['output']);
$this->assertEquals('Bootstrap to phase 0.', $parsed['log'][0]['message']);
// Check error propogation by requesting an invalid command (missing Drupal site).
- $this->drush('core-cron', array(), array('backend' => NULL), NULL, NULL, self::EXIT_ERROR);
+ $exec = sprintf('%s core-cron --backend 2>/dev/null', self::escapeshellarg(UNISH_DRUSH));
+ $this->execute($exec, self::EXIT_ERROR);
$parsed = parse_backend_output($this->getOutput());
$this->assertEquals(1, $parsed['error_status']);
$this->assertArrayHasKey('DRUSH_NO_DRUPAL_ROOT', $parsed['error_log']);
* - 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 --nocolor 2>&1', UNISH_DRUSH);
+ $exec = sprintf('%s core-status --backend --nocolor 2>&1', self::escapeshellarg(UNISH_DRUSH));
$this->execute($exec);
$output = $this->getOutput();
// assert that $parsed has 'foo' and not 'bar'
$this->assertEquals("'foo'", var_export($parsed['object'], TRUE));
}
-
- /**
- * Covers the following target responsibilities.
- * - Insures that the backend option 'invoke-multiple' will cause multiple commands to be executed.
- * - Insures that the right number of commands run.
- * - Insures that the 'concurrent'-format result array is returned.
- * - Insures that correct results are returned from each command.
- */
- function testBackendInvokeMultiple() {
- $options = array(
- 'backend' => NULL,
- 'include' => dirname(__FILE__), // Find unit.drush.inc commandfile.
- );
- $php = "\$values = drush_invoke_process('@none', 'unit-return-options', array('value'), array('x' => 'y'), array('invoke-multiple' => '3')); return \$values;";
- $this->drush('php-eval', array($php), $options);
- $parsed = parse_backend_output($this->getOutput());
- // assert that $parsed has a 'concurrent'-format output result
- $this->assertEquals('concurrent', implode(',', array_keys($parsed['object'])));
- // assert that the concurrent output has indexes 0, 1 and 2 (in any order)
- $concurrent_indexes = array_keys($parsed['object']['concurrent']);
- sort($concurrent_indexes);
- $this->assertEquals('0,1,2', implode(',', $concurrent_indexes));
- foreach ($parsed['object']['concurrent'] as $index => $values) {
- // assert that each result contains 'x' => 'y' and nothing else
- $this->assertEquals("array (
- 'x' => 'y',
-)", var_export($values['object'], TRUE));
- }
- }
+
/**
* Covers the following target responsibilities.
* - Insures that arrays are stripped when using --backend mode's method GET
)", var_export($parsed['object'], TRUE));
}
}
-
-class backendUnitCase extends Drush_UnitTestCase {
-
- /**
- * Covers the following target responsibilities.
- * - Insures that drush_invoke_process called with fork backend set is able
- * to invoke a non-blocking process.
- */
- function testBackendFork() {
- // Need to set DRUSH_COMMAND so that drush will be called and not phpunit
- define('DRUSH_COMMAND', UNISH_DRUSH);
-
- // Ensure that file that will be created by forked process does not exist
- // before invocation.
- $test_file = UNISH_SANDBOX . '/fork_test.txt';
- if (file_exists($test_file)) {
- unlink($test_file);
- }
-
- // Sleep for a milisecond, then create the file
- $ev_php = "usleep(1000);fopen('$test_file','a');";
- drush_invoke_process("@none", "ev", array($ev_php), array(), array("fork" => TRUE));
-
- // Test file does not exist immediate after process forked
- $this->assertEquals(file_exists($test_file), FALSE);
- // Check every 100th of a second for up to 4 seconds to see if the file appeared
- $repetitions = 400;
- while (!file_exists($test_file) && ($repetitions > 0)) {
- usleep(10000);
- }
- // Assert that the file did finally appear
- $this->assertEquals(file_exists($test_file), TRUE);
- }
-}
+++ /dev/null
-<?php
-
-/*
-* @file
-* Assure that context API behaves as designed. Mostly implicitly tested, but we
-* do have some edges that need explicit testing.
-*
-* @see drush/includes/context.inc.
-*/
-
-class commandSpecificCase extends Drush_CommandTestCase {
-
- /**
- * Try to write a tiny drushrc.php to each place that drush checks. Also
- * write a sites/dev/aliases.drushrc.php file to the sandbox.
- */
- function setUp() {
- parent::setUp();
-
- $path = UNISH_SANDBOX . '/aliases.drushrc.php';
- $aliases['site1'] = array(
- 'root' => UNISH_SANDBOX,
- 'uri' => 'site1.com',
- 'source-command-specific' => array(
- 'core-rsync' => array(
- 'exclude-paths' => 'excluded_by_source',
- ),
- ),
- 'target-command-specific' => array(
- 'core-rsync' => array(
- 'exclude-paths' => 'excluded_by_target',
- ),
- ),
- 'path-aliases' => array(
- '%files' => 'sites/default/files',
- ),
- );
- $contents = $this->file_aliases($aliases);
- $return = file_put_contents($path, $contents);
- }
-
- function testCommandSpecific() {
- $options = array(
- 'alias-path' => UNISH_SANDBOX,
- 'simulate' => NULL,
- 'include-vcs' => NULL,
- );
- $this->drush('core-rsync', array('/tmp', '@site1'), $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1');
- $output = trim($this->getOutput());
- $this->assertContains('excluded_by_target', $output);
- $this->drush('core-rsync', array('@site1', '/tmp'), $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1');
- $output = trim($this->getOutput());
- $this->assertContains('excluded_by_source', $output);
- $this->drush('core-rsync', array('@site1', '@site1'), $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1');
- $output = trim($this->getOutput());
- $this->assertContains('excluded_by_target', $output);
- // Now do that all again with 'exclude-files'
- $options['exclude-files'] = NULL;
- $this->drush('core-rsync', array('/tmp', '@site1'), $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1');
- $output = trim($this->getOutput());
- $this->assertContains('sites/default/files', $output);
- $this->assertContains('excluded_by_target', $output);
- $this->assertNotContains('include-vcs', $output);
- $this->assertNotContains('exclude-paths', $output);
- $this->assertNotContains('exclude-files-processed', $output);
- $this->drush('core-rsync', array('@site1', '/tmp'), $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1');
- $output = trim($this->getOutput());
- $this->assertContains('sites/default/files', $output);
-// This one does not work. @see drush_sitealias_evaluate_path
-// $this->assertContains('excluded_by_source', $output);
- $this->assertNotContains('include-vcs', $output);
- $this->assertNotContains('exclude-paths', $output);
- $this->assertNotContains('exclude-files-processed', $output);
- $this->drush('core-rsync', array('@site1', '@site1'), $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1');
- $output = trim($this->getOutput());
- $this->assertContains('sites/default/files', $output);
- $this->assertContains('excluded_by_target', $output);
- $this->assertNotContains('include-vcs', $output);
- $this->assertNotContains('exclude-paths', $output);
- $this->assertNotContains('exclude-files-processed', $output);
- }
-}
$include_path = dirname(__FILE__) . '/hooks/magic_help_alter';
$this->drush('version', array(), array('include' => $include_path, 'pipe' => NULL, 'magic' => '1234', 'strict' => NULL));
}
-
- /**
- * Assert that errors are thrown for commands with missing callbacks.
- */
- public function testMissingCommandCallback() {
- $options = array(
- 'include' => dirname(__FILE__), // Find unit.drush.inc commandfile.
- //'show-invoke' => TRUE,
- );
- $this->drush('missing-callback', array(), $options, NULL, NULL, self::EXIT_ERROR);
- }
-
- /**
- * Assert that commands depending on unknown commandfiles are detected.
- */
- public function testMissingDrushDependency() {
- $options = array(
- 'include' => dirname(__FILE__), // Find unit.drush.inc commandfile.
- 'backend' => NULL, // To obtain and parse the error log.
- );
- $this->drush('unit-drush-dependency', array(), $options, NULL, NULL, self::EXIT_ERROR);
- $parsed = parse_backend_output($this->getOutput());
- $this->assertArrayHasKey("DRUSH_COMMANDFILE_DEPENDENCY_ERROR", $parsed['error_log']);
- }
-
- /**
- * Assert that commands in disabled modules are detected.
- */
- public function testDisabledModule() {
- $sites = $this->setUpDrupal(1, TRUE);
- $uri = key($sites);
- $root = $this->webroot();
- $options = array(
- 'root' => $root,
- 'uri' => $uri,
- );
- $this->drush('pm-download', array('devel'), $options);
- $options += array(
- 'backend' => NULL, // To obtain and parse the error log.
- );
- $this->drush('devel-download', array(), $options, NULL, NULL, self::EXIT_ERROR);
- $parsed = parse_backend_output($this->getOutput());
- $this->assertArrayHasKey("DRUSH_COMMAND_DEPENDENCY_ERROR", $parsed['error_log']);
- }
}
// Test overall context sensitivity - almost all of these are cache hits.
// No context (i.e. "drush <tab>"), should list aliases and commands.
- $this->verifyComplete('""', '@dev', 'ws');
+ $this->verifyComplete("''", '@dev', 'ws');
// Site alias alone.
$this->verifyComplete('@', '@dev', '@stage');
// Command alone.
// Note: this is checked implicitly by the argument cache testing above.
// Site alias + command + file/directory argument. This is a command
// argument we have not used so far, so a cache miss is expected.
-
- if ($this->is_windows()) {
- $this->markTestSkipped('Complete tests not fully working nor needed on Windows.');
- }
-
$this->verifyComplete('archive-restore aard', 'aard wolf.tar.gz', 'aardvark/', FALSE);
// Site alias + command + file/directory argument with quoting.
$this->verifyComplete('archive-restore aard\ w', 'aard\ wolf.tar.gz', 'aard\ wolf.tar.gz');
$this->drush('core-status', array('Drush configuration'), $options);
$output = trim($this->getOutput());
$loaded = explode(' ', $output);
- $loaded = array_map(array(&$this, 'convert_path'), $loaded);
$this->assertSame($this->written, $loaded);
}
* Tests for core commands.
*/
class coreCase extends Drush_CommandTestCase {
- /**
- * Test to see if rsync @site:%files calculates the %files path correctly.
- * This tests the non-optimized code path in drush_sitealias_resolve_path_references.
- */
- function testRsyncPercentFiles() {
- $this->setUpDrupal(1, TRUE);
- $root = $this->webroot();
- $site = key($this->sites);
- $options = array(
- 'root' => $root,
- 'uri' => key($this->sites),
- 'simulate' => NULL,
- 'include-conf' => NULL,
- 'include-vcs' => NULL,
- 'yes' => NULL,
- 'invoke' => NULL, // invoke from script: do not verify options
- );
- $this->drush('core-rsync', array("@$site:%files", "/tmp"), $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1;');
- $output = $this->getOutput();
- $level = $this->log_level();
- $pattern = in_array($level, array('verbose', 'debug')) ? "Calling system(rsync -e 'ssh ' -akzv --stats --progress --yes --invoke %s /tmp);" : "Calling system(rsync -e 'ssh ' -akz --yes --invoke %s /tmp);";
- $expected = sprintf($pattern, UNISH_SANDBOX . "/web/sites/$site/files");
- $this->assertEquals($expected, $output);
- }
-
- /**
- * Test to see if the optimized code path in drush_sitealias_resolve_path_references
- * that avoids a call to backend invoke when evaluating %files works.
- */
- function testPercentFilesOptimization() {
- $this->setUpDrupal(1, TRUE);
- $root = $this->webroot();
- $site = key($this->sites);
- $options = array(
- 'root' => $root,
- 'uri' => key($this->sites),
- 'simulate' => NULL,
- 'include-conf' => NULL,
- 'include-vcs' => NULL,
- 'yes' => NULL,
- 'invoke' => NULL, // invoke from script: do not verify options
- );
- $php = '$a=drush_sitealias_get_record("@' . $site . '"); drush_sitealias_resolve_path_references($a, "%files"); print_r($a["path-aliases"]["%files"]);';
- $this->drush('ev', array($php), $options);
- $output = $this->getOutput();
- $expected = "sites/dev/files";
- $this->assertEquals($expected, $output);
- }
/*
* Test standalone php-script scripts. Assure that script args and options work.
mkdir(getenv('HOME') . '/.drush', 0777, TRUE);
mkdir($sandbox . '/etc/drush', 0777, TRUE);
mkdir($sandbox . '/share/drush/commands', 0777, TRUE);
-
- if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
- // Hack to make git use unix line endings on windows
- // We need it to make hashes of files pulled from git match ones hardcoded in tests
- if (!file_exists($sandbox . '\home')) {
- mkdir($sandbox . '\home');
- }
- exec("git config --file $sandbox\\home\\.gitconfig core.autocrlf false", $output, $return);
- }
}
/**
return (strtoupper(substr(PHP_OS, 0, 3)) == "WIN");
}
- public static function get_tar_executable() {
- return self::is_windows() ? "bsdtar.exe" : "tar";
- }
-
/**
* Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3).
* Also converts a cygwin "drive emulation" path (/cygdrive/c/dir1) into a
return $path;
}
- /*
- *Borrowed from Drush.
- * Checks operating system and returns
- * supported bit bucket folder.
- */
- function bit_bucket() {
- if (!$this->is_windows()) {
- return '/dev/null';
- }
- else {
- return 'nul';
- }
- }
-
public static function escapeshellarg($arg) {
// Short-circuit escaping for simple params (keep stuff readable)
if (preg_match('|^[a-zA-Z0-9.:/_-]*$|', $arg)) {
// Double up existing backslashes
$arg = preg_replace('/\\\/', '\\\\\\\\', $arg);
- // Double up double quotes
- $arg = preg_replace('/"/', '""', $arg);
+ // Escape double quotes.
+ $arg = preg_replace('/"/', '\\"', $arg);
- // Double up percents.
- // $arg = preg_replace('/%/', '%%', $arg);
+ // Escape single quotes.
+ $arg = preg_replace('/\'/', '\\\'', $arg);
// Add surrounding quotes.
$arg = '"' . $arg . '"';
* A directory to change into before executing.
* @param $expected_return
* The expected exit code. Usually self::EXIT_ERROR or self::EXIT_SUCCESS.
- * @param $suffix
- * Any code to append to the command. For example, redirection like 2>&1.
* @return integer
* An exit code.
*/
- function drush($command, array $args = array(), array $options = array(), $site_specification = NULL, $cd = NULL, $expected_return = self::EXIT_SUCCESS, $suffix = NULL) {
+ function drush($command, array $args = array(), array $options = array(), $site_specification = NULL, $cd = NULL, $expected_return = self::EXIT_SUCCESS) {
$global_option_list = array('simulate', 'root', 'uri', 'include', 'config', 'alias-path', 'ssh-options');
// insert "cd ... ; drush"
$cmd[] = $cd ? sprintf('cd %s &&', self::escapeshellarg($cd)) : NULL;
$cmd[] = "--$key=" . self::escapeshellarg($value);
}
}
- $cmd[] = $suffix;
$exec = array_filter($cmd, 'strlen'); // Remove NULLs
return $this->execute(implode(' ', $exec), $expected_return);
}
return $major;
}
+ // Copied from D7 - profiles/standard/standard.install
+ function create_node_types_php() {
+ $php = "
+ \$types = array(
+ array(
+ 'type' => 'page',
+ 'name' => 'Basic page',
+ 'base' => 'node_content',
+ 'description' => 'Use <em>basic pages</em> for your static content, such as an \'About us\' page.',
+ 'custom' => 1,
+ 'modified' => 1,
+ 'locked' => 0,
+ ),
+ array(
+ 'type' => 'article',
+ 'name' => 'Article',
+ 'base' => 'node_content',
+ 'description' => 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.',
+ 'custom' => 1,
+ 'modified' => 1,
+ 'locked' => 0,
+ ),
+ );
+
+ foreach (\$types as \$type) {
+ \$type = node_type_set_defaults(\$type);
+ node_type_save(\$type);
+ node_add_body_field(\$type);
+ }
+ ";
+ return $php;
+ }
+
/*
* Prepare the contents of an aliases file.
*/
}
define('UNISH_TMP', getenv('UNISH_TMP') ? getenv('UNISH_TMP') : (isset($GLOBALS['UNISH_TMP']) ? $GLOBALS['UNISH_TMP'] : sys_get_temp_dir()));
- define('UNISH_SANDBOX', UNISH_TMP . DIRECTORY_SEPARATOR . 'drush-sandbox');
-
- // Cache dir lives outside the sandbox so that we get persistence across classes.
- define('UNISH_CACHE', UNISH_TMP . DIRECTORY_SEPARATOR . 'drush-cache');
- putenv("CACHE_PREFIX=" . UNISH_CACHE);
- // Wipe at beginning of run.
- if (file_exists(UNISH_CACHE)) {
- // TODO: We no longer clean up cache dir between runs. Much faster, but we
- // we should watch for subtle problems. To manually clean up, delete the
- // UNISH_TMP/drush-cache directory.
- // unish_file_delete_recursive($cache);
- }
- else {
- $ret = mkdir(UNISH_CACHE, 0777, TRUE);
- }
+ define('UNISH_SANDBOX', UNISH_TMP . '/drush-sandbox');
$home = UNISH_SANDBOX . '/home';
putenv("HOME=$home");
putenv('ETC_PREFIX=' . UNISH_SANDBOX);
putenv('SHARE_PREFIX=' . UNISH_SANDBOX);
- define('UNISH_USERGROUP', isset($GLOBALS['UNISH_USERGROUP']) ? $GLOBALS['UNISH_USERGROUP'] : NULL);
+ // Cache dir lives outside the sandbox so that we get persistence across classes.
+ $cache = UNISH_TMP . '/drush-cache';
+ putenv("CACHE_PREFIX=" . $cache);
+ // Wipe at beginning of run.
+ if (file_exists($cache)) {
+ // TODO: We no longer clean up cache dir between runs. Much faster, but we
+ // we should watch for subtle problems. To manually clean up, delete the
+ // UNISH_TMP/drush-cache directory.
+ // unish_file_delete_recursive($cache);
+ }
define('UNISH_BACKEND_OUTPUT_DELIMITER', 'DRUSH_BACKEND_OUTPUT_START>>>%s<<<DRUSH_BACKEND_OUTPUT_END');
}
--- /dev/null
+<?php
+
+/*
+ * @file
+ * Tests for archive-dump
+ */
+
+class EntityTestCase extends Drush_CommandTestCase {
+ /*
+ * Test entity support for Node entities
+ *
+ * plan:
+ * - create an entity with entity-create
+ * - verify with entity-show
+ * - delete with entity-delete
+ * - verify with entity-show that it's no longer present
+ */
+
+ public function testEntityNode6() {
+ $sites = $this->setUpDrupal(1, TRUE, 6);
+ $uri = key($sites);
+ $alias = '@' . $uri;
+
+ $type = 'node';
+
+ // Create a temp file with the content we wish to create
+ $tmp_filename = tempnam(UNISH_TMP, 'entity_test');
+ $data = '{"title":"Foo","type":"page","body":{"und":[{"value":"","format":"filtered_html"}]}}';
+ file_put_contents($tmp_filename, $data);
+
+ // Create
+ $this->drush('entity-create', array($type, $tmp_filename), array('json' => 1), $alias);
+
+ // List all, to determin the newly created id
+ $this->drush('entity-read', array($type), array('format' => 'json'), $alias);
+ $output = json_decode($this->getOutput());
+ $id = end($output);
+ $this->assertEquals(array($id), $output, 'The new id shows up in a listing');
+
+ // Show as verification
+ $this->drush('entity-read', array($type, $id), array('format' => 'json'), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals($output->title, 'Foo', 'Title field is what we expected');
+
+ // Delete
+ $this->drush('entity-delete', array($type, $id), array(), $alias);
+
+ // List to verify that the entity has really been deleted
+ $this->drush('entity-read', array($type), array('format' => 'json'), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals(FALSE, in_array($id, $output), 'The deleted entity is nolonger present in a listing');
+ }
+
+ public function testEntityNode7() {
+ $sites = $this->setUpDrupal(1, TRUE, 7);
+ $uri = key($sites);
+ $alias = '@' . $uri;
+
+ $type = 'node';
+
+ // Create a temp file with the content we wish to create
+ $tmp_filename = tempnam(UNISH_TMP, 'entity_test');
+ $data = '{"title":"Foo","type":"page","body":{"und":[{"value":"","format":"filtered_html"}]}}';
+ file_put_contents($tmp_filename, $data);
+
+ // Create
+ $this->drush('entity-create', array($type, $tmp_filename), array('json' => 1), $alias);
+
+ // List all, to determin the newly created id
+ $this->drush('entity-read', array($type), array('format' => 'json'), $alias);
+ $output = json_decode($this->getOutput());
+ $id = end($output);
+ $this->assertEquals(array($id), $output, 'The new id shows up in a listing');
+
+ // Show as verification
+ $this->drush('entity-read', array($type, $id), array('format' => 'json'), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals('Foo', $output->title, 'Title field is what we expected');
+
+ // Delete
+ $this->drush('entity-delete', array($type, $id), array(), $alias);
+
+ // List to verify that the entity has really been deleted
+ $this->drush('entity-read', array($type), array('format' => 'json'), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals(FALSE, in_array($id, $output), 'The deleted entity is nolonger present in a listing');
+ }
+
+
+ public function testEntityFile7() {
+ $sites = $this->setUpDrupal(1, TRUE, 7);
+ $uri = key($sites);
+ $alias = '@' . $uri;
+
+ $type = 'file';
+
+ // Create a real file for which we will create a file entity.
+ file_put_contents($this->webroot() . '/sites/dev/files/foo.txt', "ABC");
+ $public_path = 'public://foo.txt';
+ //file_put_contents(drupal_realpath($public_path), 'ACB');
+
+ // Create a temp file with the content we wish to create
+ $tmp_filename = tempnam(UNISH_TMP, 'entity_test');
+ $data = '{"uid":"1","filename":"foo.txt","uri":"' . $public_path . '","filemime":"text/plain","filesize":"3","status":"1","timestamp":"1320416219","rdf_mapping":[]}';
+ file_put_contents($tmp_filename, $data);
+
+ // Create
+ $this->drush('entity-create', array($type, $tmp_filename), array('json' => 1), $alias);
+
+ // List all, to determin the newly created id
+ $this->drush('entity-read', array($type), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $id = end($output);
+ $this->assertEquals(array($id), $output, 'The new id shows up in a listing');
+
+ // Show as verification
+ $this->drush('entity-read', array($type, $id), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals('foo.txt', $output->filename, 'Filename field is what we expected');
+
+ // Delete
+ $this->drush('entity-delete', array($type, $id), array(), $alias);
+
+ // List to verify that the entity has really been deleted
+ $this->drush('entity-read', array($type), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals(FALSE, in_array($id, $output), 'The deleted entity is nolonger present in a listing');
+ }
+
+ public function testEntityUpdate7() {
+ $sites = $this->setUpDrupal(1, TRUE, 7);
+ $uri = key($sites);
+ $alias = '@' . $uri;
+
+ $type = 'node';
+
+ // Create a temp file with the content we wish to create
+ $tmp_filename = tempnam(UNISH_TMP, 'entity_test');
+ $data = '{"title":"Foo","type":"page","body":{"und":[{"value":"","format":"filtered_html"}]}}';
+ file_put_contents($tmp_filename, $data);
+
+ // Create
+ $this->drush('entity-create', array($type, $tmp_filename), array('json' => 1), $alias);
+
+ // List all, to determin the newly created id
+ $this->drush('entity-read', array($type), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $id = end($output);
+ $this->assertEquals(array($id), $output, 'The new id shows up in a listing');
+
+ // Show as verification
+ $this->drush('entity-read', array($type, $id), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals('Foo', $output->title, 'Title field is what we expected');
+
+ $output->title = 'Bar';
+ file_put_contents($tmp_filename, json_encode($output));
+
+ // Do an update
+ $this->drush('entity-update', array($type, $id), array('json' => 1, 'json-input' => $tmp_filename), $alias);
+
+ // Show as verification
+ $this->drush('entity-read', array($type, $id), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals('Bar', $output->title, 'Title field has changed as we expected');
+
+ $data = '{"title":"Qux"}';
+ file_put_contents($tmp_filename, $data);
+
+ // Do an update
+ $this->drush('entity-update', array($type, $id), array('json' => 1, 'json-input' => $tmp_filename, 'fields' => 'title'), $alias);
+
+ // Show as verification
+ $this->drush('entity-read', array($type, $id), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals('Qux', $output->title, 'Title field has changed as we expected');
+
+ }
+
+
+ /*
+ * Test entity support for User entities
+ *
+ * plan:
+ * - create a user with entity-create
+ * - verify with entity-show
+ * - delete with entity-delete
+ * - verify with entity-show that it's no longer present
+ */
+
+ public function testEntityUser6() {
+ $sites = $this->setUpDrupal(1, TRUE, 6);
+ $uri = key($sites);
+ $alias = '@' . $uri;
+
+ $type = 'user';
+
+ // Create a temp file with the content we wish to create
+ $tmp_filename = tempnam(UNISH_TMP, 'entity_test');
+ $data = '{"name":"Mr. Foo","mail":"admin@example.com","status":"1"}';
+ file_put_contents($tmp_filename, $data);
+
+ // Create
+ $this->drush('entity-create', array($type, $tmp_filename), array('json' => 1), $alias);
+
+ // List all, to determin the newly created id
+ $this->drush('entity-read', array($type), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $id = end($output);
+ $this->assertEquals(array(0, 1, $id), $output, "New user $id created");
+
+ // Show as verification
+ $this->drush('entity-read', array($type, $id), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals($output->name, 'Mr. Foo', 'name field is what we expected');
+
+ // Delete
+ $this->drush('entity-delete', array($type, $id), array(), $alias);
+
+ // List to verify that the entity has really been deleted
+ $this->drush('entity-read', array($type), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals(FALSE, in_array($id, $output), 'The deleted entity is nolonger present in a listing');
+ }
+
+ public function testEntityUser7() {
+ $sites = $this->setUpDrupal(1, TRUE, 7);
+ $uri = key($sites);
+ $alias = '@' . $uri;
+
+ $type = 'user';
+
+ // Create a temp file with the content we wish to create
+ $tmp_filename = tempnam(UNISH_TMP, 'entity_test');
+ $data = '{"name":"Mr. Foo","mail":"admin@example.com","status":"1"}';
+ file_put_contents($tmp_filename, $data);
+
+ // Create
+ $this->drush('entity-create', array($type, $tmp_filename), array('json' => 1), $alias);
+
+ // List all, to determin the newly created id
+ $this->drush('entity-read', array($type), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $id = end($output);
+ $this->assertEquals(array(0,1,$id), $output);
+
+ // Show as verification
+ $this->drush('entity-read', array($type, $id), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals('Mr. Foo', $output->name, 'name field is what we expected');
+
+ // Delete
+ $this->drush('entity-delete', array($type, $id), array(), $alias);
+
+ // List to verify that the entity has really been deleted
+ $this->drush('entity-read', array($type), array('json' => 1), $alias);
+ $output = json_decode($this->getOutput());
+ $this->assertEquals(FALSE, in_array($id, $output), 'The deleted entity is nolonger present in a listing');
+ }
+
+ public function testEntityTypeRead6() {
+ $sites = $this->setUpDrupal(1, TRUE, 6);
+ $uri = key($sites);
+ $alias = '@' . $uri;
+
+ $type = 'node';
+ $this->drush('entity-type-read', array(), array('json' => 1), $alias);
+ $this->assertEquals('["node","user","taxonomy_vocabulary","taxonomy_term"]', $this->getOutput(), 'list of entity types in json');
+
+ $this->drush('entity-type-read', array($type), array('json' => 1), $alias);
+ $expected = '{"node":{"entity keys":{"id":"nid","revision":"vid","label":"title"},"drush":{"defaults":{"type":"","title":""},"new":["nid","vid"]},"load list sql":"select nid id from {node}"}}';
+ $this->assertEquals($expected, $this->getOutput(), 'details of the entity type node in json');
+
+ }
+
+ public function testEntityTypeRead7() {
+ $sites = $this->setUpDrupal(1, TRUE, 7);
+ $uri = key($sites);
+ $alias = '@' . $uri;
+
+ $type = 'node';
+ $this->drush('entity-type-read', array(), array('json' => 1), $alias);
+ $this->assertEquals('["node","file","user"]', $this->getOutput(), 'list of entity types in json');
+
+ $this->drush('entity-type-read', array($type), array('json' => 1), $alias);
+ $expected = '{"node":{"label":"Node","controller class":"NodeController","base table":"node","revision table":"node_revision","uri callback":"node_uri","fieldable":true,"entity keys":{"id":"nid","revision":"vid","bundle":"type","label":"title"},"bundle keys":{"bundle":"type"},"bundles":[],"view modes":{"full":{"label":"Full content","custom settings":false},"teaser":{"label":"Teaser","custom settings":true},"rss":{"label":"RSS","custom settings":false}},"static cache":true,"field cache":true,"load hook":"node_load","translation":[],"schema_fields_sql":{"base table":["nid","vid","type","language","title","uid","status","created","changed","comment","promote","sticky","tnid","translate"],"revision table":["nid","vid","uid","title","log","timestamp","status","comment","promote","sticky"]},"drush":{"defaults":{"type":"","title":"","language":"und","body":{"und":[{"value":"","format":"plain_text"}]}},"new":["nid","vid"]}}}';
+ $this->assertEquals($expected, $this->getOutput(), 'details of the entity type node in json');
+
+ }
+}
+++ /dev/null
-<?php
-
-/**
- * Filesystem related testing.
- */
-class FilesystemCase extends Drush_CommandTestCase {
-
- public function testSbit() {
- if ($this->is_windows()) {
- $this->markTestSkipped("s-bit test doesn't apply on Windows.");
- }
- if (is_null(UNISH_USERGROUP)) {
- $this->markTestSkipped("s-bit test skipped because of UNISH_USERGROUP was not set.");
- }
-
- $dest = UNISH_SANDBOX . '/test-filesystem-sbit';
- mkdir($dest);
- chgrp($dest, UNISH_USERGROUP);
- chmod($dest, 02755); // rwxr-sr-x
-
- $this->drush('pm-download', array('devel'), array('cache' => NULL, 'skip' => NULL, 'destination' => $dest));
-
- $group = posix_getgrgid(filegroup($dest . '/devel/README.txt'));
- $this->assertEquals($group['name'], UNISH_USERGROUP, 'Group is preserved.');
-
- $perms = fileperms($dest . '/devel') & 02000;
- $this->assertEquals($perms, 02000, 's-bit is preserved.');
- }
-
- public function testExecuteBits() {
- if ($this->is_windows()) {
- $this->markTestSkipped("execute bit test doesn't apply on Windows.");
- }
-
- $dest = UNISH_SANDBOX . '/test-filesystem-execute';
- mkdir($dest);
- $this->drush('pm-download', array('drush'), array('cache' => NULL, 'skip' => NULL, 'destination' => $dest));
-
- $perms = fileperms($dest . '/drush/drush') & 0111;
- $this->assertEquals($perms, 0111, 'Execute permission is preserved.');
- }
-}
-
*/
class makeMakefileCase extends Drush_CommandTestCase {
/**
- * Path to test make files.
- */
- protected $makefile_path;
-
- /**
- * Initialize $makefile_path.
- */
- function __construct() {
- $this->makefile_path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'makefiles';
- }
-
- /**
* Run a given makefile test.
*
* @param $test
'test' => NULL,
'md5' => 'print',
);
+ $makefile_path = dirname(__FILE__) . '/makefiles';
$config = $this->getMakefile($test);
$options = array_merge($config['options'], $default_options);
- $makefile = $this->makefile_path . DIRECTORY_SEPARATOR . $config['makefile'];
+ $makefile = $makefile_path . '/' . $config['makefile'];
$return = !empty($config['fail']) ? self::EXIT_ERROR : self::EXIT_SUCCESS;
$this->drush('make', array($makefile), $options, NULL, NULL, $return);
// Check the log for the build hash if this test should pass.
if (empty($config['fail'])) {
- $output = $this->getOutput();
- $this->assertContains($config['md5'], $output, $config['name'] . ' - build md5 matches expected value: ' . $config['md5']);
+ $output = $this->getOutputAsList();
+ $this->assertTrue(in_array($config['md5'], $output), $config['name'] . ' - build md5 matches expected value: ' . $config['md5']);
}
}
}
}
- /**
- * Translations can change arbitrarily, so these test for the existence of .po
- * files, rather than trying to match a build hash.
- */
function testMakeTranslations() {
- $config = $this->getMakefile('translations');
-
- $makefile = $this->makefile_path . DIRECTORY_SEPARATOR . $config['makefile'];
- $install_directory = UNISH_SANDBOX . '/translations';
- $this->drush('make', array($makefile, $install_directory), $config['options']);
-
- $po_files = array(
- 'sites/all/modules/token/translations/pt-br.po',
- 'sites/all/modules/token/translations/es.po',
- );
-
- foreach ($po_files as $po_file) {
- $this->assertFileExists($install_directory . '/' . $po_file);
- }
+ $this->runMakefileTest('translations');
}
- /**
- * Translations can change arbitrarily, so these test for the existence of .po
- * files, rather than trying to match a build hash.
- */
function testMakeTranslationsInside() {
- $config = $this->getMakefile('translations-inside');
-
- $makefile = $this->makefile_path . '/' . $config['makefile'];
- $install_directory = UNISH_SANDBOX . '/translations-inside';
- $this->drush('make', array($makefile, $install_directory));
-
- $po_files = array(
- 'profiles/default/translations/pt-br.po',
- 'profiles/default/translations/es.po',
- 'sites/all/modules/token/translations/pt-br.po',
- 'sites/all/modules/token/translations/es.po',
- 'modules/system/translations/pt-br.po',
- 'modules/system/translations/es.po',
- );
-
- foreach ($po_files as $po_file) {
- $this->assertFileExists($install_directory . '/' . $po_file);
- }
- }
-
- /**
- * Translations can change arbitrarily, so these test for the existence of .po
- * files, rather than trying to match a build hash.
- */
- function testMakeTranslationsInside7() {
- $config = $this->getMakefile('translations-inside7');
-
- $makefile = $this->makefile_path . DIRECTORY_SEPARATOR . $config['makefile'];
- $install_directory = UNISH_SANDBOX . '/translations-inside7';
- $this->drush('make', array($makefile, $install_directory));
-
- $po_files = array(
- 'profiles/minimal/translations/pt-br.po',
- 'profiles/minimal/translations/es.po',
- 'profiles/testing/translations/pt-br.po',
- 'profiles/testing/translations/es.po',
- 'profiles/standard/translations/pt-br.po',
- 'profiles/standard/translations/es.po',
- 'sites/all/modules/token/translations/pt-br.po',
- 'sites/all/modules/token/translations/es.po',
- 'modules/system/translations/pt-br.po',
- 'modules/system/translations/es.po',
- );
-
- foreach ($po_files as $po_file) {
- $this->assertFileExists($install_directory . '/' . $po_file);
- }
+ $this->runMakefileTest('translations-inside');
}
function testMakeContribDestination() {
$this->runMakefileTest('file');
}
- function testMakeSubtree() {
- $this->runMakefileTest('subtree');
- }
-
function testMakeMd5Succeed() {
$this->runMakefileTest('md5-succeed');
}
$this->runMakefileTest('ignore-checksums');
}
- /**
- * Test .info file writing and the use of a git reference cache for
- * git downloads.
- */
- function testInfoFileWritingGit() {
- // Use the git-simple.make file.
- $config = $this->getMakefile('git-simple');
-
- $options = array('no-core' => NULL);
- $makefile = $this->makefile_path . DIRECTORY_SEPARATOR . $config['makefile'];
- $this->drush('make', array($makefile, UNISH_SANDBOX . '/test-build'), $options);
-
- // Test cck_signup.info file.
- $this->assertFileExists(UNISH_SANDBOX . '/test-build/sites/all/modules/cck_signup/cck_signup.info');
- $contents = file_get_contents(UNISH_SANDBOX . '/test-build/sites/all/modules/cck_signup/cck_signup.info');
- $this->assertContains('; Information added by drush on ' . date('Y-m-d'), $contents);
- $this->assertContains('version = "2fe932c"', $contents);
- $this->assertContains('project = "cck_signup"', $contents);
-
- // Verify that a reference cache was created.
- $cache_dir = UNISH_CACHE . DIRECTORY_SEPARATOR . 'cache';
- $this->assertFileExists($cache_dir . '/git/cck_signup-' . md5('http://git.drupal.org/project/cck_signup.git'));
-
- // Test context_admin.info file.
- $this->assertFileExists(UNISH_SANDBOX . '/test-build/sites/all/modules/context_admin/context_admin.info');
- $contents = file_get_contents(UNISH_SANDBOX . '/test-build/sites/all/modules/context_admin/context_admin.info');
- $this->assertContains('; Information added by drush on ' . date('Y-m-d'), $contents);
- $this->assertContains('version = "eb9f05e"', $contents);
- $this->assertContains('project = "context_admin"', $contents);
-
- // Verify git reference cache exists.
- $this->assertFileExists($cache_dir . '/git/context_admin-' . md5('http://git.drupal.org/project/context_admin.git'));
- }
-
- function testMakeFileExtract() {
- $this->runMakefileTest('file-extract');
- }
-
- function testMakeLimitProjects() {
- $this->runMakefileTest('limit-projects');
- $this->runMakefileTest('limit-projects-multiple');
- }
-
- function testMakeLimitLibraries() {
- $this->runMakefileTest('limit-libraries');
- $this->runMakefileTest('limit-libraries-multiple');
- }
-
- /**
- * Test that make_move_build() doesn't wipe out directories that it shouldn't.
- */
- function testMakeMoveBuild() {
- // Manually download a module.
- $this->drush('pm-download', array('cck_signup'), array('destination' => UNISH_SANDBOX . '/sites/all/modules/contrib', 'yes' => NULL));
-
- // Build a make file.
- $config = $this->getMakefile('contrib-destination');
- $makefile = $this->makefile_path . DIRECTORY_SEPARATOR . $config['makefile'];
- $this->drush('make', array($makefile, '.'), $config['options']);
-
- // Verify that the manually downloaded module still exists.
- $this->assertFileExists(UNISH_SANDBOX . '/sites/all/modules/contrib/cck_signup/README.txt');
- }
-
function getMakefile($key) {
static $tests = array(
'get' => array(
'md5' => '4bf18507da89bed601548210c22a3bed',
'options' => array('no-core' => NULL),
),
+ 'post' => array(
+ 'name' => 'Test POST retrieval of projects',
+ 'makefile' => 'post.make',
+ 'build' => TRUE,
+ 'md5' => '6a50624cbd65cc69011ae6c089ce298a',
+ 'options' => array('no-core' => NULL),
+ ),
'git' => array(
'name' => 'GIT integration',
'makefile' => 'git.make',
'build' => TRUE,
- 'md5' => '4c80d78b50c89b5ba11a997bafec2b43',
- 'options' => array('no-core' => NULL, 'no-gitinfofile' => NULL),
+ 'md5' => '59a8e8f898e4f4d81349d3997498fee9',
+ 'options' => array('no-core' => NULL),
),
'git-simple' => array(
'name' => 'Simple git integration',
'makefile' => 'git-simple.make',
'build' => TRUE,
'md5' => '6754a6814d4213326513ea750e6d5b65',
- 'options' => array('no-core' => NULL, 'no-gitinfofile' => NULL),
+ 'options' => array('no-core' => NULL),
),
'no-patch-txt' => array(
'name' => 'Test --no-patch-txt option',
'name' => 'Recursion',
'makefile' => 'recursion.make',
'build' => TRUE,
- 'md5' => 'cd095bd6dadb2f0d3e81f85f13685372',
- 'options' => array(
- 'no-core' => NULL,
- 'contrib-destination' => 'profiles/drupal_forum',
- ),
+ 'md5' => 'a0357e99e2506fbf8629ae89d2f44096',
+ 'options' => array('no-core' => NULL),
),
'svn' => array(
'name' => 'SVN',
'translations' => array(
'name' => 'Translation downloads',
'makefile' => 'translations.make',
+ 'build' => TRUE,
+ 'md5' => '9b209494006aecd7f68c228a61bb26f9',
'options' => array(
'translations' => 'es,pt-br',
'no-core' => NULL,
'translations-inside' => array(
'name' => 'Translation downloads inside makefile',
'makefile' => 'translations-inside.make',
- ),
- 'translations-inside7' => array(
- 'name' => 'Translation downloads inside makefile, core 7.x',
- 'makefile' => 'translations-inside7.make',
+ 'build' => TRUE,
+ 'md5' => '0566b12158e6fba7070b80714ea4019d',
+ 'options' => array(),
),
'contrib-destination' => array(
'name' => 'Contrib-destination attribute',
'name' => 'File extraction',
'makefile' => 'file.make',
'build' => TRUE,
- 'md5' => 'c7cab3930f644961a576d78769498172',
- 'options' => array('no-core' => NULL),
- ),
- 'subtree' => array(
- 'name' => 'Use subtree from downloaded archive',
- 'makefile' => 'subtree.make',
- 'build' => TRUE,
- 'md5' => 'db3770d8b4c9ce77510cbbcc566da9b8',
+ 'md5' => 'f76ec174a775ce67f8e9edcb02336ef2',
'options' => array('no-core' => NULL),
),
'md5-succeed' => array(
'md5' => 'f76ec174a775ce67f8e9edcb02336ef2',
'options' => array('no-core' => NULL, 'ignore-checksums' => NULL),
),
- 'file-extract' => array(
- 'name' => 'Extract archives',
- 'makefile' => 'file-extract.make',
- 'build' => TRUE,
- 'md5' => 'f92471fb7979e45d2554c61314ac6236',
- // @todo This test often fails with concurrency set to more than one.
- 'options' => array('no-core' => NULL, 'concurrency' => 1),
- ),
- 'limit-projects' => array(
- 'name' => 'Limit projects downloaded',
- 'makefile' => 'limited-projects-libraries.make',
- 'build' => TRUE,
- 'md5' => '3149650120e541d7d0fa577eef0ee9a3',
- 'options' => array('no-core' => NULL, 'projects' => 'boxes'),
- ),
- 'limit-projects-multiple' => array(
- 'name' => 'Limit multiple projects downloaded',
- 'makefile' => 'limited-projects-libraries.make',
- 'build' => TRUE,
- 'md5' => 'ef8996c4d6c6f0d229e2237c73860071',
- 'options' => array('no-core' => NULL, 'projects' => 'boxes,admin_menu'),
- ),
- 'limit-libraries' => array(
- 'name' => 'Limit libraries downloaded',
- 'makefile' => 'limited-projects-libraries.make',
- 'build' => TRUE,
- 'md5' => 'cb0da4465d86eb34cafb167787862eb6',
- 'options' => array('no-core' => NULL, 'libraries' => 'drush_make'),
- ),
- 'limit-libraries-multiple' => array(
- 'name' => 'Limit multiple libraries downloaded',
- 'makefile' => 'limited-projects-libraries.make',
- 'build' => TRUE,
- 'md5' => '7c10e6fc65728a77a2b0aed4ec2a29cd',
- 'options' => array('no-core' => NULL, 'libraries' => 'drush_make,token'),
- ),
);
return $tests[$key];
}
+++ /dev/null
-core = 6.x
-api = 2
-
-projects[admin_menu][download][type] = "file"
-projects[admin_menu][download][url] = "http://ftp.drupal.org/files/projects/admin_menu-6.x-1.0-beta.zip?param=value"
-projects[admin_menu][download][md5] = "673ec1bb431113142016d5eb0a0ef77f"
-
-projects[devel][download][type] = "file"
-projects[devel][download][url] = "http://ftp.drupal.org/files/projects/devel-6.x-1.0.tar.gz?param=value"
-projects[devel][download][md5] = "3dbd3cd4c15f001c1ac7067ca58c74ff"
-
-libraries[vegas][download][type] = "get"
-libraries[vegas][download][url] = "https://github.com/jaysalvat/vegas/zipball/v1.2"
-
-libraries[autopager][download][type] = "get"
-libraries[autopager][download][url] = "http://lagoscript.org/files/jquery/autopager/jquery.autopager-1.0.0.js"
-libraries[autopager][directory_name] = "autopager"
libraries[drush_make][download][type] = "file"
libraries[drush_make][download][url] = "http://drupalcode.org/project/drush_make.git/blob_plain/8d4e770:/README.txt"
libraries[drush_make][download][filename] = "README.txt"
-
-; Single file download
-libraries[jquery.cycle][download][type] = "get"
-libraries[jquery.cycle][download][url] = "https://github.com/downloads/malsup/cycle/jquery.cycle.all.2.88.min.js"
-libraries[jquery.cycle][download][filename] = "jquery.cycle.js"
core = 6.x
api = 2
-; Tarball file download
libraries[drush_make][download][type] = file
libraries[drush_make][download][url] = http://ftp.drupal.org/files/projects/drush_make-6.x-2.0-beta8.tar.gz
libraries[drush_make][directory_name] = drush_make
libraries[drush_make][destination] = libraries
-libraries[drush_make][lock] = Locked
+libraries[drush_make][lock] = "Locked"
; Test a non-Drupal.org repository.
projects[os_lightbox][type] = "module"
projects[os_lightbox][download][type] = "git"
-projects[os_lightbox][download][url] = "https://github.com/opensourcery/os_lightbox.git"
+projects[os_lightbox][download][url] = "git@github.com:opensourcery/os_lightbox.git"
projects[os_lightbox][download][revision] = "8d60090f2"
-
-; Test a refspec fetch.
-projects[storypal][type] = module
-projects[storypal][download][type] = git
-projects[storypal][download][url] = http://git.drupal.org/project/storypal.git
-projects[storypal][download][refspec] = refs/tags/7.x-1.0
+++ /dev/null
-core = "7.x"
-api = 2
-
-projects[boxes][version] = "1.0-beta7"
-
-projects[admin_menu][version] = "3.0-rc1"
-
-; Use drupal.org as a nice stable source of libraries.
-libraries[drush_make][download][type] = "file"
-libraries[drush_make][download][url] = "http://ftp.drupal.org/files/projects/drush_make-6.x-2.3.tar.gz"
-
-; Use drupal.org as a nice stable source of libraries.
-libraries[token][download][type] = "file"
-libraries[token][download][url] = "http://ftp.drupal.org/files/projects/token-7.x-1.0-rc1.tar.gz"
-
api = 2
projects[drupal_forum][type] = "profile"
-projects[drupal_forum][version] = "1.0-alpha2"
-
-projects[views] = "3.0-rc3"
+projects[drupal_forum][version] = "1.0-alpha2"
\ No newline at end of file
+++ /dev/null
-core = 6.x
-api = 2
-
-libraries[nivo-slider][download][type] = get
-libraries[nivo-slider][download][url] = https://github.com/downloads/gilbitron/Nivo-Slider/nivo-slider2.7.1.zip
-libraries[nivo-slider][download][sha1] = bd8e14b82f5b9c6f533a4e1aa26a790cd66c3cb9
-libraries[nivo-slider][download][subtree] = nivo-slider
-
-libraries[fullcalendar][download][type] = get
-libraries[fullcalendar][download][url] = http://arshaw.com/fullcalendar/downloads/fullcalendar-1.5.3.zip
-libraries[fullcalendar][download][sha1] = c7219b1ddd2b11ccdbf83ebd116872affbc45d7a
-libraries[fullcalendar][download][subtree] = fullcalendar
+++ /dev/null
-core = 7.x
-api = 2
-
-translations[] = es
-translations[] = pt-br
-
-projects[drupal][version] = "7.10"
-projects[token][version] = "1.0-beta7"
-
<!--Uncomment the line below if your path to drush differs from `which drush`. Use absolute path.-->
<!--<var name="UNISH_DRUSH" value="/Users/mw/bin/drush"/>-->
- <!--Uncomment to provide a group the user running the tests belong to. This is needed for some filesystem tests. -->
- <!--<var name="UNISH_USERGROUP" value="staff"/>-->
-
<includePath>.</includePath>
</php>
</phpunit>
+++ /dev/null
-<?php
-
-/*
- * @file
- * Tests for queue commands.
- */
-class QueueCase extends Drush_CommandTestCase {
-
- function testQueue() {
- $sites = $this->setUpDrupal(1, TRUE);
- $options = array(
- 'yes' => NULL,
- 'root' => $this->webroot(),
- 'uri' => key($sites),
- );
-
- // Enable aggregator since it declares a queue.
- $this->drush('en', array('aggregator'), $options);
-
- $this->drush('queue-list', array(), $options);
- $output = $this->getOutput();
- $this->assertContains('aggregator_feeds', $output, 'Queue list shows the declared queue.');
-
- $this->drush('php-script', array('queue_script'), $options + array('script-path' => dirname(__FILE__) . '/resources'));
- $this->drush('queue-list', array(), $options + array('pipe' => TRUE));
- $output = trim($this->getOutput());
- $parts = explode(",", $output);
- $this->assertEquals('aggregator_feeds,1,SystemQueue', $output, 'Item was successfully added to the queue.');
- $output = $this->getOutput();
-
- $this->drush('queue-run', array('aggregator_feeds'), $options);
- $this->drush('queue-list', array(), $options + array('pipe' => TRUE));
- $output = trim($this->getOutput());
- $parts = explode(",", $output);
- $this->assertEquals('aggregator_feeds,0,SystemQueue', $output, 'Queue item processed.');
- }
-}
// Use a local, static XML file because live files change over time.
$xml = simplexml_load_file(dirname(__FILE__). '/devel.xml');
- // Pick specific release.
$request_data = array(
'name' => 'devel',
'drupal_version' => '6.x',
'project_version' => '1.18',
'version' => '6.x-1.18',
);
+
+ // Pick specific release.
$release = updatexml_parse_release($request_data, $xml);
$this->assertEquals($release['version'], '6.x-1.18');
);
$release = updatexml_parse_release($request_data, $xml);
$this->assertEquals($release['version'], '6.x-2.1');
-
- // Pick latest from a specific branch.
- $request_data = array(
- 'name' => 'devel',
- 'drupal_version' => '6.x',
- 'version' => '6.x-1',
- );
- $release = updatexml_parse_release($request_data, $xml);
- $this->assertEquals($release['version'], '6.x-1.23');
-
- // Pick latest from a different branch.
- $request_data = array(
- 'name' => 'devel',
- 'drupal_version' => '6.x',
- 'version' => '6.x-2',
- );
- $release = updatexml_parse_release($request_data, $xml);
- // 6.x-2.2 is skipped because it is unpublished.
- // 6.x-2.2-rc1 is skipped because it is not a stable release.
- $this->assertEquals($release['version'], '6.x-2.1');
-
- // Pick a -dev release.
- $request_data = array(
- 'name' => 'devel',
- 'drupal_version' => '6.x',
- 'version' => '6.x-1.x',
- );
- $release = updatexml_parse_release($request_data, $xml);
- $this->assertEquals($release['version'], '6.x-1.x-dev');
-
- // Test $restrict_to parameter.
- $request_data['version'] = '6.x-1';
- $release = updatexml_parse_release($request_data, $xml, 'version');
- $this->assertEquals($release['version'], '6.x-1.23');
- $release = updatexml_parse_release($request_data, $xml, 'dev');
- $this->assertEquals($release['version'], '6.x-1.x-dev');
}
}
+++ /dev/null
-<?php
-
-$types = array(
- array(
- 'type' => 'page',
- 'name' => 'Basic page',
- 'base' => 'node_content',
- 'description' => 'Use <em>basic pages</em> for your static content, such as an \'About us\' page.',
- 'custom' => 1,
- 'modified' => 1,
- 'locked' => 0,
- ),
- array(
- 'type' => 'article',
- 'name' => 'Article',
- 'base' => 'node_content',
- 'description' => 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.',
- 'custom' => 1,
- 'modified' => 1,
- 'locked' => 0,
- ),
-);
-
-foreach ($types as $type) {
- $type = node_type_set_defaults($type);
- node_type_save($type);
- node_add_body_field($type);
-}
+++ /dev/null
-<?php
-$feed = array(
- 'title' => 'test',
- 'url' => 'http://drupal.org/project/issues/rss/drupal?categories=All',
- 'refresh' => 3600,
- 'block' => 5,
-);
-
-// Create a new feed.
-aggregator_save_feed($feed);
-// Let cron call DrupalQueue::createItem() for us.
-aggregator_cron();
'pull' => '!git pull',
'echosimple' => '!echo {{@target}}',
'echotest' => '!echo {{@target}} {{%root}} {{%mypath}}',
- 'compound-command' => '!cd {{%sandbox}} && echo second',
+ 'compound-command' => '!cd {{%sandbox}} && pwd && touch mytest && ls mytest',
);
";
file_put_contents(UNISH_SANDBOX . '/drushrc.php', trim($contents));
'simulate' => NULL,
'rebase' => NULL,
);
- $this->drush('pull', array('origin'), $options, NULL, NULL, self::EXIT_SUCCESS, '2>&1');
+ $this->drush('pull', array('origin'), $options);
$output = $this->getOutput();
$this->assertContains('Calling proc_open(git pull origin --rebase);', $output);
}
$this->drush('glopts', array(), $options, 'user@server/path/to/drupal#sitename');
// $expected might be different on non unix platforms. We shall see.
// n.b. --config is not included in calls to remote systems.
- $bash = $this->escapeshellarg('drush --invoke --simulate --nocolor --uri=sitename --root=/path/to/drupal topic core-global-options 2>&1');
- $expected = "Simulating backend invoke: ssh user@server $bash 2>&1";
+ $expected = "Simulating backend invoke: ssh user@server 'drush --simulate --nocolor --uri=sitename --root=/path/to/drupal topic core-global-options --invoke 2>&1' 2>&1";
$output = $this->getOutput();
$this->assertEquals($expected, $output, 'Expected remote shell alias to a drush command was built');
}
'ssh-options' => '',
'rebase' => NULL,
);
- $this->drush('pull', array('origin'), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1');
+ $this->drush('pull', array('origin'), $options, 'user@server/path/to/drupal#sitename');
// $expected might be different on non unix platforms. We shall see.
$expected = "Calling proc_open(ssh user@server 'cd /path/to/drupal && git pull origin --rebase');";
$output = $this->getOutput();
$this->assertEquals($expected, $output, 'Expected remote shell alias to a bash command was built');
}
-
+
/*
* Test shell aliases with simple replacements -- no alias.
*/
'config' => UNISH_SANDBOX,
);
$this->drush('echosimple', array(), $options);
- // Windows command shell actually prints quotes. See http://drupal.org/node/1452944.
- $expected = $this->is_windows() ? '"@none"' : '@none';
+ $expected = "@none";
$output = $this->getOutput();
- $this->assertEquals($expected, $output);
+ $this->assertEquals($expected, $output, 'Expected echo test returned "@none"');
}
-
+
/*
* Test shell aliases with complex replacements -- no alias.
*/
// echo test has replacements that are not satisfied, so this is expected to return an error.
$this->drush('echotest', array(), $options, NULL, NULL, self::EXIT_ERROR);
}
-
+
/*
* Test shell aliases with replacements -- alias.
*/
'alias-path' => UNISH_SANDBOX,
);
$this->drush('echotest', array(), $options, '@myalias');
- // Windows command shell actually prints quotes. See http://drupal.org/node/1452944.
- $expected = $this->is_windows() ? '"@myalias"' : '@myalias';
- $expected .= ' /path/to/drupal /srv/data/mypath';
+ $expected = "@myalias /path/to/drupal /srv/data/mypath";
$output = $this->getOutput();
- $this->assertEquals($expected, $output);
+ $this->assertEquals($expected, $output, 'Expected echo test returned "' . $expected . '"');
}
-
+
/*
* Test shell aliases with replacements and compound commands.
*/
'alias-path' => UNISH_SANDBOX,
);
$this->drush('compound-command', array(), $options, '@myalias');
- $expected = 'second';
+ $expected = UNISH_SANDBOX . "\nmytest";
$output = $this->getOutput();
- $this->assertEquals($expected, $output);
+ $this->assertEquals($expected, $output, 'Expected compound test returned "' . $expected . '"');
}
-
+
/*
- * Test shell aliases with multiple config files.
+ * Test shell aliases with replacements and compound commands.
*/
public function testShellAliasMultipleConfigFiles() {
$options = array(
'config' => UNISH_SANDBOX . "/b" . PATH_SEPARATOR . UNISH_SANDBOX,
'alias-path' => UNISH_SANDBOX,
);
+ $this->drush('compound-command', array(), $options, '@myalias');
+ $expected = UNISH_SANDBOX . "\nmytest";
+ $output = $this->getOutput();
+ $this->assertEquals($expected, $output, 'Expected compound test returned "' . $expected . '"');
$this->drush('also', array(), $options);
$expected = "alternate config file included too";
$output = $this->getOutput();
- $this->assertEquals($expected, $output);
+ $this->assertEquals($expected, $output, 'Expected also test returned "' . $expected . '"');
}
}
+
$options = array(
'simulate' => NULL,
);
- $this->drush('ssh', array(), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1');
+ $this->drush('ssh', array(), $options, 'user@server/path/to/drupal#sitename');
$output = $this->getOutput();
$expected = sprintf('Calling proc_open(ssh -o PasswordAuthentication=no %s@%s);', self::escapeshellarg('user'), self::escapeshellarg('server'));
$this->assertEquals($expected, $output);
$options = array(
'simulate' => NULL,
);
- $this->drush('ssh', array('date'), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1');
+ $this->drush('ssh', array('date'), $options, 'user@server/path/to/drupal#sitename');
$output = $this->getOutput();
$expected = sprintf('Calling proc_open(ssh -o PasswordAuthentication=no %s@%s %s);', self::escapeshellarg('user'), self::escapeshellarg('server'), self::escapeshellarg('date'));
$this->assertEquals($expected, $output);
$options = array(
'simulate' => NULL,
);
- $this->drush('ssh', array('ls', '/path1', '/path2'), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1');
+ $this->drush('ssh', array('ls', '/path1', '/path2'), $options, 'user@server/path/to/drupal#sitename');
$output = $this->getOutput();
$expected = sprintf('Calling proc_open(ssh -o PasswordAuthentication=no %s@%s \'ls /path1 /path2\');', self::escapeshellarg('user'), self::escapeshellarg('server'));
$this->assertEquals($expected, $output);
$options = array(
'simulate' => NULL,
);
- $this->drush('ssh', array('ls /path1 /path2'), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1');
+ $this->drush('ssh', array('ls /path1 /path2'), $options, 'user@server/path/to/drupal#sitename');
$output = $this->getOutput();
$expected = sprintf('Calling proc_open(ssh -o PasswordAuthentication=no %s@%s \'ls /path1 /path2\');', self::escapeshellarg('user'), self::escapeshellarg('server'));
$this->assertEquals($expected, $output);
+++ /dev/null
-<?php
-
-/*
- * @file
- * Tests sql-connect command
- *
- * Installs Drupal and checks that the given URL by sql-connect is correct.
- * @TODO: test Postgre-SQL and Sqlite.
- */
-class SqlConnectCase extends Drush_CommandTestCase {
-
- function testSqlConnect() {
- $sites = $this->setUpDrupal(1, TRUE);
- $options = array(
- 'yes' => NULL,
- 'root' => $this->webroot(),
- 'uri' => key($sites),
- );
-
- // Get the connection details with sql-connect and check its structure.
- $this->drush('sql-connect', array(), $options);
- $output = $this->getOutput();
- $this->assertRegExp('/^mysql --database=[^\s]+ --host=[^\s]+ --user=[^\s]+ --password=.*$/', $output);
-
- // Issue a query and check the result to verify the connection.
- $this->execute($output . ' -e "select name from users where uid = 1;"');
- $output = $this->getOutput();
- $this->assertContains('admin', $output);
- }
-}
$this->markTestSkipped('SQL Sync does not apply to SQLite.');
return;
}
- chdir(UNISH_TMP); // Avoids perm denied Windows error.
+
$this->setUpBeforeClass();
$sites = $this->setUpDrupal(2, TRUE, '6');
return $this->localSqlSync();
}
-
+
public function localSqlSync() {
$dump_dir = UNISH_SANDBOX . "/dump-dir";
if (!is_dir($dump_dir)) {
$uid = $row[1];
$this->assertEquals("user+$uid@localhost", $row[2], 'email address was sanitized on destination site.');
$this->assertEquals($name, $row[0]);
-
- // Copy stage to dev with --sanitize and a fixed sanitized email
- $sync_options = array(
- 'sanitize' => NULL,
- 'yes' => NULL,
- 'dump-dir' => $dump_dir,
- 'sanitize-email' => 'user@localhost',
- );
- $this->drush('sql-sync', array('@stage', '@dev'), $sync_options);
-
- $options = array(
- 'root' => $this->webroot(),
- 'uri' => 'dev',
- 'yes' => NULL,
- );
- // Confirm that the sample user's email address has been sanitized on the dev site
- $this->drush('user-information', array($name), $options + array('pipe' => NULL));
- $output = $this->getOutput();
- $row = str_getcsv($output);
- $uid = $row[1];
- $this->assertEquals("user@localhost", $row[2], 'email address was sanitized (fixed email) on destination site.');
- $this->assertEquals($name, $row[0]);
}
}
$items['unit-return-options'] = array(
'description' => 'Return options as function result.',
'bootstrap' => DRUSH_BOOTSTRAP_NONE,
- );
- $items['missing-callback'] = array(
- 'description' => 'Command with no callback function, to test error reporting.',
- 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
- );
- $items['unit-drush-dependency'] = array(
- 'description' => 'Command depending on an unknown commandfile.',
- 'bootstrap' => DRUSH_BOOTSTRAP_NONE,
- 'drush dependencies' => array('unknown-commandfile'),
- );
+ );
return $items;
}
// user-cancel
// create content
- $this->drush('php-script', array('create_node_types'), $options + array('script-path' => dirname(__FILE__) . '/resources'));
+ $eval = $this->create_node_types_php();
$this->drush('php-eval', array($eval), $options);
- $eval = "\$node = (object) array('title' => 'foo', 'uid' => 2, 'type' => 'page',); node_save(\$node);";
+ $eval = "
+ \$node = (object) array(
+ 'title' => 'foo',
+ 'uid' => 2,
+ 'type' => 'page',
+ );
+ node_save(\$node);
+ ";
$this->drush('php-eval', array($eval), $options);
$this->drush('user-cancel', array($name), $options + array('delete-content' => NULL));
$eval = 'print (string) user_load(2)';