Install Drush 7 and 8 Side-by-Side and Automatically Switch Versions Based on Each Project | Modules Unraveled

Install Drush 7 and 8 Side-by-Side

Have you started working with Drupal 8 yet? If so, you might have noticed that Drush 7 doesn't play nice with Drupal 8. And if you install Drush 8, that won't work with your Drupal 7 sites. Yikes!

Have no fear!

Here's how to install BOTH Drush 7 and Drush 8 AND have each project automatically use the version that corresponds to that install. It's stinkin' awesome!

(The following is an combination and adaptation of techniques I learned from two blog posts. Both were a bit outdated when I came across them, so I've updated the techniques here. The first is a Lullabot article, and the second is on the Triquanta blog)

Uninstall existing Drush instances

Okay, the first thing you'll want to do is uninstall every version of Drush that you already have installed. This process varies depending on how you installed it, but for example, if you installed with homebrew, the command would be something like brew remove --force drush.

Install Composer

We're going to install multiple versions of Drush using Composer, so we need to make sure you have that installed first. Detailed instructions on how to install Composer globally are on their website, but here's the gist.

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

Note: If this fails due to permissions, run the mv line again with sudo. You may also have to create the /usr/local/bin directory first, depending on your existing system.

  • To confirm composer was successfully installed, type composer --version and you should see something like "Composer version 1.0-dev (...) 2016-01-20 11:17:40"

Install Drush 8

Okay, let's install Drush 8!

cd /usr/local/bin
mkdir drush-8
cd drush-8
composer require drush/drush:8.x
ln -s /usr/local/bin/drush-8/vendor/bin/drush /usr/local/bin/drush8
  • The "composer require..." line will download the latest stable release, you could replace "8.x" with "8.0.x-dev", for example, to download the dev version
  • The "ln -s..." line creates a "symbolic link" called "drush8" in the /usr/local/bin directory to the location where Drush 8 is installed. This means that we can call it from anywhere on the system by typing "drush8 --version", for example.

Easy!

Install Drush 7

Now, we'll install Drush 7!

cd /usr/local/bin
mkdir drush-7
cd drush-7
composer require drush/drush:7.x
ln -s /usr/local/bin/drush-7/vendor/bin/drush /usr/local/bin/drush7
  • The "composer require..." line will download the latest stable release, you could replace "7.x" with "7.x-dev", for example, to download the dev version.
  • The "ln -s..." line creates a "symbolic link" called "drush7" in the /usr/local/bin directory to the location where Drush 7 is installed. This means that we can call it from anywhere on the system by typing "drush7 --version", for example.

Create Shell Script to Automatically Select Version Based on Git Config

Now, if you're already used to typing something like "drush --version" (without the specific version number), remembering to use it can be a little cumbersome, so now, we're going to create a little shell script that will automatically use the correct one for each project based on a git config variable that we set.

cd /usr/local/bin
vi drush
  • Press the "i" key to enter "insert" mode
  • Paste the following
#!/bin/sh
version=$(git config --get drush.version)
if [ "$version" = '7' ];
then
drush7 "$@"
else
drush8 "$@"
fi
  • Press "esc", then type ":wq" and press "enter" to save and quit this file
  • Type chmod +x drush (This makes the "drush" script we just created executable.)

Now, when we type a command like "drush --version" it will use Drush 8 by default. In order to use Drush 7, we need to set a configuration variable in the git repo of the project that should use it.

Set Drush 7 as the Required Version for a Project

cd /path/to/project
drush --version
git config drush.version 7
drush --version
  • The first time you run "drush --version" it should return something like "Drush Version : 8.0.0-rc3" showing that you're using Drush 8
  • The "git config..." line declares that you want to use Drush 7 for this project
  • The second time you run "drush --version" It should show something like "Drush Version : 7.1.0". If so, you're all set!

YAY!!!

You might want/need to close and re-open all terminal windows to make sure it takes effect.

If you have any questions about, or issues with, this setup, let me know in the comments!

Comments

This setup certainly works, but another option is to use site-local Drush. Site-local Drush has a distinct advantage when using Composer to manage your Drupal site. See: https://pantheon.io/blog/avoiding-dependency-hell-site-local-drush

I did see that post a while back, but (please correct me if I'm wrong) doesn't that install drush for every site? So, if I have 12 Drupal sites, I'll also have Drush installed 12 times?

That's probably not a huge deal to most, but I'm running a 128GB hard drive, and always having to delete things to free up space. So, it seemed like an unnecessary use of hard drive space.

I'm probably just ignorant though, so again, please let me know if there's something huge I'm missing. Like maybe not all Drupal 8 sites will work with the latest Drush 8 version?

If you want to minimize the number of copies of Drush you have on your system, then the techniques you describe here will work best for that.

Having a separate copy of Drush, and a separate copy of Drupal Composer for every Drupal site on a system is important to do once you start using modules (or Drupal 8 core!) that use Composer to manage their external dependent libraries. Imagine that you have a Drush extension that uses a Composer-managed library, foo/foo: 1.0, and you also have a Drupal module that uses foo/foo: 1.0. This will work fine until the Drupal module starts using foo/foo: 2.0. At that point, foo/foo: 1.0 will be autoloaded by the Drush extension, and, when the Drupal module is initialized, it will have problems, because it (or Drupal core) will try to autoload files and classes from foo/foo: 2.0, which will crash due to incompatibilities with the files from the older version that were already loaded. If you are using a site-local Drush with site-local Drush extensions, then this problem will be reported at composer update time; when it happens at runtime, the problem can be really difficult to diagnose. Also, in some cases, you might also find that foo/foo: 1.0 crashes when used at the same time as foo/foo: 1.1; this is a problem that can be worked out immediately by using site-local components.

This sort of problem is rare at the moment, because we don't have a lot of modules (in Drupal 7) using Composer, and those that do are statistically tending to have non-overlapping requirements, or happen to both be using the most recent version of the same library. The more time that goes along, though, the more likely it will be that incompatible conflicts will occur, and keeping everything in the same autoload file makes dependency management much easier.

Drush 8 is technically compatible with Drupal 7, but some commands (like those for Omega4) are Drush 7 only... Also, if you're working on a remote site that has Drush 7 installed, your Drush 8 command might not work. ie: drush sql-sync @remote @local

I'm afraid I don't see the benefit, since it can be called globally if it's in usr/local/bin... Is there something obvious I'm missing?

The biggest advantage I see is that it's a more standard approach rather than manually creating directories in /usr/local/bin. It also means you can update it by running composer global update.

Drush suggests installing much like this http://docs.drush.org/en/master/install-alternative/, although their new way is using drush.phar http://docs.drush.org/en/master/install/.

I think that unless you are installing Drush on a server or a computer were multiple users will using it there is no advantage to installing composer or drush globally. I install drush on servers where I have ssh access but not super user access like this.

To install composer for the current user we can install and then create an alias so our shell can find all composer binaries. I install in the bin folder in the user's directory.

  • Add the following lines to your ~/.bashrc file so all composer bins get added to your path.
# Add all composer bins to our path
export PATH="$HOME/.composer/vendor/bin:$PATH"

If you are on Ubuntu make sure that you place this before the lines that read,

# **If not running interactively, don't do anything more **
[ -z "$PS1" ] && return

or Drush will not be available for non-interactive sessions (ie. drush @remotesite status).

  • Then navigate to /home/username/bin and run this to install composer:

curl -sS https://getcomposer.org/installer | php

  • Run
    which composer
    and you should get back ~/bin/composer.phar

  • Then installing Drush 7 is as simple as:

cd ~/
composer global require drush/drush:7.*

I think that what Tim Millwood and the anonymous poster were referring to was global in the composer sense not in the Linux sense when programs are installed globally so all user can use them. Which is what I thought when heard the term.

I normally do not install drush with composer for all Linux users, but I do use install globally (all projects use the same drush version) using composer(see the commands in my last post containing global).

Composer by default does not install programs globally but per project. As one poster stated you can easier update your globally installed module quickly with one command.

Considering that some are looking at installing Drupal itself with composer whether or not to install globally will be more of an issue in the future. Not installing it globally in the future would allow you to have the version of drush that you want with a Drupal site without setting a git variable to do so.

I went to setup Drush 8 on my laptop where I already had Drush 7 installed globally. Composer will only allow one version of a project to be installed in a project or globally. If you attempt to install Drush 8 globally on a computer where Drush 7 is installed globally it will upgrade your drush version to Drupal 8. I found out that you cannot simply do a composer global require drush/drush:7.* to downgrade as dependencies versions have conflicts. You must first do `composer global remove drush/drush' then you can run the previous command.

In the end left Drush 7 installed globally and installed Drush8 in a subfolder of ~/.composer and created an alias to it.