Easy Switching Between Drush 4.x and 5.x

Drush 5 was released a few months ago and has pretty much become the standard by now. Thanks to lots of work by anarcat and others, Aegir's upcoming 2.x will support it natively. He's also been busy packaging Drush 5 for Debian, and I look forward to making the switch, as it's the default in Wheezy/Testing.

In the mean time though, I use Drush Vagrant in my day-to-day development and testing of changes to our AegirVPS service, and Puppet modules and infrastructure. Unfortunately, Drush 5 made some major API changes, including changing how drush_invoke() works. This appears to have severely broken a number of things in the way I've built Drush Vagrant.

Among other Drush Vagrant features broken by Drush 5, we generate documentation dynamically, by running 'vagrant' in the background, and then formatting the output into a table. This is because Vagrant itself can be extended with, and I wanted to be able to include pointers to help for new commands from within Drush Vagrant's help text.

Another Drush Vagrant behaviour broken by Drush 5 is how we pass through Vagrant commands. This essentially provides Vagrant with the ability to control projects and VMs without having to change into the project's root directory, using Drush aliases. In order to do this, we need to determine whether a command is a native Vagrant command or extension, or provided by Drush Vagrant itself. We then call drush_shell_exec_interactive(), or re-dispatch with drush_do_command_redispatch() respectively.

These appear to be getting called repeatedly in Drush5, whereas they work just fine under Drush 4. As much as I want to upgrade Drush Vagrant to work with Drush 5, this is a non-trivial task. So, I need to be able to switch back and forth between Drush 4 and 5 depending on whether I'm using Drush Vagrant, or developing it. Since I run Debian Testing, short of pinning it's version at 4.x, my system automatically upgrades to Drush 5. In order to remain productive, I had installed the Debian package for Drush 4 manually, after having downloaded the .deb locally.

This week, I'm at Koumbit's annual retreat, where we make a point of not having internet access. So, since Drush had upgraded to 5.x again, but I had the 4.x .deb lying around, I had a chance to figure out a way to easily switch between them. So, on Debian systems, Drush is installed in /usr/share/drush, with a symlink pointing to it from /usr/bin/drush. I started by copying the Drush 5.x directory:

cp -r /usr/share/drush /usr/share/drush5

Then I installed the older Drush 4.x package:

dpkg -i drush_4.5-2~bpo60+1_all.deb

I then copied that directory to it's own as well:

cp -r /usr/share/drush /usr/share/drush4

Finally, I defined a couple aliases in ~/.bash_aliases:

alias drush4='sudo su -c "rm /usr/bin/drush && ln -s /usr/share/drush4/drush.php /usr/bin/drush"'
alias drush5='sudo su -c "rm /usr/bin/drush && ln -s /usr/share/drush5/drush.php /usr/bin/drush"'

Now, when I want to using Drush 4.x, I just have to run drush4. Likewise, running drush5 will switch to Drush 5.x. I'll replace these with Git repos of the respective project branches, but that'll have to wait for internet access again...


Now that I'm back online, I figure I should fix this properly, and use Git branches to switch back and forth between Drush 4.x and 5.X.

First, a big "thank you" to jonhattan for clarifying Drush's confusing branch structure. It turns out that the latest Drush 4.x can be found in the 7.x-4.x branch, whereas Drush 5.x remains in the master branch until such time as there's a major (read API-breaking) change required.

So, first, we should just remove the .deb version of the package, and the clean up the directories we'd created:

# apt-get remove drush
# rm /usr/share/drush* -rf

Then, we need to clone a copy of Drush locally:

~/code/drush$ git clone --recursive --branch master http://git.drupal.org/project/drush.git

We then need to create a link within our $PATH to that code, since the previous one had been removed when we uninstalled the package:

# ln -s ~/code/drush/drush/drush.php /usr/bin/drush

And finally changes the aliases to checkout release tags for 4.x and 5.x, and the master branch, so we can also easily track Drush 6.x development:

alias drush4='cd ~/code/drush/drush && git checkout --quiet 7.x-4.6 && cd - >/dev/null'
alias drush5='cd ~/code/drush/drush && git checkout --quiet 7.x-5.5 && cd - >/dev/null'
alias drush6='cd ~/code/drush/drush && git checkout --quiet master && cd - >/dev/null'

Much better!