diff --git a/README.txt b/README.txt index 08078bf..9e4ad88 100644 --- a/README.txt +++ b/README.txt @@ -65,6 +65,10 @@ STANDARD CONFIGURATION: $ which drush +5. Optional. 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. + ADDITIONAL CONFIGURATIONS FOR MAMP: ----------------------------------- @@ -83,7 +87,7 @@ If you want to use php 5.3.x, add this line instead export PATH="/Applications/MAMP/Library/bin:/Applications/MAMP/bin/php5.3/bin:$PATH" If you have MAMP v.1.84 or lower, this configuration will work for both version of php: - + export PATH="/Applications/MAMP/Library/bin:/Applications/MAMP/bin/php5/bin:$PATH" Additionally, you may need to adjust your php.ini settings before you can use @@ -160,12 +164,12 @@ disable_classes are empty. INSTALLING DRUSH ON WINDOWS: ---------------------------- -Windows support is improving, but is still lacking! 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: - http://drush.ws/drush_windows_installer. + http://drush.ws/drush_windows_installer. Please see that page for more information on running Drush on Windows. @@ -261,7 +265,7 @@ COMMANDS Drush can be extended to run your own commands. Writing a Drush command is no harder than writing simple Drupal modules, since they both follow the same structure. -See sandwich.drush.inc for light details on the internals of a Drush command file. +See examples/sandwich.drush.inc for light details on the internals of a Drush command file. Otherwise, the core commands in Drush are good models for your own commands. You can put your Drush command file in a number of places: diff --git a/commands/core/archive.drush.inc b/commands/core/archive.drush.inc index 7b3465e..cd92d4a 100644 --- a/commands/core/archive.drush.inc +++ b/commands/core/archive.drush.inc @@ -98,7 +98,7 @@ function drush_archive_dump($sites_subdirs = '@self') { } else { $first = current($full); - $prefix = count($sites_subdirs) > 1 ? 'multiple_sites' : $first['default']['default']['database']; + $prefix = count($sites_subdirs) > 1 ? 'multiple_sites' : str_replace('/', '-', $first['databases']['default']['default']['database']); } $temp_dest_name = "$prefix.$date.tar"; @@ -171,7 +171,7 @@ function drush_archive_dump($sites_subdirs = '@self') { foreach ($alias['databases'] as $dbkey => $target) { // Use a subdirectory name matching the docroot name. drush_mkdir("{$tmp}/{$docroot}"); - $result_file = "{$tmp}/{$target['default']['database']}.sql"; + $result_file = $tmp . '/' . basename($target['default']['database']) . '.sql'; $table_selection = drush_sql_get_table_selection(); list($dump_exec, $dump_file) = drush_sql_build_dump_command($table_selection, $target['default'], $result_file); drush_shell_exec($dump_exec); @@ -207,7 +207,8 @@ function drush_archive_dump($sites_subdirs = '@self') { ); // Add info for each DB connection (usually only 1); foreach ($alias['databases'] as $dbkey => $target) { - $site["database-$dbkey-file"] = './' . $target['default']['database'] . '.sql'; + // basename() is for sqlite's sake. + $site["database-$dbkey-file"] = './' . basename($target['default']['database'] . '.sql'); $site["database-$dbkey-driver"] = $target['default']['driver']; } // The section title is the sites subdirectory name. diff --git a/commands/core/core.drush.inc b/commands/core/core.drush.inc index 906ec53..fd47668 100644 --- a/commands/core/core.drush.inc +++ b/commands/core/core.drush.inc @@ -189,8 +189,8 @@ function core_drush_command() { $items['site-install'] = array( 'description' => 'Install Drupal along with modules/themes/configuration using the specified install profile.', 'arguments' => array( - 'profile' => 'the install profile you wish to run. defaults to \'default\' in D6, \'standard\' in D7', - 'key=value...' => 'any additional settings you wish to pass to the profile. Only support on D7. The key is in the form [form name].[parameter name].', + 'profile' => 'the install profile you wish to run. defaults to \'default\' in D6, \'standard\' in D7+', + 'key=value...' => 'any additional settings you wish to pass to the profile. Only support on D7+. The key is in the form [form name].[parameter name].', ), 'options' => array( 'db-url' => 'A Drupal 6 style database URL. Only required for initial install - not re-install.', @@ -209,7 +209,7 @@ function core_drush_command() { 'examples' => array( 'drush site-install expert --locale=uk' => '(Re)install using the expert install profile. Set default language to Ukranian.', 'drush site-install --db-url=mysql://root:pass@localhost:port/dbname' => 'Install using the specified DB params.', - 'drush site-install --db-url=sqlite:/full/path/to/database.sqlite' => 'Install using SQLite (D7 only).', + 'drush site-install --db-url=sqlite://sites/example.com/files/.ht.sqlite' => 'Install using SQLite (D7+ only).', 'drush site-install --account-name=joe --account-pass=mom' => 'Re-install with specified uid1 credentials.', 'drush site-install standard install_configure_form.site_default_country=FR my_profile_form.my_settings.key=value' => 'Pass additional arguments to the profile (D7 only).', ), @@ -267,9 +267,9 @@ function core_drush_command() { $items['batch-process'] = array( 'description' => 'Process operations in the specified batch set', 'hidden' => TRUE, - 'arguments' => array( - 'batch-id' => 'The batch id that will be processed', - ), + 'arguments' => array( + 'batch-id' => 'The batch id that will be processed', + ), 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN, ); @@ -287,6 +287,21 @@ function core_drush_command() { 'topic' => TRUE, 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, ); + $items['core-quick-drupal'] = array( + 'description' => 'Download, install, serve and login to Drupal with minimal configuration and dependencies.', + 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH, + 'aliases' => array('qd'), + 'arguments' => array( + 'site' => 'Short name for the site to be created - used as a directory name and as sqlite file name. Optional - if omitted timestamped "quick-drupal" directory will be used instead.', + 'projects' => 'A list of projects to download into the new site. If projects contain extensions (modules or themes) with the same name they will be enabled by default. See --enable option to control this behaviour further.', + ), + 'examples' => array( + '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=admin --browser=firefox --cache --yes' => 'Fire up stable release of Drupal site called "testsite" .', + ), + ); + // Add in options/engines. + drush_core_quick_drupal_options($items); return $items; } @@ -304,6 +319,19 @@ function core_help_complete() { * Command argument complete callback. * * @return + * Array of available profile names. + */ +function core_site_install_complete() { + $max = drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_ROOT); + if ($max >= DRUSH_BOOTSTRAP_DRUPAL_ROOT) { + return array('values' => array_keys(drush_find_profiles(DRUPAL_ROOT))); + } +} + +/** + * Command argument complete callback. + * + * @return * Array of available site aliases. */ function core_core_rsync_complete() { @@ -671,8 +699,106 @@ function _drush_core_is_named_in_array($key, $the_array) { } /** + * Callback for core-quick-drupal command. + */ +function drush_core_quick_drupal() { + $requests = FALSE; + $args = func_get_args(); + if (empty($args)) { + $name = 'quick-drupal-' . gmdate('YmdHis', $_SERVER['REQUEST_TIME']); + } + else { + $name = array_shift($args); + if (!empty($args)) { + $requests = pm_parse_arguments($args, FALSE); + } + } + $base = getcwd() . '/' . $name; + drush_set_option('destination', $base); + drush_set_option('backend', TRUE); + drush_set_option('drupal-project-rename', 'drupal'); + if (drush_invoke('pm-download', array(drush_get_option('core', 'drupal'))) === FALSE) { + return drush_set_error('QUICK_DRUPAL_CORE_DOWNLOAD_FAIL', 'Drupal core download/extract failed.'); + } + drush_set_option('root', $base . '/drupal'); + if (!drush_get_option('db-url', FALSE)) { + drush_set_option('db-url', 'sqlite:' . $base . '/' . $name . '.sqlite'); + } + if (!drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_ROOT)) { + return drush_set_error('QUICK_DRUPAL_ROOT_LOCATE_FAIL', 'Unable to locate Drupal root directory.'); + } + if ($requests) { + // Unset --destination, so that downloads go to the site directories. + drush_unset_option('destination'); + if (drush_invoke('pm-download', $requests) === FALSE) { + return drush_set_error('QUICK_DRUPAL_PROJECT_DOWNLOAD_FAIL', 'Project download/extract failed.'); + } + } + drush_invoke('site-install'); + // Log in with the admin user. + drush_set_option('user', '1'); + if (!drush_bootstrap_to_phase(DRUSH_BOOTSTRAP_DRUPAL_LOGIN)) { + return drush_set_error('QUICK_DRUPAL_INSTALL_FAIL', 'Drupal core install failed.'); + } + $enable = pm_parse_arguments(drush_get_option('enable', $requests)); + if (!empty($enable)) { + if (drush_invoke('pm-enable', $enable) === FALSE) { + return drush_set_error('QUICK_DRUPAL_PROJECT_ENABLE_FAIL', 'Project enable failed.'); + } + } + drush_print(dt('Login URL: ') . drush_invoke('user-login')); + if ($server = drush_get_option('server', '/')) { + drush_invoke('runserver', array($server)); + } +} + +/** + * Include options and engines for core-quick-drupal command, aggregated from + * other command options that are available. We prefix option descriptons, + * to make the long list more navigable. + * + * @param $items + * The core commandfile command array, by reference. Used to include + * site-install options and add options and engines for core-quick-drupal. + */ +function drush_core_quick_drupal_options(&$items) { + $options = array( + 'core' => 'Drupal core to download. Defaults to "drupal" (latest stable version).', + 'enable' => 'Specific extensions (modules or themes) to enable. By default, extensions with the same name as requested projects will be enabled automatically.', + 'server' => 'Host IP address and port number to bind to and path to open in web browser (hyphen to clear a default path), all elements optional. See runserver examples for shorthand.', + 'no-server' => 'Avoid starting runserver (and browser) for the created Drupal site.', + 'browser' => 'Optional name of a browser to open site in. If omitted the OS default browser will be used. Set --no-browser to disable.', + ); + $pm = pm_drush_command(); + foreach ($pm['pm-download']['options'] as $option => $description) { + $options[$option] = 'Download option: ' . $description; + } + // Unset a few options that are not usable here, as we control them ourselves + // or they are otherwise implied by the environment. + unset($options['destination']); + unset($options['drupal-project-rename']); + unset($options['default-major']); + unset($options['use-site-dir']); + foreach ($items['site-install']['options'] as $option => $description) { + $options[$option] = 'Site install option: ' . $description; + } + unset($options['sites-subdir']); + $runserver = runserver_drush_command(); + foreach ($runserver['runserver']['options'] as $option => $description) { + $options[$option] = 'Runserver option: ' . $description; + } + unset($options['user']); + $items['core-quick-drupal']['options'] = $options; + $items['core-quick-drupal']['engines'] = $pm['pm-download']['engines']; +} + +/** * Command callback. Runs "naked" php scripts * and drush "shebang" scripts ("#!/usr/bin/env drush"). + * + * @params + * Command arguments, optional. First argument is site name, remaining + * argument(s) are contrib modules to install. */ function drush_core_php_script() { $found = FALSE; diff --git a/commands/core/drupal/batch.inc b/commands/core/drupal/batch.inc index c7ea875..41dee24 100644 --- a/commands/core/drupal/batch.inc +++ b/commands/core/drupal/batch.inc @@ -58,7 +58,8 @@ function _drush_backend_batch_process($command = 'batch-process', $args) { else { $data = drush_invoke_process('@self', $command, $args, array($batch['id'])); } - $finished = drush_get_error() || !$data || ($data['context']['drush_batch_process_finished'] == TRUE); + + $finished = drush_get_error() || !$data || (isset($data['context']['drush_batch_process_finished']) && $data['context']['drush_batch_process_finished'] == TRUE); } } } diff --git a/commands/core/drupal/environment.inc b/commands/core/drupal/environment.inc index b9cbfeb..33dd2ff 100644 --- a/commands/core/drupal/environment.inc +++ b/commands/core/drupal/environment.inc @@ -18,6 +18,19 @@ function drush_get_modules() { } /** + * Returns drupal required modules, including modules declared as required dynamically. + */ +function _drush_drupal_required_modules($module_info) { + $required = drupal_required_modules(); + foreach ($module_info as $name => $module) { + if (isset($module->info['required']) && $module->info['required']) { + $required[] = $name; + } + } + return array_unique($required); +} + +/** * Return dependencies and its status for modules. * * @param $modules diff --git a/commands/core/drupal/environment_6.inc b/commands/core/drupal/environment_6.inc index a8d622f..455f44d 100644 --- a/commands/core/drupal/environment_6.inc +++ b/commands/core/drupal/environment_6.inc @@ -26,6 +26,19 @@ function drush_get_modules() { return $modules; } +/** + * Returns drupal required modules, including their dependencies. + * + * A module may alter other module's .info to set a dependency on it. + * See for example http://drupal.org/project/phpass + */ +function _drush_drupal_required_modules($module_info) { + $required = drupal_required_modules(); + foreach ($required as $module) { + $required = array_merge($required, $module_info[$module]->info['dependencies']); + } + return $required; +} /** * Return dependencies and its status for modules. diff --git a/commands/core/site_install.drush.inc b/commands/core/site_install.drush.inc index 0ff781d..589b1bb 100644 --- a/commands/core/site_install.drush.inc +++ b/commands/core/site_install.drush.inc @@ -23,7 +23,7 @@ function drush_core_pre_site_install() { } if ($sites_subdir = drush_get_option('sites-subdir')) { // Needed so that we later bootstrap into the right site. - drush_set_option('uri', 'http://'.$sites_subdir); + drush_set_option('uri', 'http://' . $sites_subdir); } else { $sites_subdir = 'default'; diff --git a/commands/core/upgrade.drush.inc b/commands/core/upgrade.drush.inc index db15309..c29ad08 100644 --- a/commands/core/upgrade.drush.inc +++ b/commands/core/upgrade.drush.inc @@ -110,7 +110,7 @@ function drush_upgrade_site_upgrade_check_parameters($target_key = NULL) { * * This runs bootstrapped to the SOURCE site. */ -function drush_upgrade_site_upgrade($target_key) { +function drush_upgrade_site_upgrade($target_key = NULL) { // Presume we are ready to go (n.b. some checks already performed in 'validate') $ready_to_upgrade = TRUE; $result = TRUE; diff --git a/commands/core/watchdog.drush.inc b/commands/core/watchdog.drush.inc index f1da505..48490cc 100644 --- a/commands/core/watchdog.drush.inc +++ b/commands/core/watchdog.drush.inc @@ -223,7 +223,10 @@ function core_watchdog_format_result($result, $full = FALSE) { // Message. if (drush_drupal_major_version() >= 6) { - $variables = unserialize($result->variables); + $variables = $result->variables; + if (is_string($variables)) { + $variables = unserialize(); + } if (is_array($variables)) { $result->message = strtr($result->message, $variables); } diff --git a/commands/pm/download.pm.inc b/commands/pm/download.pm.inc index 218ffd8..a94ab9b 100644 --- a/commands/pm/download.pm.inc +++ b/commands/pm/download.pm.inc @@ -31,25 +31,25 @@ function drush_pm_download_validate() { // Validate --variant or enforce a sane default. $variant = drush_get_option('variant', FALSE); if ($variant) { - if (!in_array($variant, array('core', 'no-core', 'make'))) { + if (!in_array($variant, array('full', 'projects', 'profile-only'))) { drush_log(dt('Unknown variant !variant. Valid values: !variations', array('!variant' => $variant, '!variations' => implode(', ', $variations))), 'error'); } } - // core and no-core variants are only valid for wget package handler. + // full and projects variants are only valid for wget package handler. $package_handler = drush_get_option('package-handler', 'wget'); - if (($package_handler != 'wget') && ($variant != 'make')) { - $new_variant = 'make'; + if (($package_handler != 'wget') && ($variant != 'profile-only')) { + $new_variant = 'profile-only'; if ($variant) { drush_log(dt('Variant !variant is incompatible with !ph package-handler.', array('!variant' => $variant, '!ph' => $package_handler)), 'warning'); } } - // If we are working on a drupal root, core variant is not an option. + // If we are working on a drupal root, full variant is not an option. else if (drush_get_context('DRUSH_BOOTSTRAP_PHASE') >= DRUSH_BOOTSTRAP_DRUPAL_ROOT) { - if ((!$variant) || (($variant == 'core') && (!isset($new_variant)))) { - $new_variant = 'no-core'; + if ((!$variant) || (($variant == 'full') && (!isset($new_variant)))) { + $new_variant = 'projects'; } - if ($variant == 'core') { - drush_log(dt('Variant core is not a valid option within a Drupal root.'), 'warning'); + if ($variant == 'full') { + drush_log(dt('Variant full is not a valid option within a Drupal root.'), 'warning'); } } @@ -463,4 +463,3 @@ function _pm_download_destination($type) { return $destination; } - diff --git a/commands/pm/package_handler/git_drupalorg.inc b/commands/pm/package_handler/git_drupalorg.inc index fe933e8..60d19cf 100644 --- a/commands/pm/package_handler/git_drupalorg.inc +++ b/commands/pm/package_handler/git_drupalorg.inc @@ -51,16 +51,32 @@ function package_handler_download_project(&$request, $release) { $request['repository'] = $repository; $tag = $release['tag']; - if (drush_get_option('cache')) { - $gitcache = drush_server_home() . '/.drush/gitcache'; - drush_gitcache_check($gitcache); - - drush_shell_cd_and_exec($gitcache, 'git remote show'); - $cached_modules = drush_shell_exec_output(); - - if (!in_array($request['name'], $cached_modules)) { - drush_gitcache_add_project($gitcache, $request, $repository); + // If the --cache option was given, create a new git reference cache of the + // remote repository, or update the existing cache to fetch recent changes. + if (drush_get_option('cache') && ($cachedir = drush_directory_cache())) { + $gitcache = $cachedir . '/git'; + $projectcache = $gitcache . '/' . $request['name'] . '.git'; + drush_mkdir($gitcache); + // Setup a new cache, if we don't have this project yet. + if (!file_exists($projectcache)) { + // --mirror works similar to --bare, but retrieves all tags, local + // branches, remote branches, and any other refs (notes, stashes, etc). + // @see http://stackoverflow.com/questions/3959924 + $command = 'git clone --mirror'; + if (drush_get_context('DRUSH_VERBOSE')) { + $command .= ' --verbose --progress'; + } + $command .= ' %s %s'; + drush_shell_cd_and_exec($gitcache, $command, $repository, $request['name'] . '.git'); + } + // If we already have this project, update it to speed up subsequent clones. + else { + // A --mirror clone is fully synchronized with `git remote update` instead + // of `git fetch --all`. + // @see http://stackoverflow.com/questions/6150188 + drush_shell_cd_and_exec($projectcache, 'git remote update'); } + $gitcache = $projectcache; } // Clone the repo into its appropriate target location. @@ -181,46 +197,3 @@ function package_handler_post_download($project) { } } -/** - * Check for gitcache directory or create it. - * - * @param $gitcache - * The location of the gitcache directory. - */ -function drush_gitcache_check($gitcache) { - if (is_dir(drush_server_home() . '/.drush')) { - drush_mkdir($gitcache); - if (!drush_shell_cd_and_exec($gitcache, 'test $(git rev-parse --is-bare-repository) == "true"')) { - drush_shell_cd_and_exec($gitcache, 'git init --bare'); - drush_shell_cd_and_exec($gitcache, 'git config core.compression 1'); - } - } -} - -/** - * Add a project to the cache. - * - * @param $gitcache - * The location of the gitcache directory. - * @param $request - * The project array with name, base and full (final) paths. - * @param $release - * The release details array from drupal.org. - */ -function drush_gitcache_add_project($gitcache, $request, $repository) { - // On the first fetch, git will check for common commits with the remote - // repository, so to speed this up we initially fetch from a local clone. - $tmpdir = drush_tempdir(); - $command = 'git clone --bare '; - if (drush_get_context('DRUSH_VERBOSE')) { - $command .= ' --verbose --progress '; - } - $command .= '%s'; - if (drush_shell_cd_and_exec($tmpdir, $command, $repository)) { - // Temporarily set the remote url to our temporary clone and fetch from there. - drush_shell_cd_and_exec($gitcache, 'git remote add %s %s && git fetch %s --tags', $request['name'], $tmpdir, $request['name']); - - // Change the remote URL and fetch normally. - drush_shell_cd_and_exec($gitcache, 'git remote set-url %s %s && git fetch %s --tags', $request['name'], $repository, $request['name']); - } -} diff --git a/commands/pm/package_handler/wget.inc b/commands/pm/package_handler/wget.inc index e3a8da5..393405c 100644 --- a/commands/pm/package_handler/wget.inc +++ b/commands/pm/package_handler/wget.inc @@ -33,7 +33,7 @@ function package_handler_validate() { * @param $release The release details array from drupal.org. */ function package_handler_download_project(&$request, $release) { - // Install profiles come in 3 variants. User may specify which one she wants. + // Install profiles come in several variants. User may specify which one she wants. if ($request['project_type'] == 'profile') { // @todo Use xpath to get the right file url. $files = $release['files']; diff --git a/commands/pm/pm.drush.inc b/commands/pm/pm.drush.inc index 2a02d7f..715d86d 100644 --- a/commands/pm/pm.drush.inc +++ b/commands/pm/pm.drush.inc @@ -485,6 +485,21 @@ function drush_get_projects(&$extensions = NULL) { } /** + * Returns a list of enabled modules. + * + * This is a simplified version of module_list(). + */ +function pm_module_list() { + $enabled = array(); + $rsc = drush_db_select('system', 'name', 'type=:type AND status=:status', array(':type' => 'module', ':status' => 1)); + while ($row = drush_db_result($rsc)) { + $enabled[$row] = $row; + } + + return $enabled; +} + +/** * @} End of "defgroup extensions". */ @@ -643,10 +658,11 @@ function drush_pm_enable() { $args = pm_parse_arguments(func_get_args()); + $extension_info = drush_get_extensions(); + $recheck = TRUE; while ($recheck) { $recheck = FALSE; - $extension_info = drush_get_extensions(); // Classify $args in themes, modules or unknown. $modules = array(); @@ -729,9 +745,7 @@ function drush_pm_enable() { if (!$dependencies_ok) { return FALSE; } - $enabled = array_keys(array_filter(drush_get_modules(), 'pm_is_enabled')); - $modules = array_diff(array_merge($modules, $all_dependencies), $enabled); - + $modules = array_diff(array_merge($modules, $all_dependencies), pm_module_list()); // Discard modules which doesn't meet requirements. require_once drush_get_context('DRUSH_DRUPAL_ROOT') . '/includes/install.inc'; foreach ($modules as $key => $module) { @@ -766,10 +780,7 @@ function drush_pm_enable() { // Enable modules and pass dependency validation in form submit. if (!empty($modules)) { drush_module_enable($modules); - $current = drupal_map_assoc($enabled, 'pm_true'); - $processed = drupal_map_assoc($modules, 'pm_true'); - $active_modules = array_merge($current, $processed); - drush_system_modules_form_submit($active_modules); + drush_system_modules_form_submit(pm_module_list()); } // Inform the user of final status. @@ -841,14 +852,12 @@ function drush_pm_disable() { if (!empty($modules)) { // Add enabled dependents to the list of modules to disable. - $enabled = array_keys(array_filter(drush_get_modules(), 'pm_is_enabled')); $dependents = drush_module_dependents($modules, $extension_info); - $dependents = array_unique($dependents); - $dependents = array_intersect($dependents, $enabled); + $dependents = array_intersect($dependents, pm_module_list()); $modules = array_merge($modules, $dependents); // Discard required modules. - $required = drupal_required_modules(); + $required = drush_drupal_required_modules($extension_info); foreach ($required as $module) { if (isset($modules[$module])) { unset($modules[$module]); @@ -880,9 +889,7 @@ function drush_pm_disable() { // Disable modules and pass dependency validation in form submit. if (!empty($modules)) { drush_module_disable($modules); - $active_modules = array_diff($enabled, $modules); - $active_modules = drupal_map_assoc($active_modules, 'pm_true'); - drush_system_modules_form_submit($active_modules); + drush_system_modules_form_submit(pm_module_list()); } // Inform the user of final status. @@ -939,6 +946,7 @@ function drush_pm_uninstall() { drush_include_engine('drupal', 'environment'); $module_info = drush_get_modules(); + $required = drush_drupal_required_modules($module_info); // Discards modules which are enabled, not found or already uninstalled. foreach ($modules as $key => $module) { @@ -960,7 +968,7 @@ function drush_pm_uninstall() { else { $dependents = array(); foreach (drush_module_dependents(array($module), $module_info) as $dependent) { - if ($module_info[$dependent]->schema_version != -1) { + if (!in_array($dependent, $required) && ($module_info[$dependent]->schema_version != -1)) { $dependents[] = $dependent; } } @@ -992,23 +1000,6 @@ function drush_pm_uninstall() { } /** - * Array filter callback to return enabled modules. - * - * @param $module - * A module object as returned by drush_get_modules(). - */ -function pm_is_enabled($module) { - return $module->status; -} - -/** - * Callback helper. - */ -function pm_true() { - return TRUE; -} - -/** * 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. @@ -1532,7 +1523,7 @@ function drush_pm_updatecode_postupdate() { drupal_load_updates(); // @see system_requirements(). - foreach (module_list() as $module) { + foreach (pm_module_list() as $module) { $updates = drupal_get_schema_versions($module); if ($updates !== FALSE) { $default = drupal_get_installed_schema_version($module); @@ -1969,7 +1960,7 @@ function drush_pm_extensions_in_project($project) { } // Special case: find profiles for drupal < 7.x (no .info) if ($project['drupal_version'][0] < 7) { - foreach (drush_scan_directory($project['project_install_location'], "/.*\.profile$/", $nomask) as $filename => $info) { + foreach (drush_find_profiles($project['project_install_location']) as $filename => $info) { $found['profile'][] = $info->name; } } diff --git a/commands/pm/updatecode.pm.inc b/commands/pm/updatecode.pm.inc index b464274..97a5d4c 100644 --- a/commands/pm/updatecode.pm.inc +++ b/commands/pm/updatecode.pm.inc @@ -97,7 +97,7 @@ function drush_pm_updatecode() { else { $update_info[$name]['status'] = DRUSH_PM_REQUESTED_UPDATE; } - // Set the candidate version to the requested release + // Set the candidate version to the requested release. $update_info[$name]['candidate_version'] = $release['version']; } } @@ -105,7 +105,8 @@ function drush_pm_updatecode() { // Table headers. $rows[] = array(dt('Name'), dt('Installed version'), dt('Proposed version'), dt('Status')); - // Process releases, notifying user of status and building a list of proposed updates + // Process releases, notifying user of status and + // building a list of proposed updates. $updateable = pm_project_filter($update_info, $rows, $security_only); // Pipe preparation. @@ -118,7 +119,7 @@ function drush_pm_updatecode() { $pipe .= str_replace(' ', '-', pm_update_filter($project)). "\n"; } drush_print_pipe($pipe); - // Automatically curtail update process if in pipe mode + // Automatically curtail update process if in pipe mode. $updateable = array(); } @@ -137,7 +138,8 @@ function drush_pm_updatecode() { drush_print($contents); drush_print(); - // If specific project updates were requested then remove releases for all others + // If specific project updates were requested then remove releases for all + // others. if (!empty($requests)) { foreach ($updateable as $name => $project) { if (!isset($requests[$name])) { @@ -146,7 +148,13 @@ function drush_pm_updatecode() { } } - // If there are any locked projects that were not requested, then remove them + // Prevent update of core if --no-core was specified. + if (isset($updateable['drupal']) && drush_get_option('no-core', FALSE)) { + unset($updateable['drupal']); + drush_print(dt('Skipping core update (--no-core specified).')); + } + + // If there are any locked projects that were not requested, then remove them. if (!empty($locked_list)) { foreach ($updateable as $name => $project) { if ((isset($locked_list[$name])) && (!isset($requests[$name]))) { @@ -161,7 +169,7 @@ function drush_pm_updatecode() { $drush_update_available = drush_check_self_update(); } - // Do no updates in simulated mode + // Do no updates in simulated mode. if (drush_get_context('DRUSH_SIMULATE')) { return drush_log(dt('No action taken in simulated mode.'), 'ok'); return TRUE; @@ -188,12 +196,7 @@ function drush_pm_updatecode() { else { drush_print(dt("NOTE: A code update for the Drupal core is available.")); } - if (drush_get_option('no-core', FALSE)) { - drush_print(dt('Skipping core update (--no-core specified).')); - } - else { - drush_print(dt("Drupal core will be updated after all of the non-core modules are updated.\n")); - } + drush_print(dt("Drupal core will be updated after all of the non-core modules are updated.\n")); } } @@ -218,7 +221,7 @@ function drush_pm_updatecode() { } // After projects are updated we can update core. - if ($core_update_available && !drush_get_option('no-core', FALSE)) { + if ($core_update_available) { drush_print(); return _pm_update_core($drupal_project, $tmpfile); } diff --git a/commands/runserver/runserver-drupal.inc b/commands/runserver/runserver-drupal.inc index a4ca965..d860580 100644 --- a/commands/runserver/runserver-drupal.inc +++ b/commands/runserver/runserver-drupal.inc @@ -10,12 +10,14 @@ * Extends the HTTPServer class, handling request routing and environment. */ class DrupalServer extends HTTPServer { - public $http_host; + // We pass in variables, rather than querying options here, to allow this to + // potentially be used in other commands. + public $site, $path, $conf_inject, $user, $watchdog, $debug, $first_request_complete; /** * This is the equivalent of .htaccess, passing requests to files if they * exist, and all other requests to index.php. We also set a number - * of CGI environment variables here. + * of CGI environment variables here. */ function route_request($request) { $cgi_env = array(); @@ -23,16 +25,24 @@ class DrupalServer extends HTTPServer { // 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://localhost:' . $this->port; + $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 simultanious requests on some systems. + // 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 = drush_get_option('conf-inject', array('drupal_http_request_fails' => FALSE)); + $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; $doc_root = DRUPAL_ROOT; @@ -53,8 +63,38 @@ class DrupalServer extends HTTPServer { } $cgi_env['SCRIPT_NAME'] = '/index.php'; - $cgi_env['HTTP_HOST'] = $cgi_env['SERVER_NAME'] = $this->http_host; + $cgi_env['HTTP_HOST'] = $cgi_env['SERVER_NAME'] = $this->site; return $this->get_php_response($request, $doc_root . '/index.php', $cgi_env); } + + /** + * 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); + } + } + + /** + * Override request done event. + */ + 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); + } + } } diff --git a/commands/runserver/runserver-prepend.php b/commands/runserver/runserver-prepend.php index 8f89d94..15a835c 100644 --- a/commands/runserver/runserver-prepend.php +++ b/commands/runserver/runserver-prepend.php @@ -1,29 +1,51 @@ 'Runs a lightweight built in http server for development.', 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_SITE, 'arguments' => array( - 'addr:port' => 'Host IP address and port number to bind to (default 127.0.0.1:8888). The IP is optional, in which case just pass in the numeric port.', + '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( '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.', '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)', + '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'), + 'examples' => array( + 'drush rs 8080' => 'Start runserver on 127.0.0.1, port 8080.', + 'drush rs 10.0.0.28:80' => 'Start runserver on 10.0.0.28, port 80.', + '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 --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; } @@ -75,7 +87,9 @@ function drush_core_runserver_validate() { /** * Callback for runserver command. */ -function drush_core_runserver($addrport = '8888') { +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'; @@ -91,30 +105,95 @@ function drush_core_runserver($addrport = '8888') { // Include the library and our class that extends it. require_once $httpserverfile; require_once 'runserver-drupal.inc'; - - // Determine configuration. - if (is_numeric($addrport)) { - $addr = '127.0.0.1'; - $port = $addrport; + + // 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', + 'port' => '8888', + 'path' => '', + ); + $user_default = runserver_parse_uri(drush_get_option('default-server', '127.0.0.1:8888')); + $uri = runserver_parse_uri($uri) + $user_default + $drush_default; + if (ltrim($uri['path'], '/') == '-') { + // Allow a path of a single hyphen to clear a default path. + $uri['path'] = ''; } - else { - $addrport = explode(':', $addrport); - if (count($addrport) !== 2 && is_numeric($addrport[1])) { - return drush_set_error('RUNSERVER_INVALID_ADDRPORT', dt('Invalid address/port argument - should be either numeric (port only), or in the "host:port" format..')); + + // Determine and set the new URI. + $hostname = $addr = $uri['host']; + if (drush_get_option('dns', FALSE)) { + if (ip2long($hostname)) { + $hostname = gethostbyaddr($hostname); + } + else { + $addr = gethostbyname($hostname); } - $addr = $addrport[0]; - $port = $addrport[1]; } - + drush_set_context('DRUSH_URI', 'http://' . $hostname . ':' . $uri['port']); + // We delete any registered files here, since they are not caught by Ctrl-C. _drush_delete_registered_files(); - + // Create a new server instance and start it running. $server = new DrupalServer(array( 'addr' => $addr, - 'port' => $port, - 'serverid' => 'Drush runserver', + '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(); } + +/** + * Parse a URI or partial URI (including just a port, host IP or path). + * + * @param $uri + * String that can contain partial URI. + * @return array + * URI array as returned by parse_url. + */ +function runserver_parse_uri($uri) { + if ($uri[0] == ':') { + // ':port/path' shorthand, insert a placeholder hostname to allow parsing. + $uri = 'placeholder-hostname' . $uri; + } + $first_part = substr($uri, 0, strpos($uri, '/')); + if (ip2long($first_part)) { + // 'IP/path' shorthand, insert a schema to allow parsing. + $uri = 'http://' . $uri; + } + $uri = parse_url($uri); + if (empty($uri)) { + return drush_set_error('RUNSERVER_INVALID_ADDRPORT', dt('Invalid argument - should be in the "host:port/path" format, numeric (port only) or non-numeric (path only).')); + } + if (count($uri) == 1 && isset($uri['path'])) { + if (is_numeric($uri['path'])) { + // Port only shorthand. + $uri['port'] = $uri['path']; + unset($uri['path']); + } + else if (ip2long($uri['path'])) { + // IP only shorthand. + $uri['host'] = $uri['path']; + unset($uri['path']); + } + } + if (isset($uri['host']) && $uri['host'] == 'placeholder-hostname') { + unset($uri['host']); + } + return $uri; +} \ No newline at end of file diff --git a/commands/sql/sql.drush.inc b/commands/sql/sql.drush.inc index 22e162e..7728d1f 100644 --- a/commands/sql/sql.drush.inc +++ b/commands/sql/sql.drush.inc @@ -408,7 +408,7 @@ function drush_sql_build_dump_command($table_selection, $db_spec = NULL, $file = // Postgres or MySQL equivalents. We may be able to fake some in the // future, but for now, let's just support simple dumps. $exec .= ' ".dump"'; - if ($file = drush_get_option('result-file')) { + if ($file) { $exec .= ' > '. $file; } break; @@ -663,8 +663,7 @@ function drush_sql_sanitize() { /** * Get a database specification for the active DB connection. Honors the - * 'database' and 'target command' line options. If not in a Drupal site, - * honors a --db-url option. + * 'database' and 'target command' line options. Honors a --db-url option. * * @return * An info array describing a database target. @@ -673,30 +672,27 @@ function _drush_sql_get_db_spec() { $database = drush_get_option('database', 'default'); $target = drush_get_option('target', 'default'); - // Use --db-url if present. if ($url = drush_get_option('db-url')) { $url = is_array($url) ? $url[$database] : $url; $db_spec = drush_convert_db_from_db_url($url); $db_spec['db_prefix'] = drush_get_option('db-prefix'); return $db_spec; } - - // Otherwise, read settings.php. - drush_bootstrap_max(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION); - switch (drush_drupal_major_version()) { - case 6: - $url = isset($GLOBALS['db_url']) ? $GLOBALS['db_url'] : drush_get_option('db-url', NULL); - $url = is_array($url) ? $url[$database] : $url; - $db_spec = drush_convert_db_from_db_url($url); - $db_spec['db_prefix'] = isset($GLOBALS['db_prefix']) ? $GLOBALS['db_prefix'] : drush_get_option('db-prefix', NULL); - return $db_spec; - - default: - // We don't use DB API here `sql-sync` would have to messily addConnection. - if (!isset($GLOBALS['databases']) || !array_key_exists($database, $GLOBALS['databases']) || !array_key_exists($target, $GLOBALS['databases'][$database])) { - return NULL; - } - return $GLOBALS['databases'][$database][$target]; + elseif (drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_CONFIGURATION)) { + switch (drush_drupal_major_version()) { + case 6: + $url = isset($GLOBALS['db_url']) ? $GLOBALS['db_url'] : drush_get_option('db-url', NULL); + $url = is_array($url) ? $url[$database] : $url; + $db_spec = drush_convert_db_from_db_url($url); + $db_spec['db_prefix'] = isset($GLOBALS['db_prefix']) ? $GLOBALS['db_prefix'] : drush_get_option('db-prefix', NULL); + return $db_spec; + default: + // We don't use DB API here `sql-sync` would have to messily addConnection. + if (!isset($GLOBALS['databases']) || !array_key_exists($database, $GLOBALS['databases']) || !array_key_exists($target, $GLOBALS['databases'][$database])) { + return NULL; + } + return $GLOBALS['databases'][$database][$target]; + } } } @@ -884,7 +880,7 @@ function _drush_sql_get_credentials($db_spec = NULL) { // SQLite doesn't do user management, instead relying on the filesystem // for that. So the only info we really need is the path to the database // file, and not as a "--key=value" parameter. - return ' ' . $db_spec['database']; + return ' ' . $db_spec['database']; break; case 'sqlsrv': @@ -1042,6 +1038,7 @@ function drush_sql_su($db_spec, $site_alias_record = NULL) { * in a Windows shell. Set TRUE of the CREATE is not running on the bash command line. */ function drush_sql_build_createdb_sql($db_spec, $quoted = FALSE) { + $sql = array(); switch (_drush_sql_get_scheme($db_spec)) { case 'mysql': $dbname = $quoted ? '`' . $db_spec['database'] . '`' : $db_spec['database']; @@ -1066,8 +1063,11 @@ function drush_sql_build_createdb_sql($db_spec, $quoted = FALSE) { * @return boolean */ function drush_sql_db_exists($db_spec) { - $connect_yes_db = _drush_sql_connect($db_spec); + if ($db_spec['driver'] == 'sqlite') { + return file_exists($db_spec['database']); + } + $connect_yes_db = _drush_sql_connect($db_spec); $database = $db_spec['database']; unset($db_spec['database']); $connect_no_db = _drush_sql_connect($db_spec); @@ -1082,8 +1082,6 @@ function drush_sql_db_exists($db_spec) { drush_shell_exec("$connect_no_db -t -c \"$sql\""); $output = drush_shell_exec_output(); return (bool)$output[0]; - case 'sqlite': - return file_exists($database); case 'sqlsrv': // TODO: untested, but the gist is here. $sql = "if db_id('$database') IS NOT NULL print 1"; diff --git a/drush.bat b/drush.bat index dcfcf49..34c1947 100644 --- a/drush.bat +++ b/drush.bat @@ -1,3 +1,16 @@ @echo off +rem Drush automatically determines the user's home directory by checking for +rem HOME or HOMEDRIVE/HOMEPATH environment variables, and the temporary +rem directory by checking for TEMP, TMP, or WINDIR environment variables. +rem The home path is used for caching Drush commands and the git --reference +rem cache. The temporary directory is used by various commands, including +rem package manager for downloading projects. +rem You may want to specify a path that is not user-specific here; e.g., to +rem keep cache files on the same filesystem, or to share caches with other +rem users. + +rem set HOME=H:/drush +rem set TEMP=H:/drush + REM See http://drupal.org/node/506448 for more information. @php.exe "%~dp0drush.php" %* --php="php.exe" diff --git a/drush.complete.sh b/drush.complete.sh index de38bc5..f1924b4 100644 --- a/drush.complete.sh +++ b/drush.complete.sh @@ -3,7 +3,7 @@ # BASH completion script for Drush. # # Place this in your /etc/bash_completion.d/ directory or source it from your -# ~/.bash_completion file. +# ~/.bash_completion or ~/.bash_profile files. # Ensure drush is available. have drush || return diff --git a/examples/example.aliases.drushrc.php b/examples/example.aliases.drushrc.php index 6d68359..5932d48 100644 --- a/examples/example.aliases.drushrc.php +++ b/examples/example.aliases.drushrc.php @@ -159,6 +159,8 @@ * - 'php': path to custom php interpreter, defaults to /usr/bin/php * - 'php-options': commandline options for php interpreter, you may * want to set this to '-d error_reporting="E_ALL^E_DEPRECATED"' + * - 'variables' : An array of name/value pairs which override Drupal variables. + * These values take precedence even over settings.php variable overrides. * - 'command-specific': These options will only be set if the alias * is used with the specified command. In the example below, the option * `--no-cache` will be selected whenever the @stage alias @@ -191,6 +193,9 @@ # '%files' => 'sites/mydrupalsite.com/files', # '%custom' => '/my/custom/path', # ), +# 'variables => array( +# site_name => 'My Drupal site', +# ), # 'command-specific' => array ( # 'sql-sync' => array ( # 'no-cache' => TRUE, @@ -200,6 +205,7 @@ #$aliases['dev'] = array( # 'uri' => 'dev.mydrupalsite.com', # 'root' => '/path/to/drupal/root', +# 'variables' => array('mail_system' => array('default-system' => 'DevelMailLog')), # ); #$aliases['server'] = array( # 'remote-host' => 'mystagingserver.myisp.com', diff --git a/examples/example.drushrc.php b/examples/example.drushrc.php index 40fd2a0..a1d5e48 100644 --- a/examples/example.drushrc.php +++ b/examples/example.drushrc.php @@ -1,6 +1,6 @@ '!git pull', // we've all done it. -# 'pulldb' => '!git pull && drush updatedb', -# 'noncore' => 'pm-list --no-core', -# 'wipe' => 'cache-clear all', -# ); +# $options['shell-aliases']['pull'] = '!git pull'; // We've all done it. +# $options['shell-aliases']['pulldb'] = '!git pull && drush updatedb'; +# $options['shell-aliases']['noncore'] = 'pm-list --no-core'; +# $options['shell-aliases']['wipe'] = 'cache-clear all'; +// 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'; // Load a drushrc.php configuration file from the current working directory. # $options['c'] = '.'; // Disable the nag warning for Windows. -// Consider improving Windows support: http://drupal.org/node/766080. +// @see http://drupal.org/node/766080 # $options['check_os'] = FALSE; // Control automatically check for updates in pm-updatecode and drush version. @@ -102,13 +102,13 @@ // Specify directory where sql-dump should store backups of database // dumps. @DATABASE is replaced with the name of the database being // dumped, and @DATE is replaced with the current time and date of the -// dump. TRUE will cause sql-dump to use the same backup directory that -// pm-updatecode does. -// +// dump. // If set, this can be explicitly overridden by specifying --result-file // on the commandline. The default behavior of dumping to // STDOUT can be achieved via --result-file=0 # $options['result-file'] = '/path/to/backup/dir/@DATABASE_@DATE.sql'; +// TRUE will cause sql-dump to use the same backup directory that pm-updatecode +// does. # $options['result-file'] = TRUE; // Enable verbose mode. @@ -117,12 +117,14 @@ // Show database passwords in 'status' and 'sql-conf' commands # $options['show-passwords'] = 1; -// Default logging level for php notices. Defaults to "notice"; set to "warning" -// if doing drush development. Also make sure that error_reporting is set to E_ALL -// in your php configuration file. See 'drush status' for the path to your php.ini file. +// Default logging level for PHP notices. Defaults to "notice". +// Set to "warning" when doing drush development. Also make sure that +// error_reporting is set to E_ALL in your php configuration file. +// See 'drush status' for the path to your php.ini file. # $options['php-notices'] = 'warning'; -// Specify options to pass to ssh in backend invoke. (Default is to prohibit password authentication; uncomment to change) +// Specify options to pass to ssh in backend invoke. +// Default is to prohibit password authentication. # $options['ssh-options'] = '-o PasswordAuthentication=no'; // Set 'remote-os' to 'Windows' to make drush use Windows shell escape @@ -135,54 +137,57 @@ // version of rsync installed on any system you are using with // drush). Note that drush requires at least rsync version 2.6.4 // for some functions to work correctly. -// // Note that this option can also be set in a site alias. This // is preferable if newer versions of rsync are available on some // of the systems you use. // See: http://drupal.org/node/955092 # $options['rsync-version'] = '2.6.9'; -/* -* The output charset suitable to pass to iconv PHP function as out_charset -* parameter. Drush will convert its output from UTF-8 to the charset specified -* here. It is possible to use //TRANSLIT and //IGNORE charset name suffixes -* (see iconv documentation). If not defined conversion will not be performed. -*/ +/** + * The output charset suitable to pass to iconv PHP function as out_charset parameter. + * + * Drush will convert its output from UTF-8 to the charset specified here. It is + * possible to use //TRANSLIT and //IGNORE charset name suffixes (see iconv + * documentation). If not defined conversion will not be performed. + */ # $options['output_charset'] = 'ISO-8859-1'; # $options['output_charset'] = 'KOI8-R//IGNORE'; # $options['output_charset'] = 'ISO-8859-1//TRANSLIT'; -/* - * Multiple command execution options +/** + * Multiple command execution options. + * + * By default, drush will prepend the name of the site to the output of any + * multiple-site command execution. To disable this behavior, set the --no-label + * option. */ -// By default, drush will prepend the name of the -// site to the output of any multiple-site command -// execution. To disable this behavior, set the -// --no-label option # $options['no-label'] = TRUE; -/* +/** * Customize this associative array with your own tables. This is the list of * tables whose *data* is skipped by the 'sql-dump' and 'sql-sync' commands when * a structure-tables-key is provided. You may add new tables to the existing * array or add a new element. */ -$options['structure-tables'] = array( - 'common' => array('cache', 'cache_filter', 'cache_menu', 'cache_page', 'history', 'sessions', 'watchdog'), -); +# $options['structure-tables']['common'] = array('cache', 'cache_filter', 'cache_menu', 'cache_page', 'history', 'sessions', 'watchdog'); -/* +/** * Customize this associative array with your own tables. This is the list of * tables that are entirely omitted by the 'sql-dump' and 'sql-sync' commands * when a skip-tables-key is provided. This is useful if your database contains * non Drupal tables used by some other application or during a migration for * example. You may add new tables to the existing array or add a new element. */ -$options['skip-tables'] = array( - 'common' => array('migration_data1', 'migration_data2'), -); +# $options['skip-tables']['common'] = array('migration_data1', 'migration_data2'); -/* +/** + * Override specific entries in Drupal's 'variable' table or settings.php + */ +# $options['variables']['site_name'] = 'My Drupal site'; +# $options['variables']['theme_default'] = 'minnelli'; +# $options['variables']['anonymous'] = 'Visitor'; + +/** * Command-specific options * * To define options that are only applicable to certain commands, @@ -223,20 +228,3 @@ $options['skip-tables'] = array( // you can use the regular shell command by typing in the full path // to the command (e.g. /bin/grep). # $command_specific['core-cli'] = array('override' => 'help,dd,sa'); - -/** - * Variable overrides: - * - * To override specific entries in the 'variable' table for this site, - * set them here. Any configuration setting from the 'variable' - * table can be given a new value. We use the $override global here - * to make sure that changes from settings.php can not wipe out these - * settings. - * - * Remove the leading hash signs to enable. - */ -# $override = array( -# 'site_name' => 'My Drupal site', -# 'theme_default' => 'minnelli', -# 'anonymous' => 'Visitor', -# ); diff --git a/examples/sandwich.drush.inc b/examples/sandwich.drush.inc index 677e303..2993b16 100644 --- a/examples/sandwich.drush.inc +++ b/examples/sandwich.drush.inc @@ -38,7 +38,7 @@ function sandwich_drush_command() { $items['make-me-a-sandwich'] = array( 'description' => "Makes a delicious sandwich.", 'arguments' => array( - 'filling' => 'The type of the sandwich (turkey, cheese, etc.)', + 'filling' => 'The type of the sandwich (turkey, cheese, etc.). Defaults to ascii.', ), 'options' => array( 'spreads' => 'Comma delimited list of spreads (e.g. mayonnaise, mustard)', @@ -65,6 +65,8 @@ function sandwich_drush_command() { return $items; } + + /** * Implementation of hook_drush_help(). * @@ -169,9 +171,20 @@ function drush_sandwich_make_me_a_sandwich($filling = 'ascii') { if (drush_get_context('DRUSH_NOCOLOR')) { $filename = dirname(__FILE__) . '/sandwich-nocolor.txt'; - } + } else { $filename = dirname(__FILE__) . '/sandwich.txt'; } drush_print(file_get_contents($filename)); } + +/** + * Command argument complete callback. Provides argument + * values for shell completion. + * + * @return + * Array of popular fillings. + */ +function sandwich_make_me_a_sandwich_complete() { + return array('values' => array('turkey', 'cheese', 'jelly', 'butter')); +} diff --git a/includes/backend.inc b/includes/backend.inc index a8c1ec9..2bc317e 100644 --- a/includes/backend.inc +++ b/includes/backend.inc @@ -803,7 +803,7 @@ function _drush_backend_argument_string($data, $os = NULL) { $options = array(); foreach ($data as $key => $value) { - if (!is_array($value) && !is_object($value) && !is_null($value) && ($value != '')) { + if (!is_array($value) && !is_object($value) && !is_null($value)) { if (substr($key,0,1) != '#') { $options[$key] = $value; } diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index c4e9992..20d189b 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -318,6 +318,9 @@ function drush_bootstrap_to_phase($max_phase_index) { * * @param $max_phase_index * Only attempt bootstrap to the specified level. + * + * @return int + * The maximum phase to which we bootstrapped. */ function drush_bootstrap_max($max_phase_index = FALSE) { $phases = _drush_bootstrap_phases(); @@ -749,15 +752,31 @@ function _drush_bootstrap_redo_drupal_site() { * We process and store a normalized set of database credentials * from the loaded configuration file, so we can validate them * and access them easily in the future. + * + * Also override Drupal variables as per --variables option. */ function _drush_bootstrap_drupal_configuration() { - global $conf, $drush_conf_override; + global $conf; drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); - // Overriding the $conf array from drupal CONFIGURATION bootstrap with the - // Overrides we collected on the loaded config files on DRUSH_BOOTSTRAP_DRUSH - $conf = is_array($conf) && is_array($drush_conf_override) ? array_merge($conf, $drush_conf_override) : $conf; + // Unset drupal error handler and restore drush's one. + if (drush_drupal_major_version() >= 7) { + restore_error_handler(); + } + + // Force Drupal6 not to store queries since we are not outputting them. + // Don't run poormanscron during Drush request (D7+). + $override = array( + 'dev_query' => FALSE, + 'cron_safe_threshold' => 0, + ); + $current_override = drush_get_option_list('variables'); + foreach ( $current_override as $pair) { + list($name, $value) = explode('=', $pair, 2); + $override[$name] = $value; + } + $conf = is_array($conf) ? array_merge($conf, $override) : $conf; // Populate the DRUSH_DB_CREDENTIALS with the fields loaded from the configuration. $creds = array(); @@ -836,6 +855,12 @@ function _drush_bootstrap_drupal_full() { if (!drush_get_context('DRUSH_QUIET', FALSE)) { ob_end_clean(); } + + // Unset drupal error handler and restore drush's one. + if (drush_drupal_major_version() == 6) { + restore_error_handler(); + } + // If needed, prod module_implements() to recognize our system_watchdog() implementation. $dogs = module_implements('watchdog'); if (!in_array('system', $dogs)) { diff --git a/includes/cache.inc b/includes/cache.inc index 0119b9d..415e59b 100644 --- a/includes/cache.inc +++ b/includes/cache.inc @@ -378,12 +378,12 @@ class DrushFileCache implements DrushCacheInterface { $bin_dir = $this->cacheDirectory(); $files = array(); if (empty($cid)) { - drush_delete_dir($bin_dir); + drush_delete_dir($bin_dir, TRUE); } else { if ($wildcard) { if ($cid == '*') { - drush_delete_dir($bin_dir); + drush_delete_dir($bin_dir, TRUE); } else { $matches = drush_scan_directory($bin_dir, "/^$cid/", array('.', '..')); diff --git a/includes/command.inc b/includes/command.inc index 6b5e256..3660e07 100644 --- a/includes/command.inc +++ b/includes/command.inc @@ -126,6 +126,7 @@ function _drush_invoke($command, $args, $defined_in_commandfile) { // Only the 'main' callback can send data to backend. if ($var_hook == $hook) { drush_backend_set_result($result); + $return = $result; } _drush_log_drupal_messages(); if (drush_get_error() || ($result === FALSE)) { @@ -163,9 +164,10 @@ function _drush_invoke($command, $args, $defined_in_commandfile) { drush_log(dt("Changes made in !func have been rolled back.", array('!func' => $func)), 'rollback'); } } + return $rollback; } - return !$rollback; + return $return; } /** diff --git a/includes/context.inc b/includes/context.inc index f1c30d2..9f51b67 100644 --- a/includes/context.inc +++ b/includes/context.inc @@ -83,13 +83,19 @@ function drush_context_names() { /** * Return a list of possible drushrc file locations. * + * @context + * A valid drush context from drush_context_names(). + * @prefix + * Optional. Specify a prefix to prepend to ".drushrc.php" when looking + * for config files. Most likely used by contrib commands. * @return * An associative array containing possible config files to load * The keys are the 'context' of the files, the values are the file * system locations. */ -function _drush_config_file($context) { +function _drush_config_file($context, $prefix = NULL) { $configs = array(); + $config_file = $prefix ? $prefix . '.' . 'drushrc.php' : 'drushrc.php'; // Did the user explicitly specify a config file? if ($config = drush_get_option(array('c', 'config'))) { @@ -100,28 +106,28 @@ function _drush_config_file($context) { } if ($site_path = drush_get_context('DRUSH_DRUPAL_SITE_ROOT')) { - $configs['site'] = $site_path . "/drushrc.php"; + $configs['site'] = $site_path . "/" . $config_file; } if ($drupal_root = drush_get_context('DRUSH_DRUPAL_ROOT')) { - $configs['drupal'] = $drupal_root . '/drushrc.php'; + $configs['drupal'] = $drupal_root . '/' . $config_file; } // in the user home directory if (!is_null(drush_server_home())) { - $configs['user'] = drush_server_home() . '/.drushrc.php'; + $configs['user'] = drush_server_home() . '/.' . $config_file; } // in $HOME/.drush directory if (!is_null(drush_server_home())) { - $configs['home.drush'] = drush_server_home() . '/.drush/drushrc.php'; + $configs['home.drush'] = drush_server_home() . '/.drush/' . $config_file; } // In the system wide configuration folder. - $configs['system'] = drush_get_context('ETC_PREFIX', '') . '/etc/drush/drushrc.php'; + $configs['system'] = drush_get_context('ETC_PREFIX', '') . '/etc/drush/' . $config_file; // in the drush installation folder - $configs['drush'] = dirname(__FILE__) . '/../drushrc.php'; + $configs['drush'] = dirname(__FILE__) . '/../' . $config_file; return empty($configs[$context]) ? '' : $configs[$context]; } @@ -157,13 +163,6 @@ function drush_load_config_file($context, $config) { } function drush_set_config_options($context, $options, $override = array()) { - global $drush_conf_override; - - // Only reset $drush_conf_override if the array is not set, otherwise keep old values and append new values to it. - if (!isset($drush_conf_override)) { - $drush_conf_override = array(); - } - // Copy 'config-file' into 'context-path', converting to an array to hold multiple values if necessary if (isset($options['config-file'])) { if (isset($options['context-path'])) { @@ -178,29 +177,6 @@ function drush_set_config_options($context, $options, $override = array()) { drush_set_config_special_contexts($options); drush_set_context($context, $options); - - // Instruct core not to store queries since we are not outputting them. - // Don't run poormanscron during drush request (D7+). - $defaults = array( - 'dev_query' => FALSE, - 'cron_safe_threshold' => 0, - ); - foreach ($defaults as $key => $value) { - // This can be overridden by a command or a drushrc file if needed. - if (!isset($drush_conf_override[$key])) { - $drush_conf_override[$key] = $value; - } - } - - /** - * Allow the drushrc.php file to override $conf settings. - * This is a separate variable because the $conf array gets - * initialized to an empty array, in the drupal bootstrap process, - * and changes in settings.php would wipe out the drushrc.php settings. - */ - if (!empty($override)) { - $drush_conf_override = array_merge($drush_conf_override, $override); - } } /** diff --git a/includes/drupal.inc b/includes/drupal.inc index 31c6b56..8746085 100644 --- a/includes/drupal.inc +++ b/includes/drupal.inc @@ -117,4 +117,8 @@ function _drush_log_update_sql($ret) { } } } +} + +function drush_find_profiles($drupal_root , $key = 'name') { + return drush_scan_directory($drupal_root . '/profiles', "/.*\.profile$/", array('.', '..', 'CVS', 'tests'), 0, 2, $key); } \ No newline at end of file diff --git a/includes/drush.inc b/includes/drush.inc index 0785087..4b16200 100644 --- a/includes/drush.inc +++ b/includes/drush.inc @@ -307,12 +307,12 @@ function _convert_csv_to_array($args) { * Return a reduced set of important options. Used by help command. * * @return - * An associative array containing the option definition as the key, + * An associative array containing the option definition as the key, * and a descriptive array for each of the available options. The array * elements for each item are: * * - short-form: The shortcut form for specifying the key on the commandline. - * - never-post: If TRUE, backend invoke will never POST this item's value + * - never-post: If TRUE, backend invoke will never POST this item's value * on STDIN; it will always be sent as a commandline option. * - never-propagate: If TRUE, backend invoke will never pass this item on * to the subcommand being executed. @@ -341,6 +341,7 @@ function drush_get_global_options($brief = FALSE) { $options['user'] = array('short-form' => 'u', 'description' => dt("Specify a Drupal user to login with. May be a name or a number.")); $options['backend'] = array('short-form' => 'b', 'never-propagate' => TRUE, 'description' => dt("Hide all output and return structured data (internal use only).")); $options['choice'] = array('description' => dt("Provide an answer to a multiple-choice prompt.")); + $options['variables'] = array('description' => dt("Comma delimited list of name=value pairs. These values take precedence even over settings.php variable overrides.")); $options['no-label'] = array('description' => dt("Remove the site label that drush includes in multi-site command output(e.g. `drush @site1,@site2 status`).")); $options['nocolor'] = array('context' => 'DRUSH_NOCOLOR', 'description' => dt("Suppress color highlighting on log messages.")); $options['show-passwords'] = array('description' => dt("Show database passwords in commands that display connection information.")); diff --git a/includes/environment.inc b/includes/environment.inc index d5d18ff..3e708ff 100644 --- a/includes/environment.inc +++ b/includes/environment.inc @@ -693,6 +693,14 @@ function drush_get_extensions() { } /** + * + */ +function drush_drupal_required_modules($modules) { + drush_include_engine('drupal', 'environment'); + return _drush_drupal_required_modules($modules); +} + +/** * Return the default theme. * * @return diff --git a/includes/exec.inc b/includes/exec.inc index c00154a..464b48c 100644 --- a/includes/exec.inc +++ b/includes/exec.inc @@ -290,5 +290,56 @@ function drush_shell_exec_output() { } /** + * Starts a background browser/tab for the current site or a specified URL. + * + * Uses a non-blocking proc_open call, so Drush execution will continue. + * + * @param $uri + * Optional URI or site path to open in browser. If omitted, or if a site path + * is specified, the current site home page uri will be prepended if the sites + * hostname resolves. + * @return + * 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) { + if (!parse_url($uri, PHP_URL_HOST)) { + $site = drush_get_context('DRUSH_URI'); + $host = parse_url($site, PHP_URL_HOST); + // Validate that the host part of the URL resolves, so we don't attempt to + // open the browser for http://default or similar invalid hosts. + $hosterror = gethostbyname($host) == $host; + $iperror = ip2long($host) && gethostbyaddr($host) == $host; + if ($hosterror && $iperror) { + drush_log(dt('!host does not appear to be a resolvable hostname or IP, not starting browser.', array('!host' => $host)), 'warning'); + return FALSE; + } + $uri = $site . '/' . ltrim($uri, '/'); + } + if ($browser = drush_get_option('browser', TRUE)) { + if ($browser === TRUE) { + // See if we can find an OS helper to open URLs in default browser. + if (drush_shell_exec('which xdg-open')) { + $browser = 'xdg-open'; + } + else if (drush_shell_exec('which open')) { + $browser = 'open'; + } + else { + // Can't find a valid browser. + $browser = FALSE; + } + } + if ($browser && !drush_get_context('DRUSH_SIMULATE')) { + $pipes = array(); + drush_log(dt('Opening browser at !uri', array('!uri' => $uri))); + proc_close(proc_open($browser . ' ' . drush_escapeshellarg($uri) . ' 2> /dev/null &', array(), $pipes)); + return TRUE; + } + } + return FALSE; +} + +/** * @} End of "defgroup commandwrappers". */ diff --git a/includes/filesystem.inc b/includes/filesystem.inc index d544ac1..71abe92 100644 --- a/includes/filesystem.inc +++ b/includes/filesystem.inc @@ -135,7 +135,7 @@ function drush_copy_dir($src, $dest, $overwrite = FALSE) { // Preflight based on $overwrite if $dest exists. if (file_exists($dest)) { if ($overwrite) { - drush_op('drush_delete_dir', $dest); + drush_op('drush_delete_dir', $dest, TRUE); } else { return drush_set_error('DRUSH_DESTINATION_EXISTS', dt('Destination directory !dest already exists.', array('!dest' => $dest))); @@ -207,7 +207,7 @@ function drush_move_dir($src, $dest, $overwrite = FALSE) { // Preflight based on $overwrite if $dest exists. if (file_exists($dest)) { if ($overwrite) { - drush_op('drush_delete_dir', $dest); + drush_op('drush_delete_dir', $dest, TRUE); } else { return drush_set_error('DRUSH_DESTINATION_EXISTS', dt('Destination directory !dest already exists.', array('!dest' => $dest))); @@ -234,7 +234,7 @@ function drush_move_dir($src, $dest, $overwrite = FALSE) { // If 'rename' fails, then we will use copy followed // by a delete of the source. if (drush_copy_dir($src, $dest)) { - drush_op('drush_delete_dir', $src); + drush_op('drush_delete_dir', $src, TRUE); return TRUE; } @@ -278,9 +278,9 @@ function drush_save_data_to_temp_file($data) { * path is not valid in some setups for Mac. */ function drush_find_tmp() { - static $temporary_directory = NULL; + static $temporary_directory; - if (is_null($temporary_directory)) { + if (!isset($temporary_directory)) { $directories = array(); // Operating system specific dirs. @@ -297,7 +297,10 @@ function drush_find_tmp() { } $windir = getenv('WINDIR'); if (isset($windir)) { - $directories[] = $windir; + // WINDIR itself is not writable, but it always contains a /Temp dir, + // which is the system-wide temporary directory on older versions. Newer + // versions only allow system processes to use it. + $directories[] = $windir . '/Temp'; } } else { @@ -388,7 +391,7 @@ function _drush_delete_registered_files() { // though they did not need to. if (file_exists($file)) { if (is_dir($file)) { - drush_delete_dir($file); + drush_delete_dir($file, TRUE); } else { @chmod($dir, 0777); // Make file writeable diff --git a/includes/sitealias.inc b/includes/sitealias.inc index efd53d7..01c6101 100644 --- a/includes/sitealias.inc +++ b/includes/sitealias.inc @@ -632,18 +632,18 @@ function sitealias_get_databases_from_record(&$alias_record) { * which will be used to first add the database information to the * alias records, invoking sql-conf to look them up if necessary. * - * The options 'database' and 'target' are used to specify which - * specific database should be fetched from the database record; + * The options 'database' and 'target' are used to specify which + * specific database should be fetched from the database record; * they may appear in the alias definition, or may be taken from the * command line options. The values 'default' and 'default' are * used if these options are not specified in either location. * * Note that in the context of sql-sync, the site alias record will - * be taken from one of the source or target aliases - * (e.g. `drush sql-sync @source @target`), which will be overlayed with + * be taken from one of the source or target aliases + * (e.g. `drush sql-sync @source @target`), which will be overlayed with * any options that begin with 'source-' or 'target-', respectively. * Therefore, the commandline options 'source-database' and 'source-target' - * (or 'target-database' and 'source-target') may also affect the operation + * (or 'target-database' and 'source-target') may also affect the operation * of this function. */ function drush_sitealias_get_db_spec(&$alias_record, $default_to_self = FALSE) { @@ -1316,6 +1316,7 @@ function drush_convert_db_from_db_url($db_url) { 'pass' => NULL, 'host' => NULL, 'port' => NULL, + 'path' => NULL, 'database' => NULL, ); $url = (object)array_map('urldecode', $url); @@ -1324,10 +1325,10 @@ function drush_convert_db_from_db_url($db_url) { 'username' => $url->user, 'password' => $url->pass, 'port' => $url->port, - 'host' => $url->host, + 'host' => $url->scheme == 'sqlite' ? '' : $url->host, // Remove leading / character from database names, unless we're installing // to SQLite (which won't have a slash there unless it's part of a path). - 'database' => $url->scheme === 'sqlite' ? $url->path : substr($url->path, 1) + 'database' => $url->scheme == 'sqlite' ? $url->host . $url->path : substr($url->path, 1), ); } @@ -1702,7 +1703,7 @@ function drush_sitealias_evaluate_path($path, &$additional_options, $local_only // When calculating a path for use with rsync, we must correct // absolute paths in the form c:\path when cwrsync is in use. $path = drush_correct_absolute_path_for_exec($path, $os); - + // If there is a $machine component, to the path, then // add it to the beginning $evaluated_path = drush_escapeshellarg($path, $os); diff --git a/tests/archiveDumpTest.php b/tests/archiveDumpTest.php index ca2523e..cd4884c 100644 --- a/tests/archiveDumpTest.php +++ b/tests/archiveDumpTest.php @@ -35,7 +35,9 @@ class archiveDumpCase extends Drush_CommandTestCase { $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); - $this->execute(sprintf('head %s/unish_%s.sql | grep "MySQL dump"', $untar_dest, $uri)); + if (strpos(UNISH_DB_URL, 'mysql') !== FALSE) { + $this->execute(sprintf('head %s/unish_%s.sql | grep "MySQL dump"', $untar_dest, $uri)); + } $this->execute('test -f ' . $untar_dest . '/MANIFEST.ini'); $this->execute('test -d ' . $untar_dest . '/' . $docroot); } diff --git a/tests/drush_testcase.inc b/tests/drush_testcase.inc index dfe2dc5..a731b89 100644 --- a/tests/drush_testcase.inc +++ b/tests/drush_testcase.inc @@ -130,6 +130,10 @@ abstract class Drush_TestCase extends PHPUnit_Framework_TestCase { return getenv('CACHE_PREFIX') . '/' . $subdir; } + function db_url($env) { + return substr(UNISH_DB_URL, 0, 6) == 'sqlite' ? "sqlite://sites/$env/files/unish.sqlite" : UNISH_DB_URL . '/unish_' . $env; + } + function setUpDrupal($num_sites = 1, $install = FALSE, $version_string = '7', $profile = NULL) { $sites_subdirs_all = array('dev', 'stage', 'prod', 'retired', 'elderly', 'dead', 'dust'); $sites_subdirs = array_slice($sites_subdirs_all, 0, $num_sites); @@ -138,7 +142,8 @@ abstract class Drush_TestCase extends PHPUnit_Framework_TestCase { if (is_null($profile)) { $profile = substr($version_string, 0, 1) >= 7 ? 'testing' : 'default'; } - $cache_keys = array($num_sites, $install ? 'install' : 'noinstall', $version_string, $profile); + $db_driver = parse_url(UNISH_DB_URL, PHP_URL_SCHEME); + $cache_keys = array($num_sites, $install ? 'install' : 'noinstall', $version_string, $profile, $db_driver); $source = $this->directory_cache('environments') . '/' . implode('-', $cache_keys) . '.tar.gz'; if (file_exists($source)) { $this->log('Cache HIT. Environment: ' . $source, 'verbose'); @@ -162,7 +167,7 @@ abstract class Drush_TestCase extends PHPUnit_Framework_TestCase { // Stash details about each site. foreach ($sites_subdirs as $subdir) { $this->sites[$subdir] = array( - 'db_url' => UNISH_DB_URL . '/unish_' . $subdir, + 'db_url' => $this->db_url($subdir), ); // Make an alias for the site $alias_definition = array($subdir => array('root' => $root, 'uri' => $subdir)); @@ -191,7 +196,7 @@ abstract class Drush_TestCase extends PHPUnit_Framework_TestCase { if ($install) { $options = array( 'root' => $root, - 'db-url' => UNISH_DB_URL . '/unish_' . $env, + 'db-url' => $this->db_url($env), 'sites-subdir' => $env, 'yes' => NULL, 'quiet' => NULL, diff --git a/tests/phpunit.xml.dist b/tests/phpunit.xml.dist index 45d27c1..c277994 100644 --- a/tests/phpunit.xml.dist +++ b/tests/phpunit.xml.dist @@ -10,6 +10,7 @@ + diff --git a/tests/siteUpgradeTest.php b/tests/siteUpgradeTest.php index 62760b1..ff3eb1a 100644 --- a/tests/siteUpgradeTest.php +++ b/tests/siteUpgradeTest.php @@ -14,6 +14,11 @@ class siteUpgradeCase extends Drush_CommandTestCase { function testUpgrade() { + if (strpos(UNISH_DB_URL, 'sqlite') !== FALSE) { + $this->markTestSkipped('Drupal 6 does not run on SQLite.'); + return; + } + $sites = $this->setUpDrupal(1, TRUE, '6'); $root = $this->webroot(); diff --git a/tests/sqlSyncTest.php b/tests/sqlSyncTest.php index 0060409..d5b9772 100644 --- a/tests/sqlSyncTest.php +++ b/tests/sqlSyncTest.php @@ -18,6 +18,11 @@ class sqlSyncTest extends Drush_CommandTestCase { * General handling of site aliases will be in sitealiasTest.php. */ public function testLocalSqlSync() { + if (strpos(UNISH_DB_URL, 'sqlite') !== FALSE) { + $this->markTestSkipped('SQL Sync does not apply to SQLite.'); + return; + } + $sites = $this->setUpDrupal(2, TRUE); $dump_dir = UNISH_SANDBOX . "/dump-dir"; mkdir($dump_dir);