├── drush.info ├── misc └── windrush_build │ ├── assets │ ├── setenv.bat │ ├── notify_env_change.exe │ ├── composer.bat │ ├── drush.bat │ └── setenv.js │ └── README.md ├── drush_logo-black.png ├── unish.sh ├── lib └── Drush │ ├── Sql │ ├── SqlException.php │ ├── Sql8.php │ ├── Sql7.php │ ├── SqlVersion.php │ ├── Sql6.php │ └── Sqlsqlsrv.php │ ├── Queue │ ├── QueueException.php │ ├── Queue6.php │ ├── QueueBase.php │ ├── QueueInterface.php │ ├── Queue7.php │ └── Queue8.php │ ├── Role │ ├── RoleException.php │ ├── Role7.php │ ├── Role6.php │ └── Role8.php │ ├── User │ ├── UserListException.php │ ├── UserSingle8.php │ ├── UserSingle6.php │ ├── User7.php │ ├── User6.php │ ├── User8.php │ ├── UserSingle7.php │ └── UserVersion.php │ ├── Command │ ├── CommandfilesInterface.php │ └── Commandfiles.php │ ├── Boris │ └── VarDumperInspector.php │ ├── Cache │ └── JSONCache.php │ ├── Make │ └── Parser │ │ ├── ParserYaml.php │ │ └── ParserInterface.php │ ├── UpdateService │ ├── StatusInfoInterface.php │ └── StatusInfoDrupal6.php │ ├── Log │ └── DrushLog.php │ └── Boot │ ├── EmptyBoot.php │ ├── DrupalBoot7.php │ └── DrupalBoot6.php ├── tests ├── makefiles │ ├── included.make │ ├── included2.make │ ├── included2.make.yml │ ├── translations.make │ ├── git-simple-8.make │ ├── included.make.yml │ ├── qd-devel.make │ ├── includes-sub-platform.make │ ├── recursion.make │ ├── bzr.make │ ├── translations-inside.make │ ├── translations-inside7.make │ ├── svn.make │ ├── use-distribution-as-core.make │ ├── includes-platform.make │ ├── recursion-override.make │ ├── include.make │ ├── contrib-destination.make.yml │ ├── defaults.make │ ├── defaults.make.yml │ ├── include.make.yml │ ├── md5-fail.make │ ├── contrib-destination.make │ ├── bz2.make │ ├── md5-succeed.make │ ├── get.make │ ├── bz2-singlefile.make │ ├── options-array.make │ ├── patches-local-test-wysiwyg.patch │ ├── file.make │ ├── limited-projects-libraries.make │ ├── includes-main.make │ ├── git-simple.make │ ├── options-project.make │ ├── file-extract.make │ ├── patches.make │ ├── gzip.make │ ├── subtree.make │ └── git.make ├── hooks │ └── magic_help_alter │ │ └── magic.drush.inc ├── resources │ ├── queue_script-D7.php │ ├── queue_script-D8.php │ ├── testDispatchUsingAlias_script.php │ ├── create_node_types.php │ └── example.profile ├── Unish │ ├── UnishProcessFailedError.php │ └── UnitUnishTestCase.php ├── completetestsite.drush.inc ├── siteSetUnitTest.php ├── testChildren.sh ├── drushScriptTest.php ├── pmReleaseNotesTest.php ├── batchTest.php ├── Drush │ └── Tests │ │ └── Make │ │ └── Parser │ │ ├── ParserIniTest.php │ │ └── ParserYamlTest.php ├── backendUnitTest.php ├── siteSetTest.php ├── completetest.drush.inc ├── README.md ├── filesystemTest.php ├── siteAliasUnitTest.php ├── phpunit.xml.dist ├── roleTest.php ├── queueTest.php ├── imageTest.php ├── expandWildcardTablesUnitTest.php ├── watchdogTest.php ├── releaseInfoTest.php ├── generateMakeTest.php ├── siteIntallD6Test.php ├── quickDrupalTest.php ├── variableTest.php ├── sqlConnectCreateTest.php ├── commandUnitTest.php ├── COVERAGE.txt ├── cacheCommandTest.php ├── fieldTest.php └── siteSshTest.php ├── .gitignore ├── examples ├── sandwich-topic.txt ├── drush ├── pm_update.drush.inc ├── helloworld.script ├── git-bisect.example.sh ├── example.drush.ini └── sandwich-nocolor.txt ├── commands ├── core │ ├── outputformat │ │ ├── php.inc │ │ ├── json.inc │ │ ├── csv_or_string.inc │ │ ├── yaml.inc │ │ ├── message.inc │ │ ├── string.inc │ │ ├── print_r.inc │ │ ├── variables.inc │ │ ├── table.inc │ │ ├── var_export.inc │ │ ├── topics │ │ │ └── table.html │ │ └── key_value.inc │ ├── scratch.php │ ├── druplicon.drush.inc │ ├── cli.drush.inc │ ├── drupal │ │ ├── image.inc │ │ ├── image_7.inc │ │ ├── cache_8.inc │ │ ├── cache.inc │ │ ├── site_install.inc │ │ └── site_install_7.inc │ ├── shellalias.drush.inc │ ├── browse.drush.inc │ └── queue.drush.inc ├── runserver │ ├── d8-rs-router.php │ └── runserver-prepend.php ├── xh.drush.inc ├── make │ └── update.make.inc └── pm │ └── version_control │ └── backup.inc ├── mkdocs.yml ├── drush.bat ├── docs ├── strict-options.md ├── output-formats.md ├── usage.md ├── shellaliases.md └── examples.md ├── composer.json ├── drush.complete.sh ├── CONTRIBUTING.md └── .travis.yml /drush.info: -------------------------------------------------------------------------------- 1 | drush_version=8.0-dev 2 | -------------------------------------------------------------------------------- /misc/windrush_build/assets/setenv.bat: -------------------------------------------------------------------------------- 1 | @cscript //NoLogo %~dp0setenv.js 2 | @pause -------------------------------------------------------------------------------- /drush_logo-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tracker/drush/master/drush_logo-black.png -------------------------------------------------------------------------------- /unish.sh: -------------------------------------------------------------------------------- 1 | # Run Unish, the test suite for Drush. 2 | vendor/bin/phpunit --configuration tests $@ 3 | -------------------------------------------------------------------------------- /lib/Drush/Sql/SqlException.php: -------------------------------------------------------------------------------- 1 | account->uid); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /lib/Drush/Command/CommandfilesInterface.php: -------------------------------------------------------------------------------- 1 | 'test', 6 | 'url' => 'http://drupal.org/project/issues/rss/drupal?categories=All', 7 | 'refresh' => 3600, 8 | 'block' => 5, 9 | )); 10 | 11 | // Let cron call DrupalQueue::createItem() for us. 12 | aggregator_cron(); 13 | -------------------------------------------------------------------------------- /tests/makefiles/bz2.make: -------------------------------------------------------------------------------- 1 | core = 6.x 2 | api = 2 3 | 4 | ; bison-1.30.tar.bz2 contains wrapper folder "bison-1.30/". 5 | ; This should move that wrapper folder to sites/all/libraries/bison/ . 6 | libraries[bison][destination] = libraries 7 | libraries[bison][download][type] = get 8 | libraries[bison][download][url] = http://ftp.gnu.org/gnu/bison/bison-1.30.tar.bz2 9 | -------------------------------------------------------------------------------- /tests/makefiles/md5-succeed.make: -------------------------------------------------------------------------------- 1 | core = 6.x 2 | api = 2 3 | 4 | libraries[drush_make][download][type] = "file" 5 | libraries[drush_make][download][url] = "http://drupalcode.org/project/drush_make.git/blob_plain/8d4e770:/README.txt" 6 | libraries[drush_make][download][filename] = "README.txt" 7 | libraries[drush_make][download][md5] = "c8968d801a953b9ea735364d6f3dfabc" 8 | -------------------------------------------------------------------------------- /misc/windrush_build/README.md: -------------------------------------------------------------------------------- 1 | This directory holds the build script for Drush's Windows distribution. This script is only useful to Drush administrators who are generating a new build. 2 | 3 | To use this script: 4 | 5 | - Edit the metadata at the top 6 | - Run the script 7 | - Attach the .zip file to the corresponding Release on Github. 8 | - Update the links at bottom of docs/install.md 9 | -------------------------------------------------------------------------------- /tests/makefiles/get.make: -------------------------------------------------------------------------------- 1 | core = 6.x 2 | api = 2 3 | 4 | ; Tarball file download 5 | libraries[drush_make][download][type] = file 6 | libraries[drush_make][download][url] = http://ftp.drupal.org/files/projects/drush_make-6.x-2.0-beta8.tar.gz 7 | libraries[drush_make][directory_name] = drush_make 8 | libraries[drush_make][destination] = libraries 9 | libraries[drush_make][lock] = Locked 10 | -------------------------------------------------------------------------------- /tests/resources/queue_script-D8.php: -------------------------------------------------------------------------------- 1 | 'test', 8 | 'url' => 'http://drupal.org/project/issues/rss/drupal?categories=All', 9 | 'refresh' => 3600, 10 | )); 11 | $feed->save(); 12 | 13 | // Let cron call QueueInterface::createItem() for us. 14 | aggregator_cron(); 15 | -------------------------------------------------------------------------------- /tests/resources/testDispatchUsingAlias_script.php: -------------------------------------------------------------------------------- 1 | TRUE)); 4 | $valuesWithoutAlias = drush_invoke_process("@dev", "unit-return-argv", array(), array(), array()); 5 | return array('with' => $valuesUsingAlias['object'], 'without' => $valuesWithoutAlias['object']); 6 | 7 | -------------------------------------------------------------------------------- /commands/core/outputformat/php.inc: -------------------------------------------------------------------------------- 1 | getOutput()) { 10 | $message .= "\n\nCommand output:\n" . $output; 11 | } 12 | if ($stderr = $process->getErrorOutput()) { 13 | $message .= "\n\nCommand stderr:\n" . $stderr; 14 | } 15 | 16 | parent::__construct($message); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/makefiles/patches-local-test-wysiwyg.patch: -------------------------------------------------------------------------------- 1 | diff --git a/wysiwyg.module b/wysiwyg.module 2 | index 771cbd7..647ac31 100644 3 | --- a/wysiwyg.module 4 | +++ b/wysiwyg.module 5 | @@ -112,6 +112,12 @@ function wysiwyg_theme() { 6 | } 7 | 8 | /** 9 | + * This patch doesn't really do anything. It just adds a comment to 10 | + * wysiwyg.module so that we can test Drush Make's support of applying patches 11 | + * that are on the local filesystem. 12 | + */ 13 | + 14 | +/** 15 | * Implementation of hook_help(). 16 | */ 17 | function wysiwyg_help($path, $arg) { 18 | -------------------------------------------------------------------------------- /lib/Drush/Cache/JSONCache.php: -------------------------------------------------------------------------------- 1 | 'No-op command, used to test various completions for commands that start the same as other commands.', 13 | 'bootstrap' => DRUSH_BOOTSTRAP_NONE, 14 | 'callback' => 'drush_completetest_noop', 15 | ); 16 | return $items; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /tests/makefiles/limited-projects-libraries.make: -------------------------------------------------------------------------------- 1 | core = "7.x" 2 | api = 2 3 | 4 | projects[boxes][version] = "1.0-beta7" 5 | 6 | projects[admin_menu][version] = "3.0-rc1" 7 | 8 | ; Use drupal.org as a nice stable source of libraries. 9 | libraries[drush_make][download][type] = "file" 10 | libraries[drush_make][download][url] = "http://ftp.drupal.org/files/projects/drush_make-6.x-2.3.tar.gz" 11 | 12 | ; Use drupal.org as a nice stable source of libraries. 13 | libraries[token][download][type] = "file" 14 | libraries[token][download][url] = "http://ftp.drupal.org/files/projects/token-7.x-1.0-rc1.tar.gz" 15 | 16 | -------------------------------------------------------------------------------- /lib/Drush/User/User6.php: -------------------------------------------------------------------------------- 1 | $name)); 20 | } 21 | 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | public function load_by_mail($mail) { 26 | return user_load(array('mail' => $mail)); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /lib/Drush/Make/Parser/ParserYaml.php: -------------------------------------------------------------------------------- 1 | array("b" => 2, "c" => 3), 15 | * "d" => array("e" => 5, "f" => 6) 16 | * ); 17 | * 18 | * Output with --format=json: 19 | * 20 | * {"a":{"b":2,"c":3},"d":{"e":5,"f":6}} 21 | */ 22 | class drush_outputformat_json extends drush_outputformat { 23 | function format($input, $metadata) { 24 | return drush_json_encode($input); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Drush/Sql/Sql8.php: -------------------------------------------------------------------------------- 1 | is_windows()) { 12 | $this->markTestSkipped('Site-set not currently available on Windows.'); 13 | } 14 | 15 | $tmp_path = UNISH_TMP; 16 | putenv("TMPDIR=$tmp_path"); 17 | $posix_pid = posix_getppid(); 18 | $username = drush_get_username(); 19 | 20 | $expected_file = UNISH_TMP . '/drush-env-' . $username . '/drush-drupal-site-' . $posix_pid; 21 | $filename = drush_sitealias_get_envar_filename(); 22 | 23 | $this->assertEquals($expected_file, $filename); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Drush docs 2 | pages: 3 | - Home: index.md 4 | - General: 5 | - Install: install.md 6 | - Configure: configure.md 7 | - Usage: usage.md 8 | - Example files: examples.md 9 | - Cron: cron.md 10 | - Make: make.md 11 | - Output formats: output-formats.md 12 | - Shell aliases: shellaliases.md 13 | - Shell scripts: shellscripts.md 14 | - Strict options: strict-options.md 15 | - Bastion: bastion.md 16 | - Writing commands: 17 | - Command Authoring: commands.md 18 | - Bootstrap: bootstrap.md 19 | - Context system: context.md 20 | theme: readthedocs 21 | site_author: "" 22 | repo_url: https://github.com/drush-ops/drush 23 | include_search: true 24 | #use_directory_urls: false 25 | -------------------------------------------------------------------------------- /tests/testChildren.sh: -------------------------------------------------------------------------------- 1 | # $AUTH_TOKEN is provided via a secure Travis environment variable. 2 | # Secure environment variables are not set for pull requests that 3 | # originated from another repository, so skip child tests if $AUTH_TOKEN 4 | # is empty. 5 | if [ -n "$AUTH_TOKEN" ] 6 | then 7 | # After a travis build succeeds, run tests from any child repository defined in $TEST_CHILDREN 8 | for CHILD in $TEST_CHILDREN 9 | do 10 | BUILD_NUM=$(curl -s "https://api.travis-ci.org/repos/$CHILD/builds" | grep -o '^\[{"id":[0-9]*,' | grep -o '[0-9]' | tr -d '\n') 11 | echo "Restarting build $BUILD_NUM for $CHILD" 12 | curl -X POST https://api.travis-ci.org/builds/$BUILD_NUM/restart --header "Authorization: token "$AUTH_TOKEN 13 | done 14 | fi 15 | -------------------------------------------------------------------------------- /lib/Drush/Queue/Queue6.php: -------------------------------------------------------------------------------- 1 | setIndentation(2); 21 | // The level where you switch to inline YAML is set to PHP_INT_MAX to 22 | // ensure this does not occur. 23 | $output = $dumper->dump($input, PHP_INT_MAX, NULL, NULL, TRUE); 24 | return $output; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /commands/core/scratch.php: -------------------------------------------------------------------------------- 1 | is_windows()) { 16 | $this->markTestSkipped('Environment variable tests not currently functional on Windows.'); 17 | } 18 | 19 | $options = array(); 20 | $env = array('PHP_OPTIONS' => '-d default_mimetype="text/drush"'); 21 | $this->drush('ev', array('print ini_get("default_mimetype");'), $options, NULL, NULL, self::EXIT_SUCCESS, NULL, $env); 22 | $output = $this->getOutput(); 23 | $this->assertEquals('text/drush', $output); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/makefiles/options-project.make: -------------------------------------------------------------------------------- 1 | core = 6.x 2 | api = 2 3 | 4 | ; Test that revision passed in uses git to download project. 5 | projects[context_admin][download][revision] = "eb9f05e" 6 | 7 | ; Test that make preserves VCS directories. 8 | projects[context_admin][options][working-copy] = TRUE 9 | 10 | ; Test that make defaults to download type of git if any download 11 | ; parameters are present. 12 | projects[cck_signup][download][revision] = "2fe932c" 13 | 14 | ; When branch is passed in addition to revision, .info file rewriting has better versioning. 15 | projects[caption_filter][subdir] = "contrib" 16 | projects[caption_filter][download][type] = "git" 17 | projects[caption_filter][download][branch] = "7.x-1.x" 18 | projects[caption_filter][download][revision] = "c9794cf" 19 | projects[caption_filter][options][working-copy] = TRUE 20 | -------------------------------------------------------------------------------- /tests/resources/create_node_types.php: -------------------------------------------------------------------------------- 1 | 'page', 6 | 'name' => 'Basic page', 7 | 'base' => 'node_content', 8 | 'description' => 'Use basic pages for your static content, such as an \'About us\' page.', 9 | 'custom' => 1, 10 | 'modified' => 1, 11 | 'locked' => 0, 12 | ), 13 | array( 14 | 'type' => 'article', 15 | 'name' => 'Article', 16 | 'base' => 'node_content', 17 | 'description' => 'Use articles for time-sensitive content like news, press releases or blog posts.', 18 | 'custom' => 1, 19 | 'modified' => 1, 20 | 'locked' => 0, 21 | ), 22 | ); 23 | 24 | foreach ($types as $type) { 25 | $type = node_type_set_defaults($type); 26 | node_type_save($type); 27 | node_add_body_field($type); 28 | } 29 | -------------------------------------------------------------------------------- /lib/Drush/UpdateService/StatusInfoInterface.php: -------------------------------------------------------------------------------- 1 | 1, 'b' => 2); 14 | * 15 | * Given 'message-template' == 'The first is !a and the second is !b', 16 | * output with --format=message: 17 | * 18 | * The first is 1 and the second is 2 19 | */ 20 | class drush_outputformat_message extends drush_outputformat { 21 | function format($data, $metadata) { 22 | $result = ''; 23 | if (isset($metadata['message-template'])) { 24 | foreach ($data as $key => $value) { 25 | $data_for_dt['!' . $key] = $value; 26 | } 27 | $result = dt($metadata['message-template'], $data_for_dt); 28 | } 29 | return $result; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/pmReleaseNotesTest.php: -------------------------------------------------------------------------------- 1 | drush('pm-releasenotes', array('drupal-7.1')); 15 | $output = $this->getOutput(); 16 | $expected = <<< EXPECTED 17 | ------------------------------------------------------------------------------ 18 | > RELEASE NOTES FOR 'DRUPAL' PROJECT, VERSION 7.1: 19 | > Last updated: May 25, 2011 - 20:21 . 20 | > Security 21 | ------------------------------------------------------------------------------ 22 | EXPECTED; 23 | $this->assertContains($expected, $output, 'Header is fine.'); 24 | $this->assertContains('SA-CORE-2011-001 - Drupal core - Multiple vulnerabilities', $output, 'Release notes includes SA reference.'); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /tests/makefiles/patches.make: -------------------------------------------------------------------------------- 1 | core = 7.x 2 | api = 2 3 | 4 | ; Test that patches work 5 | projects[wysiwyg][version] = "2.1" 6 | ; http://drupal.org/node/624018#comment-5098162 7 | projects[wysiwyg][patch][] = "http://drupal.org/files/0001-feature.inc-from-624018-211.patch" 8 | ; Test local filesystem patches 9 | projects[wysiwyg][patch][] = "patches-local-test-wysiwyg.patch" 10 | 11 | ; http://drupal.org/node/1152908#comment-5010536 12 | projects[features][version] = "1.0-beta4" 13 | projects[features][patch][] = "http://drupal.org/files/issues/features-drush-backend-invoke-25.patch" 14 | 15 | projects[context][version] = "3.0-beta2" 16 | ; http://drupal.org/node/1251406#comment-5020012 17 | projects[context][patch][] = "http://drupal.org/files/issues/custom_blocks_arent_editable-make.patch" 18 | ; http://drupal.org/node/661094#comment-4735064 19 | projects[context][patch][] = "http://drupal.org/files/issues/661094-context-permissions.patch" 20 | -------------------------------------------------------------------------------- /commands/core/druplicon.drush.inc: -------------------------------------------------------------------------------- 1 | rid => $this->name)); 8 | return array_keys($perms[$this->rid]); 9 | } 10 | 11 | public function getModulePerms($module) { 12 | $perms = module_invoke($module, 'permission'); 13 | return $perms ? array_keys($perms) : array(); 14 | } 15 | 16 | public function role_create($role_machine_name, $role_human_readable_name = '') { 17 | return user_role_save((object)array('name' => $role_machine_name)); 18 | } 19 | 20 | public function delete() { 21 | user_role_delete($this->rid); 22 | } 23 | 24 | public function grant_permissions($perms) { 25 | return drush_op('user_role_grant_permissions', $this->rid, $perms); 26 | } 27 | 28 | public function revoke_permissions($perms) { 29 | return drush_op('user_role_revoke_permissions', $this->rid, $perms); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/makefiles/gzip.make: -------------------------------------------------------------------------------- 1 | api = 2 2 | core = 6.x 3 | 4 | ; GeSHi-1.0.8.10.tar.gz contains wrapper folder "geshi/". 5 | ; This should move that wrapper folder to sites/all/libraries/geshi/ . 6 | libraries[geshi][destination] = libraries 7 | libraries[geshi][download][type] = get 8 | libraries[geshi][download][url] = http://downloads.sourceforge.net/project/geshi/geshi/GeSHi%201.0.8.10/GeSHi-1.0.8.10.tar.gz 9 | 10 | 11 | ; getid3 doesn't contain a wrapper folder. All files are in the root of the archive. 12 | libraries[getid3][destination] = libraries 13 | libraries[getid3][download][type] = get 14 | libraries[getid3][download][url] = "http://downloads.sourceforge.net/project/getid3/getID3%28%29%201.x/1.9.1/getid3-1.9.1-20110810.zip?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fgetid3%2Ffiles%2FgetID3%2528%2529%25201.x%2F1.9.1%2F&ts=1320871534" 15 | libraries[getid3][directory_name] = getid3 16 | ; http://drupal.org/node/1336886 17 | libraries[getid3][patch][] = http://drupal.org/files/getid3-remove-demos-1.9.1.patch 18 | -------------------------------------------------------------------------------- /lib/Drush/Queue/QueueBase.php: -------------------------------------------------------------------------------- 1 | getQueues()) as $name) { 20 | $q = $this->getQueue($name); 21 | $result[$name] = array( 22 | 'queue' => $name, 23 | 'items' => $q->numberOfItems(), 24 | 'class' => get_class($q), 25 | ); 26 | } 27 | return $result; 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public function getInfo($name) { 34 | $queues = $this->getQueues(); 35 | if (!isset($queues[$name])) { 36 | throw new QueueException(dt('Could not find the !name queue.', array('!name' => $name))); 37 | } 38 | return $queues[$name]; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /tests/makefiles/subtree.make: -------------------------------------------------------------------------------- 1 | core = 6.x 2 | api = 2 3 | 4 | ; nivo-slider2.7.1.zip contains Mac OS X metadata (a "__MACOSX/" folder) in addition to the desired content in the "nivo-slider/" folder. 5 | ; Using the "subtree" directive, we tell Drush Make we only want the "nivo-slider/" folder. 6 | libraries[nivo-slider][download][type] = get 7 | libraries[nivo-slider][download][url] = https://github.com/downloads/gilbitron/Nivo-Slider/nivo-slider2.7.1.zip 8 | libraries[nivo-slider][download][sha1] = bd8e14b82f5b9c6f533a4e1aa26a790cd66c3cb9 9 | libraries[nivo-slider][download][subtree] = nivo-slider 10 | 11 | ; Tell Drush Make we only want the "fullcalendar-1.5.3/fullcalendar/" folder. 12 | libraries[fullcalendar][download][type] = get 13 | libraries[fullcalendar][download][url] = https://github.com/arshaw/fullcalendar/releases/download/v1.5.3/fullcalendar-1.5.3.zip 14 | libraries[fullcalendar][download][sha1] = c7219b1ddd2b11ccdbf83ebd116872affbc45d7a 15 | libraries[fullcalendar][download][subtree] = fullcalendar-1.5.3/fullcalendar 16 | -------------------------------------------------------------------------------- /tests/batchTest.php: -------------------------------------------------------------------------------- 1 | setUpDrupal(1, TRUE); 16 | $options = array( 17 | 'root' => $this->webroot(), 18 | 'uri' => key($sites), 19 | 'yes' => NULL, 20 | 'include' => dirname(__FILE__), 21 | ); 22 | $this->drush('unit-batch', array(), $options); 23 | // Collect log messages that begin with "!!!" (@see: _drush_unit_batch_operation()) 24 | $parsed = $this->parse_backend_output($this->getOutput()); 25 | $special_log_msgs = ''; 26 | foreach ($parsed['log'] as $key => $log) { 27 | if(substr($log['message'],0,3) == '!!!') { 28 | $special_log_msgs .= $log['message']; 29 | } 30 | } 31 | $this->assertEquals("!!! ArrayObject does its job.", $special_log_msgs, 'Batch messages were logged'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /commands/runserver/d8-rs-router.php: -------------------------------------------------------------------------------- 1 | 1) { 26 | return $this->format_error("Multiple rows provided where only one is allowed."); 27 | } 28 | if (!empty($data)) { 29 | $data = reset($data); 30 | } 31 | if (is_array($data)) { 32 | return $this->format_error("Array provided where a string is required."); 33 | } 34 | } 35 | return (string)$data; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/Drush/Sql/SqlVersion.php: -------------------------------------------------------------------------------- 1 | =7 requires PDO and Drush requires php 5.4+ which ships with PDO 28 | // but it may be compiled with --disable-pdo. 29 | if (!class_exists('\PDO')) { 30 | drush_log(dt('PDO support is required.'), 'bootstrap'); 31 | return FALSE; 32 | } 33 | return TRUE; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /commands/core/cli.drush.inc: -------------------------------------------------------------------------------- 1 | 'Open an interactive shell on a Drupal site.', 9 | 'remote-tty' => TRUE, 10 | 'aliases' => array('php'), 11 | 'bootstrap' => DRUSH_BOOTSTRAP_MAX, 12 | ); 13 | return $items; 14 | } 15 | 16 | /** 17 | * Command callback. 18 | */ 19 | function drush_cli_core_cli() { 20 | $boris = new \Boris\Boris(drush_sitealias_bootstrapped_site_name() . '> '); 21 | // Use the Symfony var-dumper to inspect variables. 22 | $boris->setInspector(new \Drush\Boris\VarDumperInspector()); 23 | 24 | if (drush_drupal_major_version() >= 8) { 25 | // Set a local $container variable for Drupal 8. 26 | $boris->setLocal(['container' => \Drupal::getContainer()]); 27 | } 28 | 29 | // Boris will never return control to us, but our shutdown 30 | // handler will still run after the user presses ^D. Mark 31 | // this command as completed to avoid a spurious error message. 32 | drush_set_context('DRUSH_EXECUTION_COMPLETED', TRUE); 33 | $boris->start(); 34 | } 35 | -------------------------------------------------------------------------------- /lib/Drush/User/User8.php: -------------------------------------------------------------------------------- 1 | save(); 16 | return new UserSingle8($account); 17 | } 18 | 19 | /** 20 | * Attempt to load a user account. 21 | * 22 | * @param int $uid 23 | * @return \Drupal\user\Entity\User; 24 | */ 25 | public function load_by_uid($uid) { 26 | return User::load($uid); 27 | } 28 | 29 | /** 30 | * {inheritdoc} 31 | */ 32 | public function getCurrentUserAsAccount() { 33 | return \Drupal::currentUser()->getAccount(); 34 | } 35 | 36 | /** 37 | * Set the current user in Drupal. 38 | * 39 | * @param \Drupal\Core\Session\AccountInterface $account 40 | */ 41 | public function setCurrentUser($account) { 42 | // Some parts of Drupal still rely on a global user object. 43 | // @todo remove once https://www.drupal.org/node/2163205 is in. 44 | global $user; 45 | $user = $account; 46 | \Drupal::currentUser()->setAccount($account); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/Drush/Log/DrushLog.php: -------------------------------------------------------------------------------- 1 | assertFalse(ParserIni::supportedFile('-')); 17 | $this->assertFalse(ParserIni::supportedFile('/tmp/foo/bar/baz.make.yml')); 18 | $this->assertTrue(ParserIni::supportedFile('./baz/foo.make')); 19 | } 20 | 21 | /** 22 | * @dataProvider providerParse 23 | * @covers ::parse 24 | */ 25 | public function testParse($ini, $expected) { 26 | $parsed = ParserIni::parse($ini); 27 | $this->assertSame($expected, $parsed); 28 | } 29 | 30 | /** 31 | * Provides INI snippets to test the parser. 32 | */ 33 | public function providerParse() { 34 | $snippets[] = array('foo[bar][baz] = one', array('foo' => array('bar' => array('baz' => 'one')))); 35 | $snippets[] = array("; A comment should not be part of the returned array\nprojects[] = drupal", array('projects' => array('drupal'))); 36 | 37 | // @todo make more tests. 38 | return $snippets; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /tests/Unish/UnitUnishTestCase.php: -------------------------------------------------------------------------------- 1 | array("b" => 2, "c" => 3), 15 | * "d" => array("e" => 5, "f" => 6) 16 | * ); 17 | * 18 | * Output with --format=print-r: 19 | * 20 | * Array 21 | * ( 22 | * [a] => Array 23 | * ( 24 | * [b] => 2 25 | * [c] => 3 26 | * ) 27 | * 28 | * [d] => Array 29 | * ( 30 | * [e] => 5 31 | * [f] => 6 32 | * ) 33 | * ) 34 | */ 35 | class drush_outputformat_print_r extends drush_outputformat { 36 | function format($input, $metadata) { 37 | if (is_string($input)) { 38 | $output = '"' . $input . '"'; 39 | } 40 | elseif (is_array($input) || is_object($input)) { 41 | $output = print_r($input, TRUE); 42 | } 43 | else { 44 | $output = $input; 45 | } 46 | if (isset($metadata['label'])) { 47 | $output = $metadata['label'] . ': ' . $output; 48 | } 49 | return $output; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/backendUnitTest.php: -------------------------------------------------------------------------------- 1 | TRUE)); 23 | 24 | // Test file does not exist immediate after process forked 25 | $this->assertEquals(file_exists($test_file), FALSE); 26 | // Check every 100th of a second for up to 4 seconds to see if the file appeared 27 | $repetitions = 400; 28 | while (!file_exists($test_file) && ($repetitions > 0)) { 29 | usleep(10000); 30 | } 31 | // Assert that the file did finally appear 32 | $this->assertEquals(file_exists($test_file), TRUE); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/makefiles/git.make: -------------------------------------------------------------------------------- 1 | core = 6.x 2 | api = 2 3 | 4 | ; Test that a specific tag can be pulled. 5 | projects[tao][type] = theme 6 | projects[tao][download][type] = git 7 | projects[tao][download][tag] = 6.x-3.2 8 | 9 | ; Test that a branch can be pulled. We use a super-old "stale" branch in the 10 | ; Drupalbin project that we expect not to change. 11 | projects[drupalbin][type] = profile 12 | projects[drupalbin][download][type] = git 13 | projects[drupalbin][download][branch] = 5.x-1.x 14 | 15 | ; Test that a specific revision can be pulled. Note that provision is not 16 | ; actually a module. 17 | projects[visitor][type] = module 18 | projects[visitor][download][type] = git 19 | projects[visitor][download][revision] = 5f256032cd4bcc2db45c962306d12c85131388ef 20 | 21 | ; Test a non-Drupal.org repository. 22 | projects[os_lightbox][type] = "module" 23 | projects[os_lightbox][download][type] = "git" 24 | projects[os_lightbox][download][url] = "https://github.com/opensourcery/os_lightbox.git" 25 | projects[os_lightbox][download][revision] = "8d60090f2" 26 | 27 | ; Test a refspec fetch. 28 | projects[storypal][type] = module 29 | projects[storypal][download][type] = git 30 | projects[storypal][download][url] = http://git.drupal.org/project/storypal.git 31 | projects[storypal][download][refspec] = refs/tags/7.x-1.0 32 | -------------------------------------------------------------------------------- /commands/core/drupal/image.inc: -------------------------------------------------------------------------------- 1 | getStorage('image_style')->loadMultiple(); 13 | } 14 | 15 | function drush_image_style_load($style_name) { 16 | return \Drupal::entityManager()->getStorage('image_style')->load($style_name); 17 | } 18 | 19 | function drush_image_flush_single($style_name) { 20 | if ($style = drush_image_style_load($style_name)) { 21 | $style->flush(); 22 | drush_log(dt('Image style !style_name flushed', array('!style_name' => $style_name)), 'success'); 23 | } 24 | } 25 | 26 | /* 27 | * Command callback. Create an image derivative. 28 | * 29 | * @param string $style_name 30 | * The name of an image style. 31 | * 32 | * @param string $source 33 | * The path to a source image, relative to Drupal root. 34 | */ 35 | function _drush_image_derive($style_name, $source) { 36 | $image_style = drush_image_style_load($style_name); 37 | $derivative_uri = $image_style->buildUri($source); 38 | if ($image_style->createDerivative($source, $derivative_uri)) { 39 | return $derivative_uri; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /commands/core/drupal/image_7.inc: -------------------------------------------------------------------------------- 1 | $style_name)), 'success'); 23 | } 24 | } 25 | 26 | /* 27 | * Command callback. Create an image derivative. 28 | * 29 | * @param string $style_name 30 | * The name of an image style. 31 | * 32 | * @param string $source 33 | * The path to a source image, relative to Drupal root. 34 | */ 35 | function _drush_image_derive($style_name, $source) { 36 | $image_style = image_style_load($style_name); 37 | $scheme = file_default_scheme(); 38 | $image_uri = $scheme . '://' . $source; 39 | $derivative_uri = image_style_path($image_style['name'], $image_uri); 40 | if (image_style_create_derivative($image_style, $source, $derivative_uri)) { 41 | return $derivative_uri; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/siteSetTest.php: -------------------------------------------------------------------------------- 1 | is_windows()) { 12 | $this->markTestSkipped('Site-set not currently available on Windows.'); 13 | } 14 | $sites = $this->setUpDrupal(1, TRUE); 15 | $site_names = array_keys($sites); 16 | $alias = '@' . $site_names[0]; 17 | 18 | $this->drush('ev', array("drush_invoke('site-set', '$alias'); print drush_sitealias_site_get();")); 19 | $output = $this->getOutput(); 20 | $this->assertEquals("Site set to $alias\n$alias", $output); 21 | 22 | $this->drush('site-set', array()); 23 | $output = $this->getOutput(); 24 | $this->assertEquals('Site set to @none', $output); 25 | 26 | $this->drush('site-set', array($alias)); 27 | $expected = 'Site set to ' . $alias; 28 | $output = $this->getOutput(); 29 | $this->assertEquals($expected, $output); 30 | 31 | $this->drush('ev', array("drush_invoke('site-set', '@none'); drush_invoke('site-set', '$alias'); drush_invoke('site-set', '@none'); drush_invoke('site-set', '-'); print drush_sitealias_site_get();")); 32 | $output = $this->getOutput(); 33 | $this->assertEquals("Site set to @none 34 | Site set to $alias 35 | Site set to @none 36 | Site set to $alias 37 | $alias", $output); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/resources/example.profile: -------------------------------------------------------------------------------- 1 | 'Example', 6 | 'description' => 'Example profile with a couple of basic added configuration options.', 7 | ); 8 | } 9 | 10 | function example_profile_modules() { 11 | return array(); 12 | } 13 | 14 | function example_form_alter(&$form, $form_state, $form_id) { 15 | if ($form_id == 'install_configure') { 16 | $form['my_options'] = array( 17 | '#type' => 'fieldset', 18 | '#title' => t('Example options'), 19 | ); 20 | $form['my_options']['myopt1'] = array( 21 | '#type' => 'textfield', 22 | '#title' => 'Example option 1' 23 | ); 24 | $form['my_options']['myopt2'] = array( 25 | '#type' => 'select', 26 | '#title' => t('Example option 2'), 27 | '#options' => array( 28 | 0 => t('Something'), 29 | 1 => t('Something else'), 30 | 2 => t('Something completely different'), 31 | ), 32 | ); 33 | 34 | // Make sure we don't clobber the original auto-detected submit func 35 | $form['#submit'] = array('install_configure_form_submit', 'example_install_configure_form_submit'); 36 | } 37 | } 38 | 39 | function example_install_configure_form_submit($form, &$form_state) { 40 | variable_set('myopt1', $form_state['values']['myopt1']); 41 | variable_set('myopt2', $form_state['values']['myopt2']); 42 | } -------------------------------------------------------------------------------- /tests/completetest.drush.inc: -------------------------------------------------------------------------------- 1 | 'No-op command, used to test various completions for commands that start the same as other commands.', 22 | 'bootstrap' => DRUSH_BOOTSTRAP_NONE, 23 | 'callback' => 'drush_completetest_noop', 24 | 'command-hook' => 'completetest_noop', 25 | ); 26 | } 27 | $items['aaaaaaaard']['arguments'] = array('name' => 'Name'); 28 | $items['aaaaaaaard']['options'] = array( 29 | 'ears' => 'Ears', 30 | 'eyes' => 'Eyes', 31 | 'nose' => 'Nose', 32 | 'legs' => 'Legs', 33 | ); 34 | return $items; 35 | } 36 | 37 | function drush_completetest_noop() { 38 | // No-op. 39 | } 40 | 41 | /** 42 | * Command argument complete callback. 43 | * 44 | * @return 45 | * Array of completions. 46 | */ 47 | function completetest_completetest_noop_complete() { 48 | return array( 49 | 'values' => array( 50 | 'aardvark', 51 | 'aardwolf', 52 | 'zebra', 53 | ), 54 | ); 55 | } 56 | -------------------------------------------------------------------------------- /lib/Drush/Boot/EmptyBoot.php: -------------------------------------------------------------------------------- 1 | '_drush_bootstrap_drush', 25 | ); 26 | } 27 | 28 | function bootstrap_init_phases() { 29 | return array(DRUSH_BOOTSTRAP_DRUSH); 30 | } 31 | 32 | function command_defaults() { 33 | return array( 34 | // TODO: Historically, commands that do not explicitly specify 35 | // their bootstrap level default to DRUSH_BOOTSTRAP_DRUPAL_LOGIN. 36 | // This isn't right any more, but we can't just change this to 37 | // DRUSH_BOOTSTRAP_DRUSH, or we will start running commands that 38 | // needed a full bootstrap with no bootstrap, and that won't work. 39 | // For now, we will continue to force this to 'login'. Any command 40 | // that does not declare 'bootstrap' is declaring that it is a Drupal 41 | // command. 42 | 'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_LOGIN, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/Drush/Tests/Make/Parser/ParserYamlTest.php: -------------------------------------------------------------------------------- 1 | assertFalse(ParserYaml::supportedFile('-')); 18 | $this->assertTrue(ParserYaml::supportedFile('/tmp/foo/bar/baz.make.yml')); 19 | $this->assertFalse(ParserYaml::supportedFile('./baz/foo.make')); 20 | } 21 | 22 | /** 23 | * @dataProvider providerParse 24 | * @covers ::parse 25 | */ 26 | public function testParse($yaml, $expected) { 27 | $parsed = ParserYaml::parse($yaml); 28 | $this->assertSame($expected, $parsed); 29 | } 30 | 31 | /** 32 | * Provides YAML snippets to test the parser. 33 | */ 34 | public function providerParse() { 35 | $yaml = <<<'YAML' 36 | foo: 37 | bar: 38 | baz: one 39 | YAML; 40 | $snippets[] = array($yaml, array('foo' => array('bar' => array('baz' => 'one')))); 41 | 42 | $yaml = <<<'YAML' 43 | projects: 44 | drupal: ~ 45 | views: 46 | version: '3.0' 47 | YAML; 48 | 49 | $snippets[] = array($yaml, array('projects' => array('drupal' => NULL, 'views' => array('version' => '3.0')))); 50 | 51 | // @todo make more tests. 52 | return $snippets; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | Drush's test suite is based on [PHPUnit](http://www.phpunit.de). In order to maintain 2 | high quality, our tests are run on every push by [Travis](https://travis-ci.org/drush-ops/drush) 3 | 4 | Usage 5 | -------- 6 | 1. Review the configuration settings in tests/phpunit.xml.dist. If customization is needed, copy to phpunit.xml and edit away. 7 | 1. Run unit tests: `unish.sh` 8 | 9 | Advanced usage 10 | --------- 11 | - Run only tests matching a regex: `unish.sh --filter=testVersionString` 12 | - Skip slow tests (usually those with network usage): `unish.sh --exclude-group slow` 13 | - XML results: `unish.sh --filter=testVersionString --log-junit results.xml` 14 | - Use an alternate version of Drupal: `UNISH_DRUPAL_MAJOR_VERSION=8 unish.sh ...` 15 | - Skip teardown (to examine test sites after a failure): `UNISH_DIRTY=1 unish.sh ...` 16 | 17 | Reuse by Drush Commandfiles 18 | ----------- 19 | Drush commandfiles are encouraged to ship with PHPUnit test cases that 20 | extend UnitUnishTestCase and CommandUnishTestCase. In order to run 21 | the tests, you have to point to the phpunit.xml file that used by Drush. 22 | The devel project has a wrapper script which demonstrates this - 23 | http://drupalcode.org/project/devel.git/blob/refs/heads/8.x-1.x:/run-tests-drush.sh 24 | 25 | Cache 26 | ----------- 27 | In order to speed up test runs, Unish (the Drush testing class) caches built Drupal sites 28 | and restores them as requested by tests. Once in while, you might need to clear this cache 29 | by deleting the /drush-cache directory. 30 | -------------------------------------------------------------------------------- /examples/helloworld.script: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env drush 2 | 3 | // 4 | // This example demonstrates how to write a drush 5 | // "shebang" script. These scripts start with the 6 | // line "#!/usr/bin/env drush" or "#!/full/path/to/drush". 7 | // 8 | // See `drush topic docs-scripts` for more information. 9 | // 10 | drush_print("Hello world!"); 11 | drush_print(); 12 | drush_print("The arguments to this command were:"); 13 | 14 | // 15 | // If called with --everything, use drush_get_arguments 16 | // to print the commandline arguments. Note that this 17 | // call will include 'php-script' (the drush command) 18 | // and the path to this script. 19 | // 20 | if (drush_get_option('everything')) { 21 | drush_print(" " . implode("\n ", drush_get_arguments())); 22 | } 23 | // 24 | // If --everything is not included, then use 25 | // drush_shift to pull off the arguments one at 26 | // a time. drush_shift only returns the user 27 | // commandline arguments, and does not include 28 | // the drush command or the path to this script. 29 | // 30 | else { 31 | while ($arg = drush_shift()) { 32 | drush_print(' ' . $arg); 33 | } 34 | } 35 | 36 | drush_print(); 37 | 38 | // 39 | // We can check which site was bootstrapped via 40 | // the '@self' alias, which is defined only if 41 | // there is a bootstrapped site. 42 | // 43 | $self_record = drush_sitealias_get_record('@self'); 44 | if (empty($self_record)) { 45 | drush_print('No bootstrapped site.'); 46 | } 47 | else { 48 | drush_print('The following site is bootstrapped:'); 49 | _drush_sitealias_print_record($self_record); 50 | } 51 | -------------------------------------------------------------------------------- /tests/filesystemTest.php: -------------------------------------------------------------------------------- 1 | is_windows()) { 14 | $this->markTestSkipped("s-bit test doesn't apply on Windows."); 15 | } 16 | if (UNISH_USERGROUP === NULL) { 17 | $this->markTestSkipped("s-bit test skipped because of UNISH_USERGROUP was not set."); 18 | } 19 | 20 | $dest = UNISH_SANDBOX . '/test-filesystem-sbit'; 21 | mkdir($dest); 22 | chgrp($dest, UNISH_USERGROUP); 23 | chmod($dest, 02755); // rwxr-sr-x 24 | 25 | $this->drush('pm-download', array('devel'), array('cache' => NULL, 'skip' => NULL, 'destination' => $dest)); 26 | 27 | $group = posix_getgrgid(filegroup($dest . '/devel/README.txt')); 28 | $this->assertEquals($group['name'], UNISH_USERGROUP, 'Group is preserved.'); 29 | 30 | $perms = fileperms($dest . '/devel') & 02000; 31 | $this->assertEquals($perms, 02000, 's-bit is preserved.'); 32 | } 33 | 34 | public function testExecuteBits() { 35 | if ($this->is_windows()) { 36 | $this->markTestSkipped("execute bit test doesn't apply on Windows."); 37 | } 38 | 39 | $dest = UNISH_SANDBOX . '/test-filesystem-execute'; 40 | mkdir($dest); 41 | $this->execute(sprintf("git clone --depth=1 https://github.com/drush-ops/drush.git %s", $dest . '/drush')); 42 | 43 | $perms = fileperms($dest . '/drush/drush') & 0111; 44 | $this->assertEquals($perms, 0111, 'Execute permission is preserved.'); 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /lib/Drush/User/UserSingle7.php: -------------------------------------------------------------------------------- 1 | account->uid)); 9 | } 10 | 11 | public function unblock() { 12 | user_user_operations_unblock(array($this->account->uid)); 13 | } 14 | 15 | public function addRole($rid) { 16 | user_multiple_role_edit(array($this->account->uid), 'add_role', $rid); 17 | } 18 | 19 | public function removeRole($rid) { 20 | user_multiple_role_edit(array($this->account->uid), 'remove_role', $rid); 21 | } 22 | 23 | function info() { 24 | $userinfo = (array)$this->account; 25 | unset($userinfo['data']); 26 | unset($userinfo['block']); 27 | unset($userinfo['form_build_id']); 28 | foreach (array('created', 'access', 'login') as $key) { 29 | $userinfo['user_' . $key] = format_date($userinfo[$key]); 30 | } 31 | $userinfo['user_status'] = $userinfo['status'] ? 'active' : 'blocked'; 32 | return $userinfo; 33 | } 34 | 35 | public function passResetUrl($path = '') { 36 | $options = array(); 37 | if ($path) { 38 | $options['query']['destination'] = $path; 39 | } 40 | // D6,D7 append a /login. Otherwise identical to D8+. 41 | return drush_url(user_pass_reset_url($this->account) . '/login', $options); 42 | } 43 | 44 | function password($pass) { 45 | user_save($this->account, array('pass' => $pass)); 46 | } 47 | 48 | public function getUsername() { 49 | return $this->account->name; 50 | } 51 | 52 | public function id() { 53 | return $this->account->uid; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /docs/strict-options.md: -------------------------------------------------------------------------------- 1 | Strict Option Handling 2 | ====================== 3 | 4 | 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. 5 | 6 | One example of this is the core-rsync command: 7 | 8 | drush --simulate core-rsync -v @site1 @site2 9 | 10 | 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. 11 | 12 | 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. 13 | 14 | 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. 15 | 16 | -------------------------------------------------------------------------------- /lib/Drush/Sql/Sql6.php: -------------------------------------------------------------------------------- 1 | $type)), 'bootstrap'); 33 | return FALSE; 34 | } 35 | } 36 | else { 37 | drush_log(dt('!type database type is unsupported.', array('!type' => $type)), 'bootstrap'); 38 | return FALSE; 39 | } 40 | return TRUE; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /tests/siteAliasUnitTest.php: -------------------------------------------------------------------------------- 1 | 'fake.remote-host.com', 21 | 'remote-user' => 'www-admin', 22 | 'root' => '/fake/path/to/root', 23 | 'uri' => 'default', 24 | 'command-specific' => array( 25 | 'rsync' => array( 26 | 'delete' => TRUE, 27 | ), 28 | ), 29 | ); 30 | // Site alias which overrides some settings from $site_alias_a. 31 | $site_alias_b = array( 32 | 'remote-host' => 'another-fake.remote-host.com', 33 | 'remote-user' => 'www-other', 34 | 'root' => '/fake/path/to/root', 35 | 'uri' => 'default', 36 | 'command-specific' => array( 37 | 'rsync' => array( 38 | 'delete' => FALSE, 39 | ), 40 | ), 41 | ); 42 | // Expected result from merging $site_alias_a and $site_alias_b. 43 | $site_alias_expected = array( 44 | 'remote-host' => 'another-fake.remote-host.com', 45 | 'remote-user' => 'www-other', 46 | 'root' => '/fake/path/to/root', 47 | 'uri' => 'default', 48 | 'command-specific' => array( 49 | 'rsync' => array( 50 | 'delete' => FALSE, 51 | ), 52 | ), 53 | ); 54 | 55 | $site_alias_result = _sitealias_array_merge($site_alias_a, $site_alias_b); 56 | $this->assertEquals($site_alias_expected, $site_alias_result); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/output-formats.md: -------------------------------------------------------------------------------- 1 | Drush Output Formats 2 | ==================== 3 | 4 | Many Drush commands produce output that may be rendered in a variety of different ways using a pluggable formatting system. Drush commands that support output formats will show a --format option in their help text. The available formats are also listed in the help text, along with the default value for the format option. The list of formats shown is abbreviated; to see the complete list of available formats, run the help command with the --verbose option. 5 | 6 | The --pipe option is a quick, consistent way to get machine readable output from a command, in whatever way the command author thought was helpful. The --pipe option is equivalent to using --format=`` The pipe format will be shown in the options section of the command help, under the --pipe option. For historic reasons, --pipe also hides all log messages. 7 | 8 | To best understand how the various Drush output formatters work, it is best to first look at the output of the command using the 'var\_export' format. This will show the result of the command using the exact structure that was built by the command, without any reformatting. This is the standard format for the Drush command. Different formatters will take this information and present it in different ways. 9 | 10 | Global Options 11 | -------------- 12 | 13 | - --list-separator: Specify how elements in a list should be separated. In lists of lists, this applies to the elements in the inner lists. 14 | - --line-separator: In nested lists of lists, specify how the outer lists ("lines") should be separated. 15 | 16 | Output Formats 17 | -------------- 18 | 19 | A list of available formats, and their affect on the output of certain Drush commands, is shown below. 20 | 21 | -------------------------------------------------------------------------------- /lib/Drush/User/UserVersion.php: -------------------------------------------------------------------------------- 1 | getCurrentUserAsAccount()); 66 | } 67 | 68 | /** 69 | * Set the current "global" user account in Drupal. 70 | 71 | * @param 72 | * A user object. 73 | */ 74 | public function setCurrentUser($account) { 75 | global $user; 76 | $user = $account; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /commands/core/shellalias.drush.inc: -------------------------------------------------------------------------------- 1 | array_keys($all)); 24 | } 25 | } 26 | 27 | function shellalias_drush_command() { 28 | $items = array(); 29 | 30 | $items['shell-alias'] = array( 31 | 'description' => 'Print all known shell alias records.', 32 | 'bootstrap' => DRUSH_BOOTSTRAP_NONE, 33 | 'arguments' => array( 34 | 'alias' => 'Shell alias to print', 35 | ), 36 | 'outputformat' => array( 37 | 'default' => 'key-value', 38 | 'pipe-format' => 'json', 39 | 'simplify-single' => TRUE, 40 | 'output-data-type' => 'format-list', 41 | ), 42 | 'aliases' => array('sha'), 43 | 'examples' => array( 44 | 'drush shell-alias' => 'List all alias records known to drush.', 45 | 'drush shell-alias pull' => 'Print the value of the shell alias \'pull\'.', 46 | ), 47 | ); 48 | return $items; 49 | } 50 | 51 | /** 52 | * Print out the specified shell aliases. 53 | */ 54 | function drush_core_shell_alias($alias = FALSE) { 55 | $shell_aliases = drush_get_context('shell-aliases', array()); 56 | if (!$alias) { 57 | return $shell_aliases; 58 | } 59 | elseif (isset($shell_aliases[$alias])) { 60 | return array($alias => $shell_aliases[$alias]); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /commands/core/outputformat/variables.inc: -------------------------------------------------------------------------------- 1 | array("b" => 2, "c" => 3), 18 | * "d" => array("e" => 5, "f" => 6) 19 | * ); 20 | * 21 | * Output with --format=variables: 22 | * 23 | * $a['b'] = 2; 24 | * $a['c'] = 3; 25 | * $d['e'] = 5; 26 | * $d['f'] = 6; 27 | */ 28 | class drush_outputformat_variables extends drush_outputformat { 29 | function validate() { 30 | $metadata = $this->engine_config; 31 | $this->sub_engine = drush_load_engine('outputformat', 'var_export', $metadata); 32 | if (!is_object($this->sub_engine)) { 33 | return FALSE; 34 | } 35 | return TRUE; 36 | } 37 | 38 | function format($data, $metadata) { 39 | $output = ''; 40 | if (is_array($data)) { 41 | foreach ($data as $variable_name => $section) { 42 | foreach ($section as $label => $value) { 43 | $metameta = array( 44 | 'variable-name' => $variable_name, 45 | 'label' => $label, 46 | ); 47 | $formatted_item = $this->sub_engine->process($value, $metameta); 48 | if ($formatted_item === FALSE) { 49 | return FALSE; 50 | } 51 | $output .= $formatted_item; 52 | $output .= "\n"; 53 | } 54 | } 55 | } 56 | return $output; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests/phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | . 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | . 39 | 40 | 41 | -------------------------------------------------------------------------------- /tests/roleTest.php: -------------------------------------------------------------------------------- 1 | setUpDrupal(1, TRUE, UNISH_DRUPAL_MAJOR_VERSION, UNISH_DRUPAL_MAJOR_VERSION == 6 ? 'default' : 'standard'); 22 | $root = $this->webroot(); 23 | $name = "example"; 24 | $options = array( 25 | 'root' => $root, 26 | 'uri' => key($sites), 27 | 'yes' => NULL, 28 | ); 29 | $anonymous = 'anonymous'; 30 | $authenticated = 'authenticated'; 31 | if (UNISH_DRUPAL_MAJOR_VERSION < 8) { 32 | $anonymous .= ' user'; 33 | $authenticated .= ' user'; 34 | } 35 | $this->drush('role-list', array($anonymous), $options + array('pipe' => NULL) ); 36 | $output = $this->getOutput(); 37 | $this->assertContains('access content', $output); 38 | $this->drush('role-list', array($authenticated), $options + array('pipe' => NULL) ); 39 | $output = $this->getOutput(); 40 | $this->assertContains('access content', $output); 41 | $this->drush('role-add-perm', array($anonymous, 'administer nodes'), $options ); 42 | $this->drush('role-list', array($anonymous), $options + array('pipe' => NULL) ); 43 | $output = $this->getOutput(); 44 | $this->assertContains('administer nodes', $output); 45 | $this->drush('role-remove-perm', array($anonymous, 'administer nodes'), $options ); 46 | $this->drush('role-list', array($anonymous), $options + array('pipe' => NULL) ); 47 | $output = $this->getOutput(); 48 | $this->assertNotContains('administer nodes', $output); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tests/queueTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped("Queue API not available in Drupal 6."); 13 | } 14 | 15 | if (UNISH_DRUPAL_MAJOR_VERSION == 7) { 16 | $expected = 'aggregator_feeds,%items,SystemQueue'; 17 | } 18 | else { 19 | $expected = 'aggregator_feeds,%items,Drupal\Core\Queue\DatabaseQueue'; 20 | } 21 | 22 | $sites = $this->setUpDrupal(1, TRUE); 23 | $options = array( 24 | 'yes' => NULL, 25 | 'root' => $this->webroot(), 26 | 'uri' => key($sites), 27 | ); 28 | 29 | // Enable aggregator since it declares a queue. 30 | $this->drush('pm-enable', array('aggregator'), $options); 31 | 32 | $this->drush('queue-list', array(), $options); 33 | $output = $this->getOutput(); 34 | $this->assertContains('aggregator_feeds', $output, 'Queue list shows the declared queue.'); 35 | 36 | $this->drush('php-script', array('queue_script-D' . UNISH_DRUPAL_MAJOR_VERSION), $options + array('script-path' => dirname(__FILE__) . '/resources')); 37 | $this->drush('queue-list', array(), $options + array('pipe' => TRUE)); 38 | $output = trim($this->getOutput()); 39 | $parts = explode(",", $output); 40 | $this->assertEquals(str_replace('%items', 1, $expected), $output, 'Item was successfully added to the queue.'); 41 | $output = $this->getOutput(); 42 | 43 | $this->drush('queue-run', array('aggregator_feeds'), $options); 44 | $this->drush('queue-list', array(), $options + array('pipe' => TRUE)); 45 | $output = trim($this->getOutput()); 46 | $parts = explode(",", $output); 47 | $this->assertEquals(str_replace('%items', 0, $expected), $output, 'Queue item processed.'); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /commands/core/outputformat/table.inc: -------------------------------------------------------------------------------- 1 | array("b" => 2, "c" => 3), 24 | * "d" => array("b" => 5, "c" => 6) 25 | * ); 26 | * 27 | * Output with --format=table: 28 | * 29 | * b c 30 | * 2 3 31 | * 5 6 32 | */ 33 | class drush_outputformat_table extends drush_outputformat { 34 | function format($input, $metadata) { 35 | $field_list = isset($metadata['field-labels']) ? $metadata['field-labels'] : array(); 36 | $widths = array(); 37 | $col = 0; 38 | foreach($field_list as $key => $label) { 39 | if (isset($metadata['column-widths'][$key])) { 40 | $widths[$col] = $metadata['column-widths'][$key]; 41 | } 42 | ++$col; 43 | } 44 | $rows = drush_rows_of_key_value_to_array_table($input, $field_list, $metadata); 45 | $field_labels = array_key_exists('include-field-labels', $metadata) && $metadata['include-field-labels']; 46 | if (!$field_labels) { 47 | array_shift($rows); 48 | } 49 | return drush_format_table($rows, $field_labels, $widths); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tests/imageTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped("Image styles not available in Drupal 6 core."); 15 | } 16 | 17 | $sites = $this->setUpDrupal(1, TRUE, UNISH_DRUPAL_MAJOR_VERSION, 'standard'); 18 | $options = array( 19 | 'yes' => NULL, 20 | 'root' => $this->webroot(), 21 | 'uri' => key($sites), 22 | ); 23 | $logo = UNISH_DRUPAL_MAJOR_VERSION >= 8 ? 'core/themes/bartik/screenshot.png' : 'themes/bartik/screenshot.png'; 24 | $styles_dir = $options['root'] . '/sites/' . key($sites) . '/files/styles/'; 25 | $thumbnail = $styles_dir . 'thumbnail/public/' . $logo; 26 | $medium = $styles_dir . 'medium/public/' . $logo; 27 | 28 | // Test that "drush image-derive" works. 29 | $style_name = 'thumbnail'; 30 | $this->drush('image-derive', array($style_name, $logo), $options); 31 | $this->assertFileExists($thumbnail); 32 | 33 | // Test that "drush image-flush thumbnail" deletes derivatives created by the thumbnail image style. 34 | $this->drush('image-flush', array($style_name), $options); 35 | $this->assertFileNotExists($thumbnail); 36 | 37 | // Check that "drush image-flush --all" deletes all image styles by creating two different ones and testing its 38 | // existance afterwards. 39 | $this->drush('image-derive', array('thumbnail', $logo), $options); 40 | $this->assertFileExists($thumbnail); 41 | $this->drush('image-derive', array('medium', $logo), $options); 42 | $this->assertFileExists($medium); 43 | $this->drush('image-flush', array(), array('all' => TRUE) + $options); 44 | $this->assertFileNotExists($thumbnail); 45 | $this->assertFileNotExists($medium); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/Drush/Command/Commandfiles.php: -------------------------------------------------------------------------------- 1 | cache = array(); 22 | $this->deferred = array(); 23 | } 24 | 25 | function get() { 26 | return $this->cache; 27 | } 28 | 29 | function deferred() { 30 | return $this->deferred; 31 | } 32 | 33 | function sort() { 34 | ksort($this->cache); 35 | } 36 | 37 | function add($commandfile) { 38 | $load_command = FALSE; 39 | 40 | $module = basename($commandfile); 41 | $module = preg_replace('/\.*drush[0-9]*\.inc/', '', $module); 42 | $module_versionless = preg_replace('/\.d([0-9]+)$/', '', $module); 43 | if (!isset($this->cache[$module_versionless])) { 44 | $drupal_version = ''; 45 | if (preg_match('/\.d([0-9]+)$/', $module, $matches)) { 46 | $drupal_version = $matches[1]; 47 | } 48 | if (empty($drupal_version)) { 49 | $load_command = TRUE; 50 | } 51 | else { 52 | if (function_exists('drush_drupal_major_version') && ($drupal_version == drush_drupal_major_version())) { 53 | $load_command = TRUE; 54 | } 55 | else { 56 | // Signal that we should try again on 57 | // the next bootstrap phase. 58 | $this->deferred[$module] = $commandfile; 59 | } 60 | } 61 | if ($load_command) { 62 | $this->cache[$module_versionless] = $commandfile; 63 | require_once $commandfile; 64 | unset($this->deferred[$module]); 65 | } 66 | } 67 | return $load_command; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /commands/core/outputformat/var_export.inc: -------------------------------------------------------------------------------- 1 | array("b" => 2, "c" => 3), 19 | * "d" => array("e" => 5, "f" => 6) 20 | * ); 21 | * 22 | * Output with --format=var_export: 23 | * 24 | * array ( 25 | * 'a' => 26 | * array ( 27 | * 'b' => 2, 28 | * 'c' => 3, 29 | * ), 30 | * 'd' => 31 | * array ( 32 | * 'e' => 5, 33 | * 'f' => 6, 34 | * ), 35 | * ) 36 | * 37 | * Output with --format=config: (list of export) 38 | * 39 | * $config['a'] = array ( 40 | * 'b' => 2, 41 | * 'c' => 3, 42 | * ); 43 | * $config['d'] = array ( 44 | * 'e' => 5, 45 | * 'f' => 6, 46 | * ); 47 | */ 48 | class drush_outputformat_var_export extends drush_outputformat { 49 | function format($input, $metadata) { 50 | if (isset($metadata['label'])) { 51 | $variable_name = isset($metadata['variable-name']) ? $metadata['variable-name'] : 'variables'; 52 | $variable_name = preg_replace("/[^a-zA-Z0-9_-]/", "", str_replace(' ', '_', $variable_name)); 53 | $label = $metadata['label']; 54 | $label_template = (isset($metadata['label-template'])) ? $metadata['label-template'] : '$!variable["!label"] = !value;'; 55 | $output = dt($label_template, array('!variable' => $variable_name, '!label' => $label, '!value' => var_export($input, TRUE))); 56 | } 57 | else { 58 | $output = drush_var_export($input); 59 | } 60 | return $output; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /docs/usage.md: -------------------------------------------------------------------------------- 1 | Usage 2 | ----------- 3 | 4 | Drush can be run in your shell by typing "drush" from within any Drupal root directory. 5 | 6 | $ drush [options] [argument1] [argument2] 7 | 8 | Use the 'help' command to get a list of available options and commands: 9 | 10 | $ drush help 11 | 12 | For even more documentation, use the 'topic' command: 13 | 14 | $ drush topic 15 | 16 | Options 17 | ----------- 18 | 19 | For multisite installations, use the --uri option to target a particular site. If 20 | you are outside the Drupal web root, you might need to use the --root, --uri or other 21 | command line options just for Drush to work. If you do not specify a URI, Drush falls 22 | back to the default site configuration, Drupal's $GLOBALS['base_url'] will be set to http://default. This may cause some 23 | functionality to not work as expected. 24 | 25 | $ drush --uri http://example.com pm-updatecode 26 | 27 | If you wish to be able to select your Drupal site implicitly from the 28 | current working directory without using the -l option, but you need your 29 | base_url to be set correctly, you may force it by setting the uri in 30 | a drushrc.php file located in the same directory as your settings.php file. *This is broken - see https://github.com/drush-ops/drush/issues/800* 31 | 32 | ``` 33 | $options['uri'] = "http://example.com"; 34 | ``` 35 | 36 | Site Aliases 37 | ------------ 38 | 39 | Drush lets you run commands on a remote server, or even on a set of remote 40 | servers. Once defined, aliases can be references with the @ nomenclature, i.e. 41 | 42 | ```bash 43 | # Synchronize staging files to production 44 | $ drush rsync @staging:%files/ @live:%files 45 | # Syncronize database from production to dev, excluding the cache table 46 | $ drush sql-sync --structure-tables-key=custom @live @dev 47 | ``` 48 | 49 | See [example.aliases.drushrc.php](https://raw.githubusercontent.com/drush-ops/drush/master/examples/example.aliases.drushrc.php) for more information. 50 | 51 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "drush/drush", 3 | "description": "Drush is a command line shell and scripting interface for Drupal, a veritable Swiss Army knife designed to make life easier for those of us who spend some of our working hours hacking away at the command prompt.", 4 | "homepage": "http://www.drush.org", 5 | "license": "GPL-2.0+", 6 | "minimum-stability": "stable", 7 | "prefer-stable": true, 8 | "authors": [ 9 | { "name": "Moshe Weitzman", "email": "weitzman@tejasa.com" }, 10 | { "name": "Owen Barton", "email": "drupal@owenbarton.com" }, 11 | { "name": "Mark Sonnabaum", "email": "marksonnabaum@gmail.com" }, 12 | { "name": "Antoine Beaupré", "email": "anarcat@koumbit.org" }, 13 | { "name": "Greg Anderson", "email": "greg.1.anderson@greenknowe.org" }, 14 | { "name": "Jonathan Araña Cruz", "email": "jonhattan@faita.net" }, 15 | { "name": "Jonathan Hedstrom", "email": "jhedstrom@gmail.com" }, 16 | { "name": "Christopher Gervais", "email": "chris@ergonlogic.com" } 17 | ], 18 | "support": { 19 | "forum": "http://drupal.stackexchange.com/questions/tagged/drush", 20 | "irc": "irc://irc.freenode.org/drush" 21 | }, 22 | "bin": [ 23 | "drush", 24 | "drush.php", 25 | "drush.bat", 26 | "drush.complete.sh" 27 | ], 28 | "require": { 29 | "php": ">=5.4.5", 30 | "d11wtq/boris": "~1.0", 31 | "symfony/yaml": "~2.2", 32 | "symfony/var-dumper": "^2.6.3", 33 | "pear/console_table": "~1.2.0" 34 | }, 35 | "require-dev": { 36 | "phpunit/phpunit": ">=3.5", 37 | "symfony/process": "2.4.5" 38 | }, 39 | "suggest": { 40 | "drush/config-extra": "Provides configuration workflow commands, such as config-merge." 41 | }, 42 | "autoload": { 43 | "psr-0": { 44 | "Drush": "lib/" 45 | } 46 | }, 47 | "autoload-dev": { 48 | "psr-0": { 49 | "Unish": "tests/" 50 | } 51 | }, 52 | "extra": { 53 | "branch-alias": { 54 | "dev-master": "8.0.x-dev" 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /lib/Drush/Queue/Queue7.php: -------------------------------------------------------------------------------- 1 | $queue) { 20 | static::$queues[$name]['worker callback'] = $queue['cron']['callback']; 21 | if (isset($queue['cron']['time'])) { 22 | static::$queues[$name]['time'] = $queue['cron']['time']; 23 | } 24 | } 25 | } 26 | return static::$queues; 27 | } 28 | 29 | /** 30 | * {@inheritdoc} 31 | * 32 | * @return \DrupalQueueInterface 33 | */ 34 | public function getQueue($name) { 35 | return DrupalQueue::get($name); 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | public function run($name, $time_limit = 0) { 42 | $info = $this->getInfo($name); 43 | $function = $info['worker callback']; 44 | $end = time() + $time_limit; 45 | $queue = $this->getQueue($name); 46 | $count = 0; 47 | 48 | while ((!$time_limit || time() < $end) && ($item = $queue->claimItem())) { 49 | try { 50 | drush_log(dt('Processing item @id from @name queue.', array('@name' => $name, 'id' => $item->item_id)), 'info'); 51 | $function($item->data); 52 | $queue->deleteItem($item); 53 | $count++; 54 | } 55 | catch (\Exception $e) { 56 | // In case of exception log it and leave the item in the queue 57 | // to be processed again later. 58 | drush_set_error('DRUSH_QUEUE_EXCEPTION', $e->getMessage()); 59 | } 60 | } 61 | 62 | return $count; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /lib/Drush/Role/Role6.php: -------------------------------------------------------------------------------- 1 | perms)) { 10 | $perms = db_result(db_query("SELECT perm FROM {permission} pm LEFT JOIN {role} r ON r.rid = pm.rid WHERE r.rid = '%d'", $this->rid)); 11 | $role_perms = explode(", ", $perms); 12 | $this->perms = array_filter($role_perms); 13 | } 14 | return $this->perms; 15 | } 16 | 17 | public function getModulePerms($module) { 18 | return module_invoke($module, 'perm'); 19 | } 20 | 21 | public function role_create($role_machine_name, $role_human_readable_name = '') { 22 | $this->_admin_user_role_op($role_machine_name, t('Add role')); 23 | return TRUE; 24 | } 25 | 26 | public function delete() { 27 | $this->_admin_user_role_op($this->rid, t('Delete role')); 28 | } 29 | 30 | function _admin_user_role_op($role_machine_name, $op) { 31 | // c.f. http://drupal.org/node/283261 32 | require_once(drupal_get_path('module', 'user') . "/user.admin.inc"); 33 | 34 | $form_id = "user_admin_new_role"; 35 | $form_values = array(); 36 | $form_values["name"] = $role_machine_name; 37 | $form_values["op"] = $op; 38 | $form_state = array(); 39 | $form_state["values"] = $form_values; 40 | 41 | drupal_execute($form_id, $form_state); 42 | } 43 | 44 | public function grant_permissions($perms_to_add) { 45 | $perms = $this->getPerms(); 46 | $this->perms = array_unique(array_merge($this->perms, $perms_to_add)); 47 | $this->updatePerms(); 48 | } 49 | 50 | public function revoke_permissions($perms_to_remove) { 51 | $perms = $this->getPerms(); 52 | $this->perms = array_diff($this->perms, $perms_to_remove); 53 | $this->updatePerms(); 54 | } 55 | 56 | function updatePerms() { 57 | $new_perms = implode(", ", $this->perms); 58 | drush_op('db_query', "UPDATE {permission} SET perm = '%s' WHERE rid= %d", $new_perms, $this->rid); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /drush.complete.sh: -------------------------------------------------------------------------------- 1 | # BASH completion script for Drush. 2 | # 3 | # Place this in your /etc/bash_completion.d/ directory or source it from your 4 | # ~/.bash_completion or ~/.bash_profile files. Alternatively, source 5 | # examples/example.bashrc instead, as it will automatically find and source 6 | # this file. 7 | # 8 | # If you're using ZSH instead of BASH, add the following to your ~/.zshrc file 9 | # and source it. 10 | # 11 | # autoload bashcompinit 12 | # bashcompinit 13 | # source /path/to/your/drush.complete.sh 14 | 15 | # Ensure drush is available. 16 | which drush > /dev/null || alias drush &> /dev/null || return 17 | 18 | __drush_ps1() { 19 | f="${TMPDIR:-/tmp/}/drush-env-${USER}/drush-drupal-site-$$" 20 | if [ -f $f ] 21 | then 22 | __DRUPAL_SITE=$(cat "$f") 23 | else 24 | __DRUPAL_SITE="$DRUPAL_SITE" 25 | fi 26 | 27 | # Set DRUSH_PS1_SHOWCOLORHINTS to a non-empty value and define a 28 | # __drush_ps1_colorize_alias() function for color hints in your Drush PS1 29 | # prompt. See example.prompt.sh for an example implementation. 30 | if [ -n "${__DRUPAL_SITE-}" ] && [ -n "${DRUSH_PS1_SHOWCOLORHINTS-}" ]; then 31 | __drush_ps1_colorize_alias 32 | fi 33 | 34 | [[ -n "$__DRUPAL_SITE" ]] && printf "${1:- (%s)}" "$__DRUPAL_SITE" 35 | } 36 | 37 | # Completion function, uses the "drush complete" command to retrieve 38 | # completions for a specific command line COMP_WORDS. 39 | _drush_completion() { 40 | # Set IFS to newline (locally), since we only use newline separators, and 41 | # need to retain spaces (or not) after completions. 42 | local IFS=$'\n' 43 | # The '< /dev/null' is a work around for a bug in php libedit stdin handling. 44 | # Note that libedit in place of libreadline in some distributions. See: 45 | # https://bugs.launchpad.net/ubuntu/+source/php5/+bug/322214 46 | COMPREPLY=( $(drush --early=includes/complete.inc "${COMP_WORDS[@]}" < /dev/null 2> /dev/null) ) 47 | } 48 | 49 | # Register our completion function. We include common short aliases for Drush. 50 | complete -o bashdefault -o default -o nospace -F _drush_completion d dr drush drush5 drush6 drush7 drush.php 51 | -------------------------------------------------------------------------------- /lib/Drush/Role/Role8.php: -------------------------------------------------------------------------------- 1 | $role_machine_name, 21 | 'label' => $role_human_readable_name, 22 | ), 'user_role'); 23 | $role->save(); 24 | return $role; 25 | } 26 | 27 | public function getPerms() { 28 | $role = entity_load('user_role', $this->rid); 29 | $perms = $role->getPermissions(); 30 | // $perms = user_role_permissions(array($this->rid => $this->name)); 31 | return $perms; 32 | } 33 | 34 | public function getAllModulePerms() { 35 | $perms = \Drupal::service('user.permissions')->getPermissions(); 36 | return array_keys($perms); 37 | } 38 | 39 | public function getModulePerms($module) { 40 | $module_perms = array(); 41 | $perms = \Drupal::service('user.permissions')->getPermissions(); 42 | foreach ($perms as $name => $perm) { 43 | if ($perm['provider'] == $module) { 44 | $module_perms[] = $name; 45 | } 46 | } 47 | return $module_perms; 48 | } 49 | 50 | public function delete() { 51 | $role = entity_load('user_role', $this->rid); 52 | $role->delete(); 53 | } 54 | 55 | public function grant_permissions($perms) { 56 | return drush_op('user_role_grant_permissions', $this->rid, $perms); 57 | } 58 | 59 | public function revoke_permissions($perms) { 60 | return drush_op('user_role_revoke_permissions', $this->rid, $perms); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tests/expandWildcardTablesUnitTest.php: -------------------------------------------------------------------------------- 1 | assertEquals($db_tables, $expanded_db_tables); 48 | } 49 | 50 | /** 51 | * Tests drush_sql_filter_tables(). 52 | * 53 | * @see drush_sql_filter_tables(). 54 | */ 55 | public function testFilterTables() { 56 | // Array of tables to search for. 57 | $wildcard_input = array( 58 | 'cache', 59 | 'cache_*', 60 | ); 61 | // Mock array of tables to test with. 62 | $db_tables = array( 63 | 'cache', 64 | 'cache_bootstrap', 65 | 'cache_field', 66 | 'cache_filter', 67 | 'cache_form', 68 | 'cache_menu', 69 | 'cache_page', 70 | 'cache_path', 71 | 'cache_update', 72 | ); 73 | $expected_result = array( 74 | 'cache', 75 | ); 76 | 77 | $actual_result = drush_sql_filter_tables($wildcard_input, $db_tables); 78 | $this->assertEquals($expected_result, $actual_result); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /commands/core/browse.drush.inc: -------------------------------------------------------------------------------- 1 | 'Display a link to a given path or open link in a browser.', 10 | 'bootstrap' => DRUSH_BOOTSTRAP_NONE, 11 | 'handle-remote-commands' => TRUE, 12 | 'arguments' => array( 13 | 'path' => 'Path to open. If omitted, the site front page will be opened.', 14 | ), 15 | 'options' => array( 16 | 'browser' => 'Specify a particular browser (defaults to operating system default). Set to 0 to suppress opening a browser.', 17 | 'redirect-port' => 'The port that the web server is redirected to (e.g. when running within a Vagrant environment)', 18 | ), 19 | 'examples' => array( 20 | 'drush browse' => 'Open default web browser (if configured or detected) to the site front page.', 21 | 'drush browse node/1' => 'Open web browser to the path node/1.', 22 | 'drush @example.prod' => 'Open a browser to the web site specified in a site alias.', 23 | 'drush browse --browser=firefox admin' => 'Open Firefox web browser to the path \'admin\'.', 24 | ), 25 | ); 26 | return $items; 27 | } 28 | 29 | /** 30 | * Command callback. 31 | */ 32 | function drush_browse($path = '') { 33 | // Redispatch if called against a remote-host so a browser is started on the 34 | // the *local* machine. 35 | $alias = drush_get_context('DRUSH_TARGET_SITE_ALIAS'); 36 | if (drush_sitealias_is_remote_site($alias)) { 37 | $site_record = drush_sitealias_get_record($alias); 38 | $return = drush_invoke_process($site_record, 'browse', func_get_args(), drush_redispatch_get_options(), array('integrate' => TRUE)); 39 | if ($return['error_status']) { 40 | return drush_set_error('Unable to execute browse command on remote alias.'); 41 | } 42 | else { 43 | $link = $return['object']; 44 | } 45 | } 46 | else { 47 | if (!drush_bootstrap(DRUSH_BOOTSTRAP_DRUPAL_FULL)) { 48 | // Fail gracefully if unable to bootstrap Drupal. drush_bootstrap() has 49 | // already logged an error. 50 | return FALSE; 51 | } 52 | $link = drush_url($path, array('absolute' => TRUE)); 53 | } 54 | 55 | drush_start_browser($link); 56 | return $link; 57 | } 58 | -------------------------------------------------------------------------------- /docs/shellaliases.md: -------------------------------------------------------------------------------- 1 | Drush Shell Aliases 2 | =================== 3 | 4 | A Drush shell alias is a shortcut to any Drush command or any shell command. Drush shell aliases are very similar to [git aliases](https://git.wiki.kernel.org/index.php/Aliases\#Advanced). 5 | 6 | A shell alias is defined in a Drush configuration file called drushrc.php. See `drush topic docs-configuration`. There are two kinds of shell aliases: an alias whose value begins with a '!' will execute the rest of the line as bash commands. Aliases that do not start with a '!' will be interpreted as Drush commands. 7 | 8 | ### Example: 9 | 10 | $options['shell-aliases']['pull'] = '!git pull'; 11 | $options['shell-aliases']['noncore'] = 'pm-list --no-core'; 12 | 13 | With the above two aliases defined, `drush pull` will then be equivalent to `git pull`, and `drush noncore` will be equivalent to `drush pm-list --no-core`. 14 | 15 | Shell Alias Replacements 16 | ------------------------ 17 | 18 | Shell aliases are even more powerful when combined with shell alias replacements and site aliases. Shell alias replacements take the form of {{sitealias-item}} or {{%pathalias-item}}, and also the special {{@target}}, which is replaced with the name of the site alias used, or '@none' if none was used. 19 | 20 | For example, given the following site alias: 21 | 22 | $aliases['dev'] = array ( 23 | 'root' => '/path/to/drupal', 24 | 'uri' => 'mysite.org', 25 | '#live' => '@acme.live', 26 | ); 27 | 28 | The alias below can be used for all your projects to fetch the database and files from the client's live site via `drush @dev pull-data`. Note that these aliases assume that the alias used defines an item named '\#live' (as shown in the above alias). 29 | 30 | ### Shell aliases using replacements: 31 | 32 | `$options['shell-aliases']['pull-data'] = '!drush sql-sync {{#live}} {{@target}} && drush rsync {{#live}}:%files {{@target}}:%files';` 33 | 34 | If the user does not use these shell aliases with any site alias, then an error will be returned and the script will not run. These aliases with replacements can be used to quickly run combinations of drush sql-sync and rsync commands on the "standard" source or target site, reducing the risk of typos that might send information in the wrong direction or to the wrong site. 35 | 36 | -------------------------------------------------------------------------------- /lib/Drush/Queue/Queue8.php: -------------------------------------------------------------------------------- 1 | workerManager = $manager ?: \Drupal::service('plugin.manager.queue_worker'); 20 | } 21 | 22 | /** 23 | * {@inheritdoc} 24 | */ 25 | public function getQueues() { 26 | if (!isset(static::$queues)) { 27 | static::$queues = array(); 28 | foreach ($this->workerManager->getDefinitions() as $name => $info) { 29 | static::$queues[$name] = $info; 30 | } 31 | } 32 | return static::$queues; 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | * 38 | * @return \Drupal\Core\Queue\QueueInterface 39 | */ 40 | public function getQueue($name) { 41 | return \Drupal::queue($name); 42 | } 43 | 44 | /** 45 | * {@inheritdoc} 46 | */ 47 | public function run($name, $time_limit = 0) { 48 | $worker = $this->workerManager->createInstance($name); 49 | $end = time() + $time_limit; 50 | $queue = $this->getQueue($name); 51 | $count = 0; 52 | 53 | while ((!$time_limit || time() < $end) && ($item = $queue->claimItem())) { 54 | try { 55 | drush_log(dt('Processing item @id from @name queue.', array('@name' => $name, 'id' => $item->item_id)), 'info'); 56 | $worker->processItem($item->data); 57 | $queue->deleteItem($item); 58 | $count++; 59 | } 60 | catch (SuspendQueueException $e) { 61 | // If the worker indicates there is a problem with the whole queue, 62 | // release the item and skip to the next queue. 63 | $queue->releaseItem($item); 64 | drush_set_error('DRUSH_SUSPEND_QUEUE_EXCEPTION', $e->getMessage()); 65 | } 66 | catch (\Exception $e) { 67 | // In case of any other kind of exception, log it and leave the item 68 | // in the queue to be processed again later. 69 | drush_set_error('DRUSH_QUEUE_EXCEPTION', $e->getMessage()); 70 | } 71 | } 72 | 73 | return $count; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /tests/watchdogTest.php: -------------------------------------------------------------------------------- 1 | setUpDrupal(1, TRUE); 12 | $options = array( 13 | 'yes' => NULL, 14 | 'root' => $this->webroot(), 15 | 'uri' => key($sites), 16 | ); 17 | 18 | if (UNISH_DRUPAL_MAJOR_VERSION >= 7) { 19 | $this->drush('pm-enable', array('dblog'), $options); 20 | } 21 | if (UNISH_DRUPAL_MAJOR_VERSION >= 8) { 22 | $eval1 = "\\Drupal::logger('drush')->notice('Unish rocks.');"; 23 | } 24 | else { 25 | $eval1 = "watchdog('drush', 'Unish rocks.');"; 26 | } 27 | $this->drush('php-eval', array($eval1), $options); 28 | $this->drush('watchdog-show', array(), $options + array('count' => 50)); 29 | $output = $this->getOutput(); 30 | $this->assertContains('Unish rocks.', $output); 31 | 32 | // Add a new entry with a long message with the letter 'd' and verify that watchdog-show does 33 | // not print it completely in the listing unless --full is given. 34 | // As the output is formatted so lines may be splitted, assertContains does not work 35 | // in this scenario. Therefore, we will count the number of times a character is present. 36 | $message_chars = 300; 37 | $char = '*'; 38 | $message = str_repeat($char, $message_chars); 39 | if (UNISH_DRUPAL_MAJOR_VERSION >= 8) { 40 | $eval2 = "\\Drupal::logger('drush')->notice('$message');"; 41 | } 42 | else { 43 | $eval2 = "watchdog('drush', '$message');"; 44 | } 45 | $this->drush('php-eval', array($eval2), $options); 46 | $this->drush('watchdog-show', array(), $options); 47 | $output = $this->getOutput(); 48 | $this->assertGreaterThan(substr_count($output, $char), $message_chars); 49 | $this->drush('watchdog-show', array(), $options + array('extended' => NULL)); 50 | $output = $this->getOutput(); 51 | $this->assertGreaterThanOrEqual($message_chars, substr_count($output, $char)); 52 | 53 | // Tests message deletion 54 | $this->drush('watchdog-delete', array('all'), $options); 55 | $output = $this->getOutput(); 56 | $this->drush('watchdog-show', array(), $options); 57 | $output = $this->getOutput(); 58 | $this->assertEmpty($output); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/git-bisect.example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Git bisect is a helpful way to discover which commit an error 5 | # occurred in. This example file gives simple instructions for 6 | # using git bisect with Drush to quickly find erroneous commits 7 | # in Drush commands or Drupal modules, presuming that you can 8 | # trigger the error condition via Drush (e.g. using `drush php-eval`). 9 | # 10 | # Follow these simple steps: 11 | # 12 | # $ git bisect start 13 | # $ git bisect bad # Tell git that the current commit does not work 14 | # $ git bisect good bcadd5a # Tell drush that the commithash 12345 worked fine 15 | # $ git bisect run mytestscript.sh 16 | # 17 | # 'git bisect run' will continue to call 'git bisect good' and 'git bisect bad', 18 | # based on whether the script's exit code was 0 or 1, respectively. 19 | # 20 | # Replace 'mytestscript.sh' in the example above with a custom script that you 21 | # write yourself. Use the example script at the end of this document as a 22 | # guide. Replace the example command with one that calls the Drush command 23 | # that you would like to test, and replace the 'grep' string with a value 24 | # that appears when the error exists in the commit, but does not appear when 25 | # commit is okay. 26 | # 27 | # If you are using Drush to test Drupal or an external Drush module, use: 28 | # 29 | # $ git bisect run drush mycommand --strict=2 30 | # 31 | # This presumes that there is one or more '[warning]' or '[error]' 32 | # messages emitted when there is a problem, and no warnings or errors 33 | # when the commit is okay. Omit '--strict=2' to ignore warnings, and 34 | # signal failure only when 'error' messages are emitted. 35 | # 36 | # If you need to test for an error condition explicitly, to find errors 37 | # that do not return any warning or error log messages on their own, you 38 | # can use the Drush php-eval command to force an error when `myfunction()` 39 | # returns FALSE. Replace 'myfunction()' with the name of an appropriate 40 | # function in your module that can be used to detect the error condition 41 | # you are looking for. 42 | # 43 | # $ git bisect run drush ev 'if(!myfunction()) { return drush_set_error("ERR"); }' 44 | # 45 | drush mycommand --myoption 2>&1 | grep -q 'string that indicates there was a problem' 46 | if [ $? == 0 ] ; then 47 | exit 1 48 | else 49 | exit 0 50 | fi 51 | -------------------------------------------------------------------------------- /lib/Drush/UpdateService/StatusInfoDrupal6.php: -------------------------------------------------------------------------------- 1 | update_check_disabled = $conf['update_advanced_check_disabled']; 21 | $conf['update_advanced_check_disabled'] = $check_disabled; 22 | } 23 | } 24 | 25 | /** 26 | * {@inheritdoc} 27 | */ 28 | function afterGetStatus(&$update_info, $projects, $check_disabled) { 29 | // Restore Drupal settings. 30 | if (!is_null($check_disabled)) { 31 | global $conf; 32 | $conf['update_advanced_check_disabled'] = $this->update_check_disabled; 33 | unset($this->update_check_disabled); 34 | } 35 | 36 | // update_advanced.module sets a different project type 37 | // for disabled projects. Here we normalize it. 38 | if ($check_disabled) { 39 | foreach ($update_info as $key => $project) { 40 | if (in_array($project['project_type'], array('disabled-module', 'disabled-theme'))) { 41 | $update_info[$key]['project_type'] = substr($project['project_type'], strpos($project['project_type'], '-') + 1); 42 | } 43 | } 44 | } 45 | } 46 | 47 | /** 48 | * Obtains release info for all installed projects via update.module. 49 | * 50 | * @see update_get_available(). 51 | * @see update_manual_status(). 52 | */ 53 | protected function getAvailableReleases() { 54 | // We force a refresh if the cache is not available. 55 | if (!cache_get('update_available_releases', 'cache_update')) { 56 | $this->refresh(); 57 | } 58 | 59 | $available = update_get_available(TRUE); 60 | 61 | // Force to invalidate some update_status caches that are only cleared 62 | // when visiting update status report page. 63 | if (function_exists('_update_cache_clear')) { 64 | _update_cache_clear('update_project_data'); 65 | _update_cache_clear('update_project_projects'); 66 | } 67 | 68 | return $available; 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /tests/releaseInfoTest.php: -------------------------------------------------------------------------------- 1 | getSpecificRelease('6.x-1.18'); 30 | $this->assertEquals('6.x-1.18', $release['version']); 31 | 32 | // Pick latest recommended+published with no further specification. 33 | // 6.x-2.2 is skipped because it is unpublished. 34 | // 6.x-2.2-rc1 is skipped because it is not a stable release. 35 | $release = $project_release_info->getRecommendedOrSupportedRelease(); 36 | $this->assertEquals('6.x-2.1', $release['version']); 37 | 38 | // Pick latest from a specific branch. 39 | $release = $project_release_info->getSpecificRelease('6.x-1'); 40 | $this->assertEquals('6.x-1.23', $release['version']); 41 | 42 | // Pick latest from a different branch. 43 | // 6.x-2.2 is skipped because it is unpublished. 44 | // 6.x-2.2-rc1 is skipped because it is not a stable release. 45 | $release = $project_release_info->getSpecificRelease('6.x-2'); 46 | $this->assertEquals('6.x-2.1', $release['version']); 47 | 48 | // Pick a -dev release. 49 | $release = $project_release_info->getSpecificRelease('6.x-1.x'); 50 | $this->assertEquals('6.x-1.x-dev', $release['version']); 51 | 52 | // Test UpdateServiceProject::getSpecificRelease(). 53 | // Test we get latest release in branch 1. 54 | $release = $project_release_info->getSpecificRelease('6.x-1'); 55 | $this->assertEquals('6.x-1.23', $release['version']); 56 | 57 | // Test UpdateServiceProject::getDevRelease(). 58 | $release = $project_release_info->getDevRelease(); 59 | $this->assertEquals('6.x-1.x-dev', $release['version']); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /commands/core/drupal/cache_8.inc: -------------------------------------------------------------------------------- 1 | get($cid); 14 | } 15 | 16 | /** 17 | * The default bin. 18 | * 19 | * @return string 20 | */ 21 | function _drush_cache_bin_default() { 22 | return 'default'; 23 | } 24 | 25 | function _drush_cache_command_set($cid, $data, $bin, $expire, $tags) { 26 | if (is_null($bin)) { 27 | $bin = _drush_cache_bin_default(); 28 | } 29 | 30 | // Convert the "expire" argument to a valid value for Drupal's cache_set(). 31 | if ($expire == 'CACHE_TEMPORARY') { 32 | $expire = Cache::TEMPORARY; 33 | } 34 | if (!isset($expire) || $expire == 'CACHE_PERMANENT') { 35 | $expire = Cache::PERMANENT; 36 | } 37 | 38 | return \Drupal::cache($bin)->set($cid, $data, $expire, $tags); 39 | } 40 | 41 | function _drush_cache_clear_types($include_bootstrapped_types) { 42 | $types = array( 43 | 'drush' => 'drush_cache_clear_drush', 44 | ); 45 | if ($include_bootstrapped_types) { 46 | $types += array( 47 | 'theme-registry' => 'drush_cache_clear_theme_registry', 48 | 'menu' => 'drush_cache_clear_menu', 49 | 'css-js' => 'drush_cache_clear_css_js', 50 | 'block' => 'drush_cache_clear_block', 51 | 'module-list' => 'drush_get_modules', 52 | 'theme-list' => 'drush_get_themes', 53 | 'render' => 'drush_cache_clear_render', 54 | ); 55 | } 56 | return $types; 57 | } 58 | 59 | function drush_cache_clear_theme_registry() { 60 | \Drupal::service('theme.registry')->reset(); 61 | } 62 | 63 | function drush_cache_clear_menu() { 64 | /** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */ 65 | $menu_link_manager = \Drupal::service('plugin.manager.menu.link'); 66 | return $menu_link_manager->rebuild(); 67 | } 68 | 69 | function drush_cache_clear_css_js() { 70 | _drupal_flush_css_js(); 71 | drupal_clear_css_cache(); 72 | drupal_clear_js_cache(); 73 | } 74 | 75 | /** 76 | * Clear the cache of the block output. 77 | */ 78 | function drush_cache_clear_block() { 79 | \Drupal::cache('block')->deleteAll(); 80 | } 81 | 82 | /** 83 | * Clears the render cache entries. 84 | */ 85 | function drush_cache_clear_render() { 86 | \Drupal::cache('render')->deleteAll(); 87 | } 88 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Drush is built by people like you! Please [join us](https://github.com/drush-ops/drush). 2 | 3 | ## Git and Pull requests 4 | * Contributions are submitted, reviewed, and accepted using Github pull requests. [Read this article](https://help.github.com/articles/using-pull-requests) for some details. We use the _Fork and Pull_ model, as described there. 5 | * Optionally, to help keep track of [your assigned issues](https://github.com/dashboard/issues/assigned), simply ask to be added to the contributor team. A maintainer can now assign any issue to you at your request. 6 | * The latest changes are in the `master` branch. 7 | * Make a new branch for every feature you're working on. 8 | * Try to make clean commits that are easily readable (including descriptive commit messages!) 9 | * Test before you push. Get familiar with Unish, our test suite. See the test-specific [README.md](tests/README.md) 10 | * Make small pull requests that are easy to review but make sure they do add value by themselves. 11 | * We maintain branches named 7.x, 6.x, etc. These are release branches. From these branches, we make new tags for patch and minor versions. 12 | 13 | ## Coding style 14 | * Do write comments. You don't have to comment every line, but if you come up with something thats a bit complex/weird, just leave a comment. Bear in mind that you will probably leave the project at some point and that other people will read your code. Undocumented huge amounts of code are nearly worthless! 15 | * We use [Drupal's coding standards](https://drupal.org/coding-standards). 16 | * Don't overengineer. Don't try to solve any possible problem in one step, but try to solve problems as easy as possible and improve the solution over time! 17 | * Do generalize sooner or later! (if an old solution, quickly hacked together, poses more problems than it solves today, refactor it!) 18 | * Keep it compatible. Do not introduce changes to the public API, or configurations too lightly. Don't make incompatible changes without good reasons! 19 | 20 | ## Documentation 21 | * The docs are in the [docs](docs) and [examples](examples) folders in the git repository, so people can easily find the suitable docs for the current git revision. You can read these from within Drush, with the `drush topic` command. 22 | * Documentation should be kept up-to-date. This means, whenever you add a new API method, add a new hook or change the database model, pack the relevant changes to the docs in the same pull request. 23 | -------------------------------------------------------------------------------- /tests/generateMakeTest.php: -------------------------------------------------------------------------------- 1 | setUpDrupal(1, TRUE); 15 | $major_version = UNISH_DRUPAL_MAJOR_VERSION . '.x'; 16 | 17 | $options = array( 18 | 'yes' => NULL, 19 | 'pipe' => NULL, 20 | 'root' => $this->webroot(), 21 | 'uri' => key($sites), 22 | 'cache' => NULL, 23 | 'strict' => 0, // Don't validate options 24 | ); 25 | $this->drush('pm-download', array('omega', 'devel'), $options); 26 | $this->drush('pm-enable', array('omega', 'devel'), $options); 27 | 28 | $makefile = UNISH_SANDBOX . '/dev.make'; 29 | 30 | // First generate a simple makefile with no version information 31 | $this->drush('generate-makefile', array($makefile), array('exclude-versions' => NULL) + $options); 32 | $expected = <<assertEquals($expected, $actual); 47 | 48 | // Download a module to a 'contrib' directory to test the subdir feature 49 | mkdir($this->webroot() + '/sites/all/modules/contrib'); 50 | $this->drush('pm-download', array('libraries'), array('destination' => 'sites/all/modules/contrib') + $options); 51 | $this->drush('pm-enable', array('libraries'), $options); 52 | $this->drush('generate-makefile', array($makefile), array('exclude-versions' => NULL) + $options); 53 | $expected = <<assertEquals($expected, $actual); 70 | 71 | // Generate a makefile with version numbers. 72 | $this->drush('generate-makefile', array($makefile), $options); 73 | $actual = file_get_contents($makefile); 74 | $this->assertContains('projects[devel][version] = "', $actual); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /commands/xh.drush.inc: -------------------------------------------------------------------------------- 1 | 'Enable profiling via XHProf', 16 | ); 17 | $command['sub-options']['xh']['xh-link'] = 'URL to your XHProf report site.'; 18 | $command['sub-options']['xh']['xh-path'] = 'Absolute path to the xhprof project.'; 19 | } 20 | } 21 | } 22 | 23 | function xh_enabled() { 24 | if (drush_get_option('xh')) { 25 | if (!extension_loaded('xhprof')) { 26 | return drush_set_error('You must enable the xhprof PHP extension in your CLI PHP in order to profile.'); 27 | } 28 | if (!drush_get_option('xh-path', '')) { 29 | return drush_set_error('You must provide the xh-path option in your drushrc or on the CLI in order to profile.'); 30 | } 31 | return TRUE; 32 | } 33 | } 34 | 35 | /** 36 | * Implements hook_drush_init(). 37 | */ 38 | function xh_drush_init() { 39 | if (xh_enabled()) { 40 | if ($path = drush_get_option('xh-path', '')) { 41 | include_once $path . '/xhprof_lib/utils/xhprof_lib.php'; 42 | include_once $path . '/xhprof_lib/utils/xhprof_runs.php'; 43 | xhprof_enable(); // @todo support custom flags 44 | } 45 | } 46 | } 47 | 48 | /** 49 | * Implements hook_drush_exit(). 50 | */ 51 | function xh_drush_exit() { 52 | if (xh_enabled()) { 53 | $args = func_get_args(); 54 | $namespace = 'Drush'; // variable_get('site_name', ''); 55 | $xhprof_data = xhprof_disable(); 56 | $xhprof_runs = new XHProfRuns_Default(); 57 | $run_id = $xhprof_runs->save_run($xhprof_data, $namespace); 58 | if ($url = xh_link($run_id)) { 59 | drush_log(dt('XHProf run saved. View report at !url', array('!url' => $url)), 'ok'); 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * Returns the XHProf link. 66 | */ 67 | function xh_link($run_id) { 68 | if ($xhprof_url = trim(drush_get_option('xh-link'))) { 69 | $namespace = 'Drush'; //variable_get('site_name', ''); 70 | return $xhprof_url . '/index.php?run=' . urlencode($run_id) . '&source=' . urlencode($namespace); 71 | } 72 | else { 73 | drush_log('Configure xh_link in order to see a link to the XHProf report for this request instead of this message.'); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/Drush/Sql/Sqlsqlsrv.php: -------------------------------------------------------------------------------- 1 | db_spec['database']) ? 'master' : $this->db_spec['database']; 18 | // Host and port are optional but have defaults. 19 | $host = empty($this->db_spec['host']) ? '.\SQLEXPRESS' : $this->db_spec['host']; 20 | if ($this->db_spec['username'] == '') { 21 | return ' -S ' . $host . ' -d ' . $database; 22 | } 23 | else { 24 | return ' -S ' . $host . ' -d ' . $database . ' -U ' . $this->db_spec['username'] . ' -P ' . $this->db_spec['password']; 25 | } 26 | } 27 | 28 | public function db_exists() { 29 | // TODO: untested, but the gist is here. 30 | $database = $this->db_spec['database']; 31 | // Get a new class instance that has no 'database'. 32 | $db_spec_no_db = $this->db_spec; 33 | unset($db_spec_no_db['database']); 34 | $sql_no_db = drush_sql_get_class($db_spec_no_db); 35 | $query = "if db_id('$database') IS NOT NULL print 1"; 36 | drush_shell_exec($sql_no_db->connect() . ' -Q %s', $query); 37 | $output = drush_shell_exec_output(); 38 | return $output[0] == 1; 39 | } 40 | 41 | public function listTables() { 42 | $return = $this->query('SELECT TABLE_NAME FROM information_schema.tables'); 43 | $tables = drush_shell_exec_output(); 44 | if (!empty($tables)) { 45 | // Shift off the header of the column of data returned. 46 | array_shift($tables); 47 | return $tables; 48 | } 49 | } 50 | 51 | // @todo $file is no longer provided. We are supposed to return bash that can be piped to gzip. 52 | // Probably sqlsrv needs to override dump() entirely. 53 | public function dumpCmd($table_selection) { 54 | if (!$file) { 55 | $file = $this->db_spec['database'] . '_' . date('Ymd_His') . '.bak'; 56 | } 57 | $exec = "sqlcmd -U \"" . $this->db_spec['username'] . "\" -P \"" . $this->db_spec['password'] . "\" -S \"" . $this->db_spec['host'] . "\" -Q \"BACKUP DATABASE [" . $this->db_spec['database'] . "] TO DISK='" . $file . "'\""; 58 | return array($exec, $file); 59 | } 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /tests/siteIntallD6Test.php: -------------------------------------------------------------------------------- 1 | markTestSkipped('This test class is designed for Drupal 6.'); 15 | return; 16 | } 17 | } 18 | 19 | /** 20 | * Test a D6 install with extra options. 21 | */ 22 | public function testExtraConfigurationOptions() { 23 | // Set up codebase without installing Drupal. 24 | $sites = $this->setUpDrupal(1, FALSE, '6'); 25 | $root = $this->webroot(); 26 | $site = key($sites); 27 | 28 | // Copy the "example" test profile into the newly created site's profiles directory 29 | $profile_dir = "$root/profiles/example"; 30 | mkdir($profile_dir); 31 | copy(dirname(__FILE__) . '/resources/example.profile', $profile_dir . '/example.profile'); 32 | 33 | $test_string = $this->randomString(); 34 | // example.profile Has values 0-2 defined as allowed. 35 | $test_int = rand(0, 2); 36 | $site_name = $this->randomString(); 37 | 38 | $this->drush('site-install', array( 39 | // First argument is the profile name 40 | 'example', 41 | // Then the extra profile options 42 | "myopt1=$test_string", 43 | "myopt2=$test_int", 44 | ), 45 | array( 46 | 'db-url' => $this->db_url($site), 47 | 'yes' => NULL, 48 | 'sites-subdir' => $site, 49 | 'root' => $root, 50 | 'site-name' => $site_name, 51 | 'uri' => $site, 52 | )); 53 | 54 | $this->checkVariable('site_name', $site_name, $site); 55 | $this->checkVariable('myopt1', $test_string, $site); 56 | $this->checkVariable('myopt2', $test_int, $site); 57 | } 58 | 59 | /** 60 | * Check the value of a Drupal variable against an expectation using drush. 61 | * 62 | * @param $name 63 | * The variable name. 64 | * @param $value 65 | * The expected value of this variable. 66 | * @param $site 67 | * The name of an individual multisite installation site. 68 | */ 69 | private function checkVariable($name, $value, $site) { 70 | $options = array( 71 | 'root' => $this->webroot(), 72 | 'uri' => $site, 73 | ); 74 | 75 | $this->drush('variable-get', array($name), $options); 76 | $this->assertEquals("$name: $value", $this->getOutput()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /lib/Drush/Boot/DrupalBoot7.php: -------------------------------------------------------------------------------- 1 | bootstrap_drupal_database_has_table('blocked_ips'); 54 | } 55 | 56 | function bootstrap_drupal_database() { 57 | drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); 58 | parent::bootstrap_drupal_database(); 59 | } 60 | 61 | function bootstrap_drupal_configuration() { 62 | drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); 63 | 64 | // Unset drupal error handler and restore drush's one. 65 | restore_error_handler(); 66 | 67 | parent::bootstrap_drupal_configuration(); 68 | } 69 | 70 | function bootstrap_drupal_full() { 71 | if (!drush_get_context('DRUSH_QUIET', FALSE)) { 72 | ob_start(); 73 | } 74 | drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); 75 | if (!drush_get_context('DRUSH_QUIET', FALSE)) { 76 | ob_end_clean(); 77 | } 78 | 79 | parent::bootstrap_drupal_full(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /commands/core/outputformat/topics/table.html: -------------------------------------------------------------------------------- 1 | The 'table' formatter will convert an associative array into a formatted, 2 | word-wrapped table. Each item in the associative array represents one row 3 | in the table. Each row is similarly composed of associative arrays, with 4 | the key of each item indicating the column, and the value indicating the 5 | contents of the cell. See below for an example source array. 6 |

7 | The command core-requirements is an example of a command that produces output 8 | in a tabular format. 9 |

10 | $ drush core-requirements 11 |

12 |  Title                 Severity  Description
13 |  Cron maintenance      Error     Last run 2 weeks ago
14 |  tasks                           Cron has not run recently. For more
15 |                                  information, see the online handbook entry for
16 |                                  configuring cron jobs. You can run cron
17 |                                  manually.
18 |  Drupal                Info      7.19
19 | 
20 | (Note: the output above has been shortened for clarity; the actual output 21 | of core-requirements contains additional rows not shown here.) 22 |

23 | It is possible to determine the available fields by consulting drush 24 | help core requirements: 25 |

26 |  --fields=<title, severity, description>   Fields to output. All
27 |                                            available fields are:
28 |                                            title, severity, sid,
29 |                                            description, value,
30 |                                            reason, weight.
31 | 
32 | It is possible to control the fields that appear in the table, and their 33 | order, by naming the desired fields in the --fields option. The space 34 | between items is optional, so `--fields=title,sid` is valid. 35 |

36 | Code: 37 |

38 | return array (
39 |   'cron' =>
40 |   array (
41 |     'title' => 'Cron maintenance tasks',
42 |     'severity' => 2,
43 |     'value' => 'Last run 2 weeks ago',
44 |     'description' => 'Cron has not run recently. For more information, see the online handbook entry for configuring cron jobs. You can run cron manually.',
45 |     'severity-label' => 'Error',
46 |   ),
47 |   'drupal' =>
48 |   array (
49 |     'title' => 'Drupal',
50 |     'value' => '7.19',
51 |     'severity' => -1,
52 |     'weight' => -10,
53 |     'severity-label' => 'Info',
54 |   ),
55 | )
56 | 
57 | -------------------------------------------------------------------------------- /tests/quickDrupalTest.php: -------------------------------------------------------------------------------- 1 | makefile_path = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'makefiles'; 22 | } 23 | 24 | /** 25 | * Run a given quick-drupal test. 26 | * 27 | * @param $test 28 | * The test makefile to run, as defined by $this->getQuickDrupalTestParameters(); 29 | */ 30 | private function runQuickDrupalTest($test) { 31 | $config = $this->getQuickDrupalTestParameters($test); 32 | $default_options = array( 33 | 'yes' => NULL, 34 | 'no-server' => NULL, 35 | ); 36 | $options = array_merge($config['options'], $default_options); 37 | if (array_key_exists('makefile', $config)) { 38 | $makefile = $this->makefile_path . DIRECTORY_SEPARATOR . $config['makefile']; 39 | $options['makefile'] = $makefile; 40 | } 41 | $return = !empty($config['fail']) ? self::EXIT_ERROR : self::EXIT_SUCCESS; 42 | $target = UNISH_SANDBOX . '/qd-' . $test; 43 | $options['root'] = $target; 44 | $this->drush('core-quick-drupal', $config['args'], $options, NULL, NULL, $return); 45 | 46 | // Use pm-list to determine if all of the correct modules were enabled 47 | if (empty($config['fail'])) { 48 | $this->drush('pm-list', array(), array('root' => $target, 'status' => 'enabled', 'no-core' => NULL, 'pipe' => NULL)); 49 | $output = $this->getOutput(); 50 | $this->assertEquals($config['expected-modules'], $output, 'quick-drupal included the correct set of modules'); 51 | } 52 | } 53 | 54 | function testQuickDrupal() { 55 | $this->runQuickDrupalTest('devel'); 56 | } 57 | 58 | function getQuickDrupalTestParameters($key) { 59 | $tests = array( 60 | 'devel' => array( 61 | 'name' => 'Test quick-drupal with a makefile that downloads devel', 62 | 'makefile' => 'qd-devel.make', 63 | 'expected-modules' => 'devel', 64 | 'args' => array(), 65 | 'options' => array( 66 | 'skip' => NULL, // for speed up enable of devel module. 67 | 'browser' => 0, 68 | 'profile' => UNISH_DRUPAL_MAJOR_VERSION == 6 ? 'standard' : 'testing', 69 | ), 70 | ), 71 | ); 72 | return $tests[$key]; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/variableTest.php: -------------------------------------------------------------------------------- 1 | = 8) { 12 | $this->markTestSkipped("Variable system was removed in Drupal 8."); 13 | } 14 | 15 | $sites = $this->setUpDrupal(1, TRUE); 16 | $options_without_pipe = array( 17 | 'yes' => NULL, 18 | 'root' => $this->webroot(), 19 | 'uri' => key($sites), 20 | ); 21 | $options = $options_without_pipe + array( 22 | 'pipe' => NULL, 23 | ); 24 | 25 | $this->drush('variable-set', array('test_integer', '3.14159'), $options); 26 | $this->drush('variable-get', array('test_integer'), $options); 27 | $var_export = $this->getOutput(); 28 | eval($var_export); 29 | $this->assertEquals("3.14159", $variables['test_integer'], 'Integer variable was successfully set and get.'); 30 | 31 | $this->drush('variable-set', array('date_default_timezone', 'US/Mountain'), $options); 32 | $this->drush('variable-get', array('date_default_timezone'), $options); // Wildcard get. 33 | $var_export = $this->getOutput(); 34 | eval($var_export); 35 | $this->assertEquals('US/Mountain', $variables['date_default_timezone'], 'Variable was successfully set and get.'); 36 | 37 | $this->drush('variable-set', array('site_name', 'control'), $options + array('exact' => NULL)); 38 | $this->drush('variable-set', array('site_na', 'unish'), $options + array('always-set' => NULL)); 39 | $this->drush('variable-get', array('site_na'), $options + array('exact' => NULL)); 40 | $var_export = $this->getOutput(); 41 | eval($var_export); 42 | $this->assertEquals('unish', $variables['site_na'], '--always-set option works as expected.'); 43 | 44 | $this->drush('variable-set', array('site_n', 'exactish'), $options + array('exact' => NULL)); 45 | $this->drush('variable-get', array('site_n'), $options + array('exact' => NULL)); 46 | $var_export = $this->getOutput(); 47 | eval($var_export); 48 | $this->assertEquals('exactish', $variables['site_n'], '--exact option works as expected.'); 49 | 50 | $this->drush('variable-get', array('site_n'), $options_without_pipe + array('exact' => NULL)); 51 | $site_n_value = $this->getOutput(); 52 | $this->assertEquals('exactish', $site_n_value, '--exact option works as expected with --pipe.'); 53 | 54 | $this->drush('variable-delete', array('site_name'), $options); 55 | $output = $this->getOutput(); 56 | $this->assertEmpty($output, 'Variable was successfully deleted.'); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /commands/make/update.make.inc: -------------------------------------------------------------------------------- 1 | $project) { 26 | if (($project['download']['type'] == 'git') && !empty($project['download']['url'])) { 27 | // TODO check that tag or branch are valid version strings (with pm_parse_version()). 28 | if (!empty($project['download']['tag'])) { 29 | $version = $project['download']['tag']; 30 | } 31 | elseif (!empty($project['download']['branch'])) { 32 | $version = $project['download']['branch'] . '-dev'; 33 | } 34 | /* 35 | elseif (!empty($project['download']['refspec'])) { 36 | #TODO# Parse refspec. 37 | } 38 | */ 39 | else { 40 | // If no tag or branch, we can't match a d.o version. 41 | continue; 42 | } 43 | $projects[$project_name] = $project + array( 44 | 'path' => '', 45 | 'label' => $project_name, 46 | 'version' => $version, 47 | ); 48 | } 49 | elseif ($project['download']['type'] == 'pm') { 50 | $projects[$project_name] = $project + array( 51 | 'path' => '', 52 | 'label' => $project_name, 53 | ); 54 | } 55 | } 56 | 57 | // Check for updates. 58 | $update_status = drush_get_engine('update_status'); 59 | $update_info = $update_status->getStatus($projects, TRUE); 60 | 61 | $security_only = drush_get_option('security-only', FALSE); 62 | foreach ($update_info as $project_name => $project_update_info) { 63 | if (!$security_only || ($security_only && $project_update_info['status'] == DRUSH_UPDATESTATUS_NOT_SECURE)) { 64 | $make_projects[$project_name]['download']['full_version'] = $project_update_info['recommended']; 65 | } 66 | } 67 | 68 | // Inject back make projects and generate the updated makefile. 69 | drush_set_option('DRUSH_MAKE_PROJECTS', $make_projects); 70 | make_generate_from_makefile(drush_get_option('result-file'), $makefile); 71 | } 72 | 73 | -------------------------------------------------------------------------------- /docs/examples.md: -------------------------------------------------------------------------------- 1 | This folder contains example files which you are copy and edit as needed. Read the documentation right in the file. If you see an opportunity to improve the file, please submit a pull request. 2 | 3 | * [drush](https://raw.githubusercontent.com/drush-ops/drush/master/examples/drush). A handy launcher script which calls the Drush located in vendor/bin/drush and can add options like --local, --root, etc. 4 | * [example.make](https://raw.githubusercontent.com/drush-ops/drush/master/examples/example.make). An ini style make file. 5 | * [example.make.yml](https://raw.githubusercontent.com/drush-ops/drush/master/examples/example.make.yml). A YML make file. 6 | * [example.bashrc](https://raw.githubusercontent.com/drush-ops/drush/master/examples/example.bashrc). Enhance your shell with lots of Drush niceties including bash completion. 7 | * [example.prompt.sh](https://raw.githubusercontent.com/drush-ops/drush/master/examples/example.prompt.sh). Displays Git repository and Drush alias status in your prompt. 8 | * [example.drush.ini](https://raw.githubusercontent.com/drush-ops/drush/master/examples/example.drush.ini). Configure your PHP just for Drush requests. 9 | * [example.drushrc.php](https://raw.githubusercontent.com/drush-ops/drush/master/examples/example.drushrc.php). A Drush configuration file. 10 | * [example.aliases.drushrc.php](https://raw.githubusercontent.com/drush-ops/drush/master/examples/example.aliases.drushrc.php). Example site alias definitions. 11 | * [helloworld.script](https://raw.githubusercontent.com/drush-ops/drush/master/examples/helloworld.script). An example Drush script. 12 | * [git-bisect.example.sh](https://raw.githubusercontent.com/drush-ops/drush/master/examples/git-bisect.example.sh). Spelunking through Drush's git history with bisect. 13 | * [policy.drush.inc](https://raw.githubusercontent.com/drush-ops/drush/master/examples/policy.drush.inc). A policy file can disallow prohibited commands/options etc. 14 | * [sync_via_http.drush.inc](https://raw.githubusercontent.com/drush-ops/drush/master/examples/sync_via_http.drush.inc). sql-sync modification that transfers via http instead of rsync. 15 | * [sync_enable.drush.inc](https://raw.githubusercontent.com/drush-ops/drush/master/examples/sync_enable.drush.inc). Automatically enable modules after a sql-sync. 16 | * [pm_update.drush.inc](https://raw.githubusercontent.com/drush-ops/drush/master/examples/pm_update.drush.inc). Restore sqlsrv driver after core update. 17 | * [xkcd.drush.inc](https://raw.githubusercontent.com/drush-ops/drush/master/examples/xkcd.drush.inc). A fun example command that browses XKCD comics. 18 | * [sandwich.drush.inc](https://raw.githubusercontent.com/drush-ops/drush/master/examples/sandwich.drush.inc). A fun example command inspired by a famous XKCD comic. 19 | -------------------------------------------------------------------------------- /tests/sqlConnectCreateTest.php: -------------------------------------------------------------------------------- 1 | setUpDrupal(1, TRUE); 17 | $options = array( 18 | 'yes' => NULL, 19 | 'root' => $this->webroot(), 20 | 'uri' => key($sites), 21 | ); 22 | 23 | // Get the connection details with sql-connect and check its structure. 24 | $this->drush('sql-connect', array(), $options); 25 | $connectionString = $this->getOutput(); 26 | 27 | // Not all drivers need -e option like sqlite 28 | $shell_options = "-e"; 29 | $db_driver = $this->db_driver(); 30 | if ($db_driver == 'mysql') { 31 | $this->assertRegExp('/^mysql --user=[^\s]+ --password=.* --database=[^\s]+ --host=[^\s]+/', $connectionString); 32 | } 33 | elseif ($db_driver == 'sqlite') { 34 | $this->assertContains('sqlite3', $connectionString); 35 | $shell_options = ''; 36 | } 37 | elseif ($db_driver == 'pgsql') { 38 | $this->assertRegExp('/^psql -q --dbname=[^\s]+ --host=[^\s]+ --port=[^\s]+ --username=[^\s]+/', $connectionString); 39 | } 40 | else { 41 | $this->markTestSkipped('sql-connect test does not recognize database type in ' . UNISH_DB_URL); 42 | } 43 | 44 | // Issue a query and check the result to verify the connection. 45 | $this->execute($connectionString . ' ' . $shell_options . ' "SELECT uid FROM users where uid = 1;"'); 46 | $output = $this->getOutput(); 47 | $this->assertContains('1', $output); 48 | 49 | // Run 'core-status' and insure that we can bootstrap Drupal. 50 | $this->drush('core-status', array("Drupal bootstrap"), $options); 51 | $output = $this->getOutput(); 52 | $this->assertContains('Successful', $output); 53 | 54 | // Test to see if 'sql-create' can erase the database. 55 | // The only output is a confirmation string, so we'll run 56 | // other commands to confirm that this worked. 57 | $this->drush('sql-create', array(), $options); 58 | 59 | // Try to execute a query. This should give a "table not found" error. 60 | $this->execute($connectionString . ' ' . $shell_options . ' "SELECT uid FROM users where uid = 1;"', self::EXIT_ERROR); 61 | 62 | // We should still be able to run 'core-status' without getting an 63 | // error, although Drupal should not bootstrap any longer. 64 | $this->drush('core-status', array("Drupal bootstrap"), $options); 65 | $output = $this->getOutput(); 66 | $this->assertNotContains('Successful', $output); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tests/commandUnitTest.php: -------------------------------------------------------------------------------- 1 | drush_major_version(); 12 | $major_plus1 = $major + 1; 13 | 14 | // Write matched and unmatched files to the system search path. 15 | $files = array( 16 | $path . "/$major.drush$major.inc", 17 | $path . "/drush$major/drush$major.drush.inc", 18 | $path . "/$major_plus1.drush$major_plus1.inc", 19 | $path . "/drush$major_plus1/drush$major_plus1.drush.inc", 20 | ); 21 | mkdir($path); 22 | mkdir($path . '/drush' . $major); 23 | mkdir($path . '/drush' . $major_plus1); 24 | foreach ($files as $file) { 25 | $contents = <<assertTrue(in_array(realpath($files[0]), $loaded), 'Loaded a version-specific command file.'); 36 | $this->assertTrue(in_array(realpath($files[1]), $loaded), 'Loaded a version-specific command directory.'); 37 | $this->assertFalse(in_array(realpath($files[2]), $loaded), 'Did not load a mismatched version-specific command file.'); 38 | $this->assertFalse(in_array(realpath($files[3]), $loaded), 'Did not load a a mismatched version-specific command directory.'); 39 | } 40 | 41 | /** 42 | * Assert that $command has interesting properties. Reference command by 43 | * it's alias (dl) to assure that those aliases are built as expected. 44 | */ 45 | public function testGetCommands() { 46 | drush_preflight(); 47 | $commands = drush_get_commands(); 48 | $command = $commands['dl']; 49 | 50 | $this->assertEquals('dl', current($command['aliases'])); 51 | $this->assertArrayHasKey('version_control', $command['engines']); 52 | $this->assertArrayHasKey('package_handler', $command['engines']); 53 | $this->assertArrayHasKey('release_info', $command['engines']); 54 | $this->assertEquals('pm-download', $command['command']); 55 | $this->assertEquals('pm', $command['commandfile']); 56 | $this->assertEquals('drush_command', $command['callback']); 57 | $this->assertArrayHasKey('examples', $command['sections']); 58 | $this->assertTrue($command['is_alias']); 59 | 60 | $command = $commands['pm-download']; 61 | $this->assertArrayNotHasKey('is_alias', $command); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /examples/example.drush.ini: -------------------------------------------------------------------------------- 1 | ; 2 | ; Example of a drush php settings override file 3 | ; 4 | ; IMPORTANT: Before following the instructions in 5 | ; this file, first check to see that the cli version 6 | ; of php is installed on your system. (e.g. On 7 | ; debian systems, `sudo apt-get install php5-cli`.) 8 | ; 9 | ; Use this file in instances when your system is 10 | ; -not- configured to use separate php.ini files for 11 | ; webserver and cli use. You can determine which 12 | ; php.ini file drush is using by running "drush status". 13 | ; If the php.ini file shown is your webserver ini 14 | ; file, then rename this file, example.drush.ini, 15 | ; to drush.ini and copy it to one of the following 16 | ; locations: 17 | ; 18 | ; 1. Drush installation folder 19 | ; 2. User's .drush folder (i.e. ~/.drush/drush.ini) 20 | ; 3. System wide configuration folder (i.e. /etc/drush/drush.ini) 21 | ; 22 | ; If the environment variable DRUSH_INI is defined, 23 | ; then the file it specified will be used as drush.ini. 24 | ; 25 | ; export DRUSH_INI='/path/to/drush.ini' 26 | ; 27 | ; When in use, the variables defined in this file 28 | ; will override the setting values that appear in 29 | ; your php.ini file. See the examples below for 30 | ; some values that may need to be set in order for 31 | ; drush to work. 32 | ; 33 | ; NOTE: There is a certain amount of overhead 34 | ; required for each override, so drush.ini should 35 | ; only be used for a relatively small number 36 | ; of variables. Comment out any variable that 37 | ; has the same value as the webserver php.ini 38 | ; to keep the size of the override list small. 39 | ; 40 | ; To fully specify the value of all php.ini variables, 41 | ; copy your webserver php.ini file to one of the 42 | ; locations mentioned above (e.g. /etc/drush/php.ini) 43 | ; and edit it to suit. Alternately, you may use 44 | ; the environment variable PHP_INI to point at 45 | ; the file that Drush should use. 46 | ; 47 | ; export PHP_INI='/path/to/php.ini' 48 | ; 49 | ; The options listed below are particularly relevant 50 | ; to drush. 51 | ; 52 | 53 | ; 54 | ; drush needs as much memory as Drupal in order 55 | ; to run; make the memory limit setting match 56 | ; what you have in your webserver's php.ini. 57 | ; 58 | memory_limit = 128M 59 | 60 | ; 61 | ; Show all errors and direct them to stderr 62 | ; when running drush. 63 | ; 64 | error_reporting = E_ALL | E_NOTICE | E_STRICT 65 | display_errors = stderr 66 | 67 | ; 68 | ; If your php.ini for your webserver is too 69 | ; restrictive, you can re-enable functionality 70 | ; for drush by adjusting values in this file. 71 | ; 72 | ; Here are some examples of settings that are 73 | ; sometimes set to restrictive values in a 74 | ; webserver's php.ini: 75 | ; 76 | ;safe_mode = 77 | ;open_basedir = 78 | ;disable_functions = 79 | ;disable_classes = 80 | -------------------------------------------------------------------------------- /lib/Drush/Boot/DrupalBoot6.php: -------------------------------------------------------------------------------- 1 | bootstrap_drupal_database_has_table('cache'); 57 | } 58 | 59 | function bootstrap_drupal_database() { 60 | drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); 61 | parent::bootstrap_drupal_database(); 62 | } 63 | 64 | function bootstrap_drupal_configuration() { 65 | drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION); 66 | 67 | parent::bootstrap_drupal_configuration(); 68 | } 69 | 70 | function bootstrap_drupal_full() { 71 | if (!drush_get_context('DRUSH_QUIET', FALSE)) { 72 | ob_start(); 73 | } 74 | drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); 75 | if (!drush_get_context('DRUSH_QUIET', FALSE)) { 76 | ob_end_clean(); 77 | } 78 | 79 | // Unset drupal error handler and restore drush's one. 80 | restore_error_handler(); 81 | 82 | parent::bootstrap_drupal_full(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /tests/COVERAGE.txt: -------------------------------------------------------------------------------- 1 | COMMANDS 2 | ------------ 3 | pm-download: GOOD. 4 | need test for `pm-download --select` (hard to test; depends on state of project releases on d.o.) 5 | pm-updatecode: GOOD. 6 | pm-update: FAIR. Implicitly tested by pm-updatecode but updatedb not yet tested. 7 | pm-releasenotes 8 | pm-releases 9 | pm-enable: GOOD. testEnDisUnList(). 10 | pm-disable: GOOD. testEnDisUnList(). 11 | pm-uninstall: GOOD. testEnDisUnList(). 12 | pm-list: GOOD. testEnDisUnList(). 13 | pm-info: GOOD. testEnDisUnList(). 14 | pm-refresh: 15 | version-control: FAIR. See updatecode. To be deprecated all git workflow after git.drupal.org? 16 | package-hander: 17 | 18 | sql-cli: 19 | sql-connect: 20 | sql-query: FAIR. Implicit by site-install, sql-sync 21 | sql-dump: GOOD. tested by sqlDumpTest. 22 | sql-sync: GOOD. testLocalSqlSync(). 23 | 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. 24 | sql-drop: FAIR. Implicit by site-install 25 | sql-sanitize: FAIR. Implicit by testLocalSqlSync() 26 | 27 | 28 | updatedb: NONE. Used to be implicitly tested by siteUpgradeTest. 29 | archive-dump: GOOD 30 | archive-restore: GOOD. Has own test and implicitly tested by environment cache in Unish framework. 31 | help 32 | version: GOOD. Implicit by testStandaloneScript() 33 | php-eval: GOOD. Implicitly tested by many tests (e.g. completeTest). 34 | php-script: GOOD. 35 | drupal-directory: GOOD 36 | cache-get: GOOD 37 | cache-set: GOOD 38 | cache-clear: GOOD 39 | core-config: NONE 40 | core-cron 41 | core-status: FAIR: Implicit test by contextTest. 42 | docs 43 | core-rsync 44 | core-quick-drupal: GOOD 45 | image: GOOD 46 | queue-*: GOOD 47 | runserver 48 | search-* 49 | shellalias: GOOD 50 | need test: shell alias with site alias 51 | site-install: FAIR. Implicit test by setUpDrupal(). 52 | ssh: GOOD 53 | topic 54 | usage-* 55 | variable-*: GOOD 56 | watchdog-*: GOOD 57 | 58 | user-*: GOOD. 59 | 60 | field-*: GOOD. 61 | 62 | make: GOOD 63 | 64 | INCLUDES 65 | ------------ 66 | backend: GOOD 67 | need test: --pipe with remote alias and --pipe with list of aliases 68 | batch: GOOD 69 | bootstrap: 70 | command: FAIR 71 | complete: GOOD 72 | context: FAIR. Many functions implicitly tested. Option merging (config, include, alias-path) not tested. 73 | drush: NONE. 74 | environment 75 | sitealias. FAIR. Explicit test for alias lists. Single aliases implicitly tested by contextTest. Option propagation tested by backendTest. Option recovery for @self alias tested by sqlDumpTest. 76 | dbtng: Good. Implicit by variable-*. 77 | drupal 78 | exec: GOOD: Implicitly tested all over. 79 | filesystem 80 | output 81 | 82 | 83 | ROOT 84 | ------------- 85 | drush: 86 | need test: drush.ini 87 | drush.php 88 | drush.bat: N/A 89 | drush.complete.sh: N/A 90 | -------------------------------------------------------------------------------- /examples/sandwich-nocolor.txt: -------------------------------------------------------------------------------- 1 | ::::::::::::::::::::::::::::::::::MMMMMMMMMMMM::::::::::::::::::::::::::::::::: 2 | :::::::::::::::::::::::::MMMMMMMM:::::::::::MM::::::::::::::::::::::::::::::::: 3 | :::::::::::::::::::MMMMM::::::::::::::::::::MMMM::::::::::::::::::::::::::::::: 4 | ::::::::::::::MMMMM:::::::::M::::::::M::::::::::MMMM::::::::::::::::::::::::::: 5 | ::::::::::MMMM::M:::::::::::::::::::::::::::::::::::MMMM::::::::::::::::::::::: 6 | :::::::MMM::::::::::::::::::::::::::::::::::::::::::::::MMMM::::::::::::::::::: 7 | :::::MM:::::::::::::::::::::::::::::::::::::::::::::::::::::MMMM::::::::::::::: 8 | :::::M::::::::::::::::::::::::::::::::::::::::::::::::::::::::::MMM:::::::::::: 9 | :::::MMM$MM::::::::::::::::::::::::::::::::::::::::::::::::::::::::MMM::::::::: 10 | :::::M$$$$$MM:::::::::::::::::::::::::::::::::::::::::::::M::::::::::MMM::::::: 11 | ::MMMMM$M$$$$MM:::::::::M:::::::::::::::::::::::::::::::::::::::::::MMMMM:::::: 12 | ::MIIIMMMM$M$$$MM:::::::::::::::::::::::::::::::::::::::::::MMMMMMM$$$$M::::::: 13 | :::MIIIIIIMM$$$$$MM::::::::::::::M:::::::::::::::::::MMMMMMM$$$$$$$$$$$$::::::: 14 | :MMIIIIIIIIMM$$$$$$MMM::::::::::::::::::::::::MMMMMM$$$$$$$$$$$$$$$$$$$$M:::::: 15 | MIIMMMMMIIIIIMMM$$$$$$MM:::::::::::::::MMMMMM$$$$$$$$$$$$$$$$$$$$$$$$$$$M:::::: 16 | MMM:MM:MMMMIIIIMMM$$$$$$MM:::::::MMMMMMM$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$MMMMM:: 17 | ::M::::MM::IIIIIIMM$$$$$$$$MMMMM$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$MMMMMMMMMIIM::: 18 | ::MM:::M::MIIMMIIIIIM$M$$$$M$$$$$$$$$$$$$$$$$$$$$$$$$$$$MMMMMMMIIIIIMMMMMMM:::: 19 | ::::MM::MMMMM:MIIIIIIMM$M$$M$$$$$$$$$$$$$$$$$$$$$$MMMMMMMIIIIIIIIIIIM::MMM::::: 20 | ::::::$MM:::::MIIIMMIIIMM$M$$$$$$$$$$$$$$$$MMMMMMIMMMIIIIM:::MMMIIIIM:::::MM::: 21 | ::::::$$$MM:::::M::MIIIMMMM$M$$$$$$$MMMMMMIIIIIIMMM:MMMMM:::::::::::::MMMMMM::: 22 | ::::::M$$$$MM:MMM::MMM:::MIMMMMMMMMMMIIMMIIIIIIIMM+M:::::MMMM::::MMMMM$M::::::: 23 | ::::::MM$M$$$M+$$M::::::MIIIIIIIMIIIIM::::MIIIIIM:MM::::M$$M+MM$$$$$$$$:::::::: 24 | :::::::MMM$$$$$$$M:::::::MIIIIIM::MMM:::::::MMMM::::::::M$$M$$$$$$$$$$$M::::::: 25 | ::::::::::MM$$$$$$$MM::::MMMMMM:::::::::::::::::::MMMMMMM$$$$$$$$$$$$$$M::::::: 26 | ::::::::::::MM$M$$$$$MM:MM$$$$M::::::::::MMMMMMM$$$$$$$$$$$$$$$$$$MMMMM:::::::: 27 | ::::::::::::::MMM$M$$$$MM$$$$$M:MMMMMMM$$$$$$$$$$$$$$$$$$$$$MMMMMM::::::::::::: 28 | :::::::::::::::::MMM$M$M$$$$$$MMM$$$$$$$$$$$$$$$$$$$$$$MMMMM:M:M::::::::::::::: 29 | :::::::::::::::::::MMMM$$M$$$$$$M$$$$$$$$$$$$$$$$$MMMMMM::::::M:::::::::::::::: 30 | ::::::::::::::::::::::MMMMM$$$$$M$$$$$$$$$$$$$MMMMM:::::::::::::::::::::::::::: 31 | :::::::::::::::::::::::::MMMM$M$M$$$$$$$$$MMMMM:::::::::::::::::::::::::::::::: 32 | ::::::::::::::::::::::::::::MMMMM$$$$MMMM:::::::::::::::::::::::::::::::::::::: 33 | :::::::::::::::::::::::::::::::MMMMM::::::::::::::::::::::::::::::::::::::::::: 34 | ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 35 | -------------------------------------------------------------------------------- /tests/cacheCommandTest.php: -------------------------------------------------------------------------------- 1 | getSites()) { 14 | $this->setUpDrupal(1, TRUE); 15 | } 16 | } 17 | 18 | function testCacheGet() { 19 | $options = $this->getOptions(); 20 | // Test the cache get command. 21 | $inputs = array( 22 | 6 => array('variables', NULL), 23 | 7 => array('schema', NULL), 24 | 8 => array('system.filter', 'config'), 25 | ); 26 | list($key, $bin) = $inputs[UNISH_DRUPAL_MAJOR_VERSION]; 27 | $this->drush('cache-get', array($key, $bin), $options + array('format' => 'json')); 28 | $schema = $this->getOutputFromJSON('data'); 29 | $this->assertNotEmpty($schema); 30 | 31 | // Test that get-ing a non-existant cid fails. 32 | $this->drush('cache-get', array('test-failure-cid'), $options + array('format' => 'json'), NULL, NULL, self::EXIT_ERROR); 33 | } 34 | 35 | function testCacheSet() { 36 | $options = $this->getOptions(); 37 | // Test setting a new cache item. 38 | $expected = 'cache test string'; 39 | $this->drush('cache-set', array('cache-test-cid', $expected), $options); 40 | $this->drush('cache-get', array('cache-test-cid'), $options + array('format' => 'json')); 41 | $data = $this->getOutputFromJSON('data'); 42 | $this->assertEquals($expected, $data); 43 | 44 | // Test cache-set using all arguments and many options. 45 | $expected = array('key' => 'value'); 46 | $input = array('data'=> $expected); 47 | $stdin = json_encode($input); 48 | $bin = UNISH_DRUPAL_MAJOR_VERSION >= 8 ? 'default' : 'cache'; 49 | $exec = sprintf('echo %s | %s cache-set %s %s my_cache_id - %s CACHE_PERMANENT --format=json --cache-get 2>/dev/null', self::escapeshellarg($stdin), UNISH_DRUSH, "--root=" . self::escapeshellarg($options['root']), '--uri=' . $options['uri'], $bin); 50 | $return = $this->execute($exec); 51 | $this->drush('cache-get', array('my_cache_id'), $options + array('format' => 'json')); 52 | $data = $this->getOutputFromJSON('data'); 53 | $this->assertEquals((object)$expected, $data); 54 | } 55 | 56 | function testCacheRebuild() { 57 | $options = $this->getOptions(); 58 | // Test cache-clear all and cache-rebuild (D8+). 59 | if (UNISH_DRUPAL_MAJOR_VERSION >= 8) { 60 | $this->drush('cache-rebuild', array(), $options); 61 | } 62 | else { 63 | $this->drush('cache-clear', array('all'), $options); 64 | } 65 | $this->drush('cache-get', array('cache-test-cid'), $options + array('format' => 'json'), NULL, NULL, self::EXIT_ERROR); 66 | } 67 | 68 | function getOptions() { 69 | return array( 70 | 'yes' => NULL, 71 | 'root' => $this->webroot(), 72 | 'uri' => key($this->getSites()), 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /commands/core/drupal/cache.inc: -------------------------------------------------------------------------------- 1 | 'drush_cache_clear_drush', 42 | 'all' => 'drush_cache_clear_both', 43 | ); 44 | if ($include_bootstrapped_types) { 45 | $types += array( 46 | 'theme-registry' => 'drush_cache_clear_theme_registry', 47 | 'menu' => 'menu_rebuild', 48 | 'css-js' => 'drush_cache_clear_css_js', 49 | 'block' => 'drush_cache_clear_block', 50 | 'module-list' => 'drush_get_modules', 51 | 'theme-list' => 'drush_get_themes', 52 | ); 53 | } 54 | $drupal_version = drush_drupal_major_version(); 55 | 56 | if ($drupal_version >= 7) { 57 | $types['registry'] = 'registry_update'; 58 | } 59 | elseif ($drupal_version == 6 && function_exists('module_exists') && module_exists('autoload')) { 60 | // TODO: move this to autoload module. 61 | $types['registry'] = 'autoload_registry_update'; 62 | } 63 | 64 | return $types; 65 | } 66 | 67 | function drush_cache_clear_theme_registry() { 68 | if (drush_drupal_major_version() >= 7) { 69 | drupal_theme_rebuild(); 70 | } 71 | else { 72 | cache_clear_all('theme_registry', 'cache', TRUE); 73 | } 74 | } 75 | 76 | function drush_cache_clear_menu() { 77 | return menu_router_rebuild(); 78 | } 79 | 80 | function drush_cache_clear_css_js() { 81 | _drupal_flush_css_js(); 82 | drupal_clear_css_cache(); 83 | drupal_clear_js_cache(); 84 | } 85 | 86 | /** 87 | * Clear the cache of the block output. 88 | */ 89 | function drush_cache_clear_block() { 90 | cache_clear_all(NULL, 'cache_block'); 91 | } 92 | 93 | /** 94 | * Clear caches internal to Drush core and Drupal. 95 | */ 96 | function drush_cache_clear_both() { 97 | drush_cache_clear_drush(); 98 | if (drush_has_boostrapped(DRUSH_BOOTSTRAP_DRUPAL_FULL)) { 99 | drupal_flush_all_caches(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /commands/core/outputformat/key_value.inc: -------------------------------------------------------------------------------- 1 | "Two B or ! Two B, that is the comparison", 21 | * "c" => "I see that C has gone to Sea" 22 | * ); 23 | * 24 | * Output with --format=key-value: 25 | * 26 | * b : Two B or ! Two B, 27 | * that is the 28 | * comparison 29 | * c : I see that C has gone 30 | * to Sea 31 | * 32 | * Code: 33 | * 34 | * return array( 35 | * "a" => array( 36 | * "b" => "Two B or ! Two B, that is the comparison", 37 | * "c" => "I see that C has gone to Sea" 38 | * ), 39 | * "d" => array( 40 | * "e" => "Elephants and electron microscopes", 41 | * "f" => "My margin is too small" 42 | * ) 43 | * ); 44 | * 45 | * Output with --format=key-value-list: 46 | * 47 | * b : Two B or ! Two B, 48 | * that is the 49 | * comparison 50 | * c : I see that C has gone 51 | * to Sea 52 | * 53 | * e : Elephants and 54 | * electron microscopes 55 | * f : My margin is too 56 | * small 57 | */ 58 | class drush_outputformat_key_value extends drush_outputformat { 59 | function format($input, $metadata) { 60 | if (!is_array($input)) { 61 | if (isset($metadata['label'])) { 62 | $input = array(dt($metadata['label']) => $input); 63 | } 64 | else { 65 | return $this->format_error(dt('No label provided.')); 66 | } 67 | } 68 | $kv_metadata = isset($metadata['table-metadata']) ? $metadata['table-metadata'] : array(); 69 | if ((!isset($kv_metadata['key-value-item'])) && (isset($metadata['field-labels']))) { 70 | $input = drush_select_output_fields($input, $metadata['field-labels'], $metadata['field-mappings']); 71 | } 72 | if (isset($metadata['include-field-labels'])) { 73 | $kv_metadata['include-field-labels'] = $metadata['include-field-labels']; 74 | } 75 | $formatted_table = drush_key_value_to_array_table($input, $kv_metadata); 76 | if ($formatted_table === FALSE) { 77 | return FALSE; 78 | } 79 | return drush_format_table($formatted_table, FALSE, array()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/fieldTest.php: -------------------------------------------------------------------------------- 1 | markTestSkipped("Field API not available in Drupal 6."); 15 | } 16 | 17 | if (UNISH_DRUPAL_MAJOR_VERSION == 8) { 18 | $this->markTestSkipped("Field commands are not yet ported to D8."); 19 | } 20 | 21 | $sites = $this->setUpDrupal(1, TRUE); 22 | $options = array( 23 | 'yes' => NULL, 24 | 'root' => $this->webroot(), 25 | 'uri' => key($sites), 26 | ); 27 | 28 | $expected_url = '/admin/config/people/accounts/fields/subtitle'; 29 | if (UNISH_DRUPAL_MAJOR_VERSION >= 8) { 30 | // Prepend for D8. We might want to change setUpDrupal() to add clean url. 31 | $expected_url = '/index.php' . $expected_url; 32 | } 33 | // Create two field instances on article content type. 34 | $this->drush('field-create', array('user', 'city,text,text_textfield', 'subtitle,text,text_textfield'), $options + array('entity_type' => 'user')); 35 | $output = $this->getOutput(); 36 | list($city, $subtitle) = explode(' ', $output); 37 | $url = parse_url($subtitle); 38 | $this->assertEquals($expected_url, $url['path']); 39 | 40 | // Assure that the second field instance was created correctly (subtitle). 41 | $this->verifyInstance('subtitle', $options); 42 | 43 | // Assure that field update URL looks correct. 44 | $this->drush('field-update', array('subtitle'), $options); 45 | $output = $this->getOutput(); 46 | $url = parse_url($this->getOutput()); 47 | $this->assertEquals($expected_url, $url['path']); 48 | 49 | // Assure that field-clone actually clones. 50 | $this->drush('field-clone', array('subtitle', 'subtitlecloned'), $options); 51 | $this->verifyInstance('subtitlecloned', $options); 52 | 53 | // Assure that delete works. 54 | $this->drush('field-delete', array('subtitlecloned'), $options); 55 | $this->verifyInstance('subtitlecloned', $options, FALSE); 56 | } 57 | 58 | function verifyInstance($name, $options, $expected = TRUE) { 59 | $this->drush('field-info', array('fields'), $options + array('format' => 'json')); 60 | $output = $this->getOutputFromJSON(); 61 | $found = FALSE; 62 | foreach($output as $key => $field) { 63 | if ($key == $name) { 64 | $this->assertEquals('text', $field->type, $name . ' field is of type=text.'); 65 | $this->assertEquals('user', current($field->bundle), $name . ' field was added to user bundle.'); 66 | $found = TRUE; 67 | break; 68 | } 69 | } 70 | if ($expected) { 71 | $this->assertTrue($found, $name . ' field was created.'); 72 | } 73 | else { 74 | $this->assertFalse($found, $name . ' field was not present.'); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /tests/siteSshTest.php: -------------------------------------------------------------------------------- 1 | is_windows()) { 18 | $this->markTestSkipped('ssh command not currently available on Windows.'); 19 | } 20 | 21 | $options = array( 22 | 'simulate' => NULL, 23 | ); 24 | $this->drush('ssh', array(), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1'); 25 | $output = $this->getOutput(); 26 | $expected = sprintf('Calling proc_open(ssh -o PasswordAuthentication=no -t %s@%s %s);', self::escapeshellarg('user'), self::escapeshellarg('server'), "'cd /path/to/drupal && bash -l'"); 27 | $this->assertEquals($expected, $output); 28 | } 29 | 30 | /** 31 | * Test drush ssh --simulate 'date'. 32 | * @todo Run over a site list. drush_sitealias_get_record() currently cannot 33 | * handle a site list comprised of longhand site specifications. 34 | */ 35 | public function testNonInteractive() { 36 | $options = array( 37 | 'cd' => '0', 38 | 'simulate' => NULL, 39 | ); 40 | $this->drush('ssh', array('date'), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1'); 41 | $output = $this->getOutput(); 42 | $expected = sprintf('Calling proc_open(ssh -o PasswordAuthentication=no %s@%s %s);', self::escapeshellarg('user'), self::escapeshellarg('server'), self::escapeshellarg('date')); 43 | $this->assertEquals($expected, $output); 44 | } 45 | 46 | /** 47 | * Test drush ssh with multiple arguments (preferred form). 48 | */ 49 | public function testSshMultipleArgs() { 50 | $options = array( 51 | 'cd' => '0', 52 | 'simulate' => NULL, 53 | ); 54 | $this->drush('ssh', array('ls', '/path1', '/path2'), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1'); 55 | $output = $this->getOutput(); 56 | $expected = sprintf('Calling proc_open(ssh -o PasswordAuthentication=no %s@%s \'ls /path1 /path2\');', self::escapeshellarg('user'), self::escapeshellarg('server')); 57 | $this->assertEquals($expected, $output); 58 | } 59 | 60 | /** 61 | * Test drush ssh with multiple arguments (legacy form). 62 | */ 63 | public function testSshMultipleArgsLegacy() { 64 | $options = array( 65 | 'cd' => '0', 66 | 'simulate' => NULL, 67 | ); 68 | $this->drush('ssh', array('ls /path1 /path2'), $options, 'user@server/path/to/drupal#sitename', NULL, self::EXIT_SUCCESS, '2>&1'); 69 | $output = $this->getOutput(); 70 | $expected = sprintf('Calling proc_open(ssh -o PasswordAuthentication=no %s@%s \'ls /path1 /path2\');', self::escapeshellarg('user'), self::escapeshellarg('server')); 71 | $this->assertEquals($expected, $output); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /commands/pm/version_control/backup.inc: -------------------------------------------------------------------------------- 1 | prepare_backup_dir()) { 22 | if ($project['project_type'] != 'core') { 23 | $backup_target .= '/' . $project['project_type'] . 's'; 24 | drush_mkdir($backup_target); 25 | } 26 | $backup_target .= '/'. $project['name']; 27 | // Save for rollback or notifications. 28 | $project['backup_target'] = $backup_target; 29 | 30 | // Move or copy to backup target based in package-handler. 31 | if (drush_get_option('package-handler', 'wget') == 'wget') { 32 | if (drush_move_dir($project['full_project_path'], $backup_target)) { 33 | return TRUE; 34 | } 35 | } 36 | // cvs or git. 37 | elseif (drush_copy_dir($project['full_project_path'], $backup_target)) { 38 | return TRUE; 39 | } 40 | return drush_set_error('DRUSH_PM_BACKUP_FAILED', dt('Failed to backup project directory !project to !backup_target', array('!project' => $project['full_project_path'], '!backup_target' => $backup_target))); 41 | } 42 | } 43 | 44 | /** 45 | * Implementation of rollback(). 46 | */ 47 | public function rollback($project) { 48 | if (drush_get_option('no-backup', FALSE)) { 49 | return; 50 | } 51 | if (drush_move_dir($project['backup_target'], $project['full_project_path'], TRUE)) { 52 | return drush_log(dt("Backups were restored successfully."), 'ok'); 53 | } 54 | return drush_set_error('DRUSH_PM_BACKUP_ROLLBACK_FAILED', dt('Could not restore backup and rollback from failed upgrade. You will need to resolve manually.')); 55 | } 56 | 57 | /** 58 | * Implementation of post_update(). 59 | */ 60 | public function post_update($project) { 61 | if (drush_get_option('no-backup', FALSE)) { 62 | return; 63 | } 64 | if ($project['backup_target']) { 65 | drush_log(dt("Backups were saved into the directory !backup_target.", array('!backup_target' => $project['backup_target'])), 'ok'); 66 | } 67 | } 68 | 69 | /** 70 | * Implementation of post_download(). 71 | */ 72 | public function post_download($project) { 73 | // NOOP 74 | } 75 | 76 | // Helper for pre_update. 77 | public function prepare_backup_dir($subdir = NULL) { 78 | return drush_prepare_backup_dir($subdir); 79 | } 80 | 81 | public static function reserved_files() { 82 | return array(); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /commands/core/drupal/site_install.inc: -------------------------------------------------------------------------------- 1 | FALSE) + install_state_defaults(); 14 | try { 15 | install_begin_request($class_loader, $install_state); 16 | $profile = _install_select_profile($install_state); 17 | } 18 | catch (\Exception $e) { 19 | // This is only a best effort to provide a better default, no harm done 20 | // if it fails. 21 | } 22 | if (empty($profile)) { 23 | $profile = 'standard'; 24 | } 25 | } 26 | 27 | $sql = drush_sql_get_class(); 28 | $db_spec = $sql->db_spec(); 29 | 30 | $account_pass = drush_get_option('account-pass', drush_generate_password()); 31 | $account_name = drush_get_option('account-name', 'admin'); 32 | $settings = array( 33 | 'parameters' => array( 34 | 'profile' => $profile, 35 | 'langcode' => drush_get_option('locale', 'en'), 36 | ), 37 | 'forms' => array( 38 | 'install_settings_form' => array( 39 | 'driver' => $db_spec['driver'], 40 | $db_spec['driver'] => $db_spec, 41 | 'op' => dt('Save and continue'), 42 | ), 43 | 'install_configure_form' => array( 44 | 'site_name' => drush_get_option('site-name', 'Site-Install'), 45 | 'site_mail' => drush_get_option('site-mail', 'admin@example.com'), 46 | 'account' => array( 47 | 'name' => $account_name, 48 | 'mail' => drush_get_option('account-mail', 'admin@example.com'), 49 | 'pass' => array( 50 | 'pass1' => $account_pass, 51 | 'pass2' => $account_pass, 52 | ), 53 | ), 54 | 'update_status_module' => array( 55 | 1 => TRUE, 56 | 2 => TRUE, 57 | ), 58 | 'clean_url' => drush_get_option('clean-url', TRUE), 59 | 'op' => dt('Save and continue'), 60 | ), 61 | ), 62 | ); 63 | 64 | // Merge in the additional options. 65 | foreach ($additional_form_options as $key => $value) { 66 | $current = &$settings['forms']; 67 | foreach (explode('.', $key) as $param) { 68 | $current = &$current[$param]; 69 | } 70 | $current = $value; 71 | } 72 | 73 | $msg = 'Starting Drupal installation. This takes a while.'; 74 | if (is_null(drush_get_option('notify'))) { 75 | $msg .= ' Consider using the --notify global option.'; 76 | } 77 | drush_log(dt($msg), 'ok'); 78 | drush_op('install_drupal', $class_loader, $settings); 79 | drush_log(dt('Installation complete. User name: @name User password: @pass', array('@name' => $account_name, '@pass' => $account_pass)), 'ok'); 80 | } 81 | -------------------------------------------------------------------------------- /commands/core/queue.drush.inc: -------------------------------------------------------------------------------- 1 | 'Run a specific queue by name', 19 | 'arguments' => array( 20 | 'queue_name' => 'The name of the queue to run, as defined in either hook_queue_info or hook_cron_queue_info.', 21 | ), 22 | 'required-arguments' => TRUE, 23 | 'options' => array( 24 | 'time-limit' => 'The maximum number of seconds allowed to run the queue', 25 | ), 26 | ); 27 | $items['queue-list'] = array( 28 | 'description' => 'Returns a list of all defined queues', 29 | 'outputformat' => array( 30 | 'default' => 'table', 31 | 'pipe-format' => 'csv', 32 | 'field-labels' => array( 33 | 'queue' => 'Queue', 34 | 'items' => 'Items', 35 | 'class' => 'Class', 36 | ), 37 | 'ini-item' => 'items', 38 | 'table-metadata' => array( 39 | 'key-value-item' => 'items', 40 | ), 41 | 'output-data-type' => 'format-table', 42 | ), 43 | ); 44 | 45 | return $items; 46 | } 47 | 48 | /** 49 | * Validation callback for drush queue-run. 50 | */ 51 | function drush_queue_run_validate($queue_name) { 52 | try { 53 | $queue = drush_queue_get_class(); 54 | $queue->getInfo($queue_name); 55 | } 56 | catch (\Drush\Queue\QueueException $exception) { 57 | return drush_set_error('DRUSH_QUEUE_RUN_VALIDATION_ERROR', $exception->getMessage()); 58 | } 59 | } 60 | 61 | /** 62 | * Return the appropriate queue class. 63 | */ 64 | function drush_queue_get_class() { 65 | return drush_get_class('Drush\Queue\Queue'); 66 | } 67 | 68 | /** 69 | * Command callback for drush queue-run. 70 | * 71 | * Queue runner that is compatible with queues declared using both 72 | * hook_queue_info() and hook_cron_queue_info(). 73 | * 74 | * @param $queue_name 75 | * Arbitrary string. The name of the queue to work with. 76 | */ 77 | function drush_queue_run($queue_name) { 78 | $queue = drush_queue_get_class(); 79 | $time_limit = (int) drush_get_option('time-limit'); 80 | $start = microtime(TRUE); 81 | $count = $queue->run($queue_name, $time_limit); 82 | $elapsed = microtime(TRUE) - $start; 83 | drush_log(dt('Processed @count items from the @name queue in @elapsed sec.', array('@count' => $count, '@name' => $queue_name, '@elapsed' => round($elapsed, 2))), drush_get_error() ? 'warning' : 'ok'); 84 | } 85 | 86 | /** 87 | * Command callback for drush queue-list. 88 | */ 89 | function drush_queue_list() { 90 | $queue = drush_queue_get_class(); 91 | return $queue->listQueues(); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Configuration file for unit test runner at http://travis-ci.org/#!/drush-ops/drush 2 | # whitelist 3 | branches: 4 | only: 5 | - master 6 | - 7.x 7 | - 6.x 8 | - 5.x 9 | language: php 10 | php: 11 | # See master-fulltest branch for broader PHP version testing. 12 | - 5.5 13 | 14 | # http://blog.travis-ci.com/2014-12-17-faster-builds-with-container-based-infrastructure/ 15 | sudo: false 16 | 17 | env: 18 | matrix: 19 | #D7 20 | - UNISH_DRUPAL_MAJOR_VERSION=7 PHPUNIT_ARGS=--group=make 21 | - UNISH_DRUPAL_MAJOR_VERSION=7 PHPUNIT_ARGS=--group=base 22 | - UNISH_DRUPAL_MAJOR_VERSION=7 PHPUNIT_ARGS=--group=commands 23 | - UNISH_DRUPAL_MAJOR_VERSION=7 PHPUNIT_ARGS=--group=pm 24 | - UNISH_DRUPAL_MAJOR_VERSION=7 PHPUNIT_ARGS=--group=quick-drupal 25 | - UNISH_DRUPAL_MAJOR_VERSION=7 PHPUNIT_ARGS=--exclude-group=base,make,commands,pm,quick-drupal 26 | #D8 27 | - UNISH_DRUPAL_MAJOR_VERSION=8 PHPUNIT_ARGS=--group=make 28 | - UNISH_DRUPAL_MAJOR_VERSION=8 PHPUNIT_ARGS=--group=base 29 | - UNISH_DRUPAL_MAJOR_VERSION=8 PHPUNIT_ARGS=--group=commands 30 | - UNISH_DRUPAL_MAJOR_VERSION=8 PHPUNIT_ARGS=--group=pm 31 | - UNISH_DRUPAL_MAJOR_VERSION=8 PHPUNIT_ARGS=--group=quick-drupal 32 | - UNISH_DRUPAL_MAJOR_VERSION=8 PHPUNIT_ARGS=--exclude-group=base,make,commands,pm,quick-drupal TEST_CHILDREN="drush-ops/config-extra" 33 | 34 | # - UNISH_DB_URL=sqlite://none/of/this/matters PHPUNIT_ARGS=--group=make 35 | # - UNISH_DB_URL=sqlite://none/of/this/matters PHPUNIT_ARGS=--group=base 36 | # - UNISH_DB_URL=sqlite://none/of/this/matters PHPUNIT_ARGS=--group=commands 37 | # - UNISH_DB_URL=sqlite://none/of/this/matters PHPUNIT_ARGS=--group=pm 38 | # - UNISH_DB_URL=sqlite://none/of/this/matters PHPUNIT_ARGS=--group=quick-drupal 39 | # - UNISH_DB_URL=sqlite://none/of/this/matters PHPUNIT_ARGS=--exclude-group=base,make,commands,pm,quick-drupal 40 | # - UNISH_DB_URL=pgsql://postgres:@localhost PHPUNIT_ARGS=--group=make 41 | # - UNISH_DB_URL=pgsql://postgres:@localhost PHPUNIT_ARGS=--group=base 42 | # - UNISH_DB_URL=pgsql://postgres:@localhost PHPUNIT_ARGS=--group=commands 43 | # - UNISH_DB_URL=pgsql://postgres:@localhost PHPUNIT_ARGS=--group=pm 44 | # - UNISH_DB_URL=pgsql://postgres:@localhost PHPUNIT_ARGS=--group=quick-drupal 45 | # - UNISH_DB_URL=pgsql://postgres:@localhost PHPUNIT_ARGS=--exclude-group=base,make,commands,pm,quick-drupal 46 | global: 47 | - secure: VfYokT2CchfuBRJp9/gSwfVGPfsVfkZdDVEuNWEqxww3z4vq+5aLKqoCtPL54E5EIMjhyCE3GVo+biG35Gab1KOVgUs8zD1hAUWA1FPKfMFhoPDfI3ZJC2rX2T1iWK4ZR90pBtcPzS+2OObzTYz8go0PfeSTT6eq69Na1KcNLaE= 48 | - UNISH_NO_TIMEOUTS=y 49 | - UNISH_DB_URL=mysql://root:@127.0.0.1 50 | before_install: composer selfupdate 51 | install: composer install 52 | 53 | before_script: 54 | - echo "sendmail_path='true'" >> `php --ini | grep "Loaded Configuration" | awk '{print $4}'` 55 | - export UNISH_DRUSH="${PWD}/drush" 56 | 57 | script: ${PWD}/unish.sh $PHPUNIT_ARGS 58 | 59 | after_success: ${PWD}/tests/testChildren.sh 60 | -------------------------------------------------------------------------------- /commands/core/drupal/site_install_7.inc: -------------------------------------------------------------------------------- 1 | db_spec(); 31 | 32 | $account_pass = drush_get_option('account-pass', drush_generate_password()); 33 | $account_name = drush_get_option('account-name', 'admin'); 34 | $settings = array( 35 | 'parameters' => array( 36 | 'profile' => $profile, 37 | 'locale' => drush_get_option('locale', 'en'), 38 | ), 39 | 'forms' => array( 40 | 'install_settings_form' => array( 41 | 'driver' => $db_spec['driver'], 42 | $db_spec['driver'] => $db_spec, 43 | 'op' => dt('Save and continue'), 44 | ), 45 | 'install_configure_form' => array( 46 | 'site_name' => drush_get_option('site-name', 'Site-Install'), 47 | 'site_mail' => drush_get_option('site-mail', 'admin@example.com'), 48 | 'account' => array( 49 | 'name' => $account_name, 50 | 'mail' => drush_get_option('account-mail', 'admin@example.com'), 51 | 'pass' => array( 52 | 'pass1' => $account_pass, 53 | 'pass2' => $account_pass, 54 | ), 55 | ), 56 | 'update_status_module' => array( 57 | 1 => TRUE, 58 | 2 => TRUE, 59 | ), 60 | 'clean_url' => drush_get_option('clean-url', TRUE), 61 | 'op' => dt('Save and continue'), 62 | ), 63 | ), 64 | ); 65 | 66 | // Merge in the additional options. 67 | foreach ($additional_form_options as $key => $value) { 68 | $current = &$settings['forms']; 69 | foreach (explode('.', $key) as $param) { 70 | $current = &$current[$param]; 71 | } 72 | $current = $value; 73 | } 74 | 75 | $msg = 'Starting Drupal installation. This takes a while.'; 76 | if (is_null(drush_get_option('notify'))) { 77 | $msg .= ' Consider using the --notify global option.'; 78 | } 79 | drush_log(dt($msg), 'ok'); 80 | drush_op('install_drupal', $settings); 81 | drush_log(dt('Installation complete. User name: @name User password: @pass', array('@name' => $account_name, '@pass' => $account_pass)), 'ok'); 82 | } 83 | -------------------------------------------------------------------------------- /commands/runserver/runserver-prepend.php: -------------------------------------------------------------------------------- 1 | uid; 41 | } 42 | else { 43 | $uid = $log_entry['user']->id(); 44 | } 45 | $message = strtr('Watchdog: !message | severity: !severity | type: !type | uid: !uid | !ip | !request_uri | !referer | !link', array( 46 | '!message' => strip_tags(!isset($log_entry['variables']) ? $log_entry['message'] : strtr($log_entry['message'], $log_entry['variables'])), 47 | '!severity' => $log_entry['severity'], 48 | '!type' => $log_entry['type'], 49 | '!ip' => $log_entry['ip'], 50 | '!request_uri' => $log_entry['request_uri'], 51 | '!referer' => $log_entry['referer'], 52 | '!uid' => $uid, 53 | '!link' => strip_tags($log_entry['link']), 54 | )); 55 | error_log($message); 56 | } 57 | } 58 | 59 | // Get a $_SERVER key, or equivalent environment variable 60 | // if it is not set in $_SERVER. 61 | function runserver_env($key) { 62 | if (isset($_SERVER[$key])) { 63 | return $_SERVER[$key]; 64 | } 65 | else { 66 | return getenv($key); 67 | } 68 | } 69 | --------------------------------------------------------------------------------