How to install Composer and Drush in a multi-user configuration

Updated September 2022 to match my current Drupal 9 workflow and to make the guide easier to follow.

 

I've been putting off using Composer for a long time because it seemed too "trendy" and reminded me too much of the framework movement that HTML9 Responsive Boilerstrap JS parodied so well. However on a recent project I've been migrating a site from Drupal 6 to Drupal 8, and Drupal 8 strongly recommends using Composer from the outset so I thought it was time to take the plunge.

In this article I'll walk through how to set up Composer, Drupal 8 (or 9), and Drush in a way that means we can have multiple users running the same centralised version of Drush when appropriate (ie. when a Drupal site isn't set up as a Composer project, or a project doesn't include its own copy of Drush) which means that our team can keep using Drush seamlessly without having to install a separate copy of Composer and Drush for each user or each project on our servers. This approach should also make it easy to keep things up to date and change the configuration later on if needed.

There's a helpful outline on how to install Composer and Drupal 8+ on Drupal.org but I wanted to write a more detailed trail for future reference, including how I came to decide on this specific configuration.

1. Install Composer

Check if Composer is already installed

To check if it's already installed, run composer in your shell. If you get a bunch of output explaining composers various options, it's already installed. On recent versions of Ubuntu, if it's not installed you'll get "Command 'composer' not found, but can be installed with: sudo apt install composer". However I'd recommend following instructions on the Composer website to download it yourself instead of using apt because the apt version is often quite old, and it won't be available in the package manager on all systems anyway (for example Debian didn't have a package for it in the standard apt repositories last time I checked).

To check if Composer is installed globally, run which composer - if you're given the location of the executable (eg. /usr/local/bin/composer ) then it's installed globally and you can skip the following 'Install Composer' step. If there's no response to this command but it is installed, then it's probably installed 'locally' in your home directory (so you'll want to consider removing it from your home directory and installing it globally instead using instructions in the following step).

Install Composer (once per server)

The first option on Composer's Getting Started page is to install Composer locally in your home directory. However, I recommend going for the global install method instead so that we only have to set it up once per server rather than repeating the process for each user on each server.

3. Install Drupal

Follow the instructions on the official guide under the Create a project heading. In our workflow, towards the end of development you should eventually have a Composer project for dev and a Composer project for live. You don't need to worry about setting up a live system until later, but it's a good idea to name them appropriately from the outset - ie. the first one you set up is /var/www/projectname-dev and then live will be /var/www/projectname-live - so it's very clear which is which regardless of whether they're on the same server or not.

After following the instructions I had a Drupal 8 site ready to start working with.
I have a composer project directory at /var/www/drupal8/ and it contains:

composer.json
composer.lock
config/
vendor/
web/

The web directory contains the actual Drupal files that run the site so that needs to web-accessible, whereas everything else should be kept private.

We don't have a compatible version of Drush installed yet but you can complete the Drupal installation process with the standard web interface, or you can install Drush first and then finish installing Drupal on the command line (instructions here).

4. Install Drush in your project

In order to avoid dependency issues, it is best to require Drush on a per-project basis via Composer rather than installing it globally. At time of writing, the drupal/recommended-project package doesn't automatically install Drush for you, so you'll need to cd into the root of your composer project (/var/www/drupal8/ in my case) and run composer require drush/drush. Composer will install a copy of drush that's appropriate to whatever version of Drupal you've just installed.

When it's finished, you should be able to test the 'local' copy of Drush like this - vendor/bin/drush version - and drush will tell you its version number.

5. Install Drush Launcher

Why?

It is inconvenient to type vendor/bin/drush in order to execute Drush commands. By installing the drush launcher globally on your local machine, you can simply type drush on the command line, and the launcher will find and execute the project specific version of drush located in your project's vendor directory.

Check if Drush Launcher is already installed

Use the following checks to see if Drush Launcher is already installed before attempting to install it. Try running drush --debug from your home directory. The first line of output should state the Drush Launcher version.

If the server's been set up for Drupal 7 compatibility (as described below), this line should follow shortly after: Calling fallback: /usr/local/bin/drush8. If it's not set to work with Drupal 7, then you need to cd into a Composer project that has its own copy of Drush installed in order for it to work.

Install Drush Launcher

Follow these steps to set up the Drush Launcher script globally on your server. This is the recommended method as it only has to be done once per server.

If you don't have permission to move Drush Launcher into /usr/local/bin/ on step 3, the alternative option would be to do the following for each user:

  1. Follow steps 1 and 2 to download Drush Launcher and make it executable.
  2. Put Drush Launcher in your home directory instead of in the global location: mv drush.phar ~/.local/bin/drush
  3. Add this location to your $PATH so your shell can run Drush Launcher without you having to specify its full location:
    edit ~/.bashrc and add this line at the end: export PATH="$HOME/.local/bin/:$PATH"

6. Install a fallback version of Drush for Drupal 7 compatibility (once per server, optional)

You can skip this section if you aren't intending to maintain any Drupal 7 sites on your server.

Now we have no global copy of Drush, how do we use Drush on our Drupal 7 sites that don't use Composer? Looking at the compatibility table it seems that Drush version 8 is the last version that's compatible with Drupal 7 so we'll need a copy of that in order to maintain our old Drupal 7 sites. Details on how to install it will follow.

Drush Launcher allows us to set a fallback option to be used when your current directory doesn't have Drush installed as a Composer dependency. The fallback option is set by defining an environment variable eg. by adding a line to .bashrc in my home directory or putting a file in /etc/profile.d/. For more info on setting environment variables, see this article.

I considered a few different options here before finding a solution that I was satisfied with...

Option A

Use Composer's global option to install a fallback instance of Drush that's available to me system-wide.
If I use composer global require/require drush/drush:8.x it installs the latest version of Drush 8 in /home/vilas/.config/composer/.
Then I can set the Drush Loader fallback by adding export DRUSH_LAUNCHER_FALLBACK=/home/ben/.config/composer/vendor/bin/drush to my .bashrc file.

That works for me, but what about the others on my team who are expecting Drush to keep working the way it did before? I don't really want to install and maintain Composer and Drush for each user on each server!

Option B

Install a global instance of Drush 8 again the "old fashioned way" to use as fallback, but give it a different name so we can have it alongside Drush Launcher. To do this I followed the instructions here but with a few differences:

  1. Browse to https://github.com/drush-ops/drush/releases and download the drush.phar attached to the latest 8.x release.
  2. Test your install.
    php drush.phar core-status
  3. Rename to `drush8` instead of `php drush.phar` and move it to a new location. Destination can be anywhere on $PATH.
    chmod +x drush.phar
    sudo mv drush.phar /usr/local/bin/drush8
  4. Optional: Enrich the bash startup file with completion and aliases.
    drush8 init
  5. Create a file that sets the fallback for Drush Launcher (multi-line command):
    echo '# Based on https://kitson-consulting.co.uk/blog/composer-drush-multi-user-config
    export DRUSH_LAUNCHER_FALLBACK="/usr/local/bin/drush8"' >> drush-launcher.sh
  6. Move it to a location where it will be loaded by all users when they log in
    sudo chown root:root drush-launcher.sh
    sudo mv drush-launcher.sh /etc/profile.d/
  7. Log out and log in again to load the new environment variables. Use the env command to see which environment vars have been set.

This seems to be a relatively elegant option and it works for all users.

Conclusion

I went for option B. To make it easier to tell what's what, I actually made a sym-link pointing to drush-launcher, so now /usr/local/bin/ contains:

  • drush - a sym-link pointing to drush-launcher so that we can launch it from anywhere by running drush.
  • drush8 - a manually downloaded copy of Drush 8 for drush-launcher to use as a fall-back.
  • drush-launcher - the Drush Launcher script which attempts run a local copy of Drush if you're in a Composer project that has one installed, but falls back to the global drush8 executable if it can't find one.

Drupal 7 comes to end of life in November 2022, so once we're no longer running Drupal 7 on the server we can skip/undo step 6 and simply use Drush Launcher to find and run whatever version of Drush is installed in your project folder.

That's all folks!

 

Also of interest:

https://www.lullabot.com/articles/switching-drush-versions

Add new comment

CAPTCHA