Drupal automation with Bash scripting and Drush
I have always been facinated by the idea of automation. Perhaps it's a pre-requisite of being a programmer, or maybe it's just what draws many of us to the field, but I have never liked doing the same thing any more times than necessary. That mind set drew me towards Drupal and open source, because most of the components needed to build powerful web sites have already been built, so I don't need to build them over and over again. This mindset also has caused me to be facinated by things like bash scripting, Mac's Automator, and anything else that allows me to take repetative tasks and wrap them up in a nice package to do them for me over and over again. My wife has sometimes chuckled at me for the number of hours I'm willing to spend creating an automated set-up for something that will save me only a few minutes a day down the road. But hey, just like compound interest, those little numbers can really add up.
Probably the greatest time saver ever created for doing Drupal development is the incredible "Drush Shell", drush (and just to clarify, that is pronounced as one word like "rush", but beginning with the "dr" sound of "draw"). With drush, things like 15 spent downloading and extracting modules can turn in to:
drush dl ctools views panels -y
drush en ctools views panels -y
This tells drush to download the ctools, views and panels modules and then enable them. -y tells drush to automatically answer yes to all questions that come up during the process (like, "are you sure you want to enable these modules?"). I wouldn't recommend using the -y until you really know what you're doing, because for some drush commands the questions can be paraphrased as "are you really sure you want to completely erase the last week of content on your site without backing up first?".
Now, drush documentation is excellent and there are many blog posts written on the subject, so I'm not aiming to give another tutorial on drush today. What I do want to do, is talk about taking drush one step further with the power of bash scripting. "BASH" stands for "Bourne Again Shell", and actually it is just one of many forms of scripting that will do the same thing. If you're on Windows, this could a batch file, and if you're on mac or unix, there are a variety of other shells one can use. For today's purposes, I will be using bash, as this is the default shell on a wide range of unix based operating systems.
A bash script is, in its simplest form, a list of commands that should be run from the command line. The power of this should not be underestimated for saving you a lot of time. In fact, I very frequently use bash scripts for just that. Consider just the simple two lines above. They didn't take me long to type, but if I used those two commands over and over again, I could put them in and just type
merlin.sh
instead. (referencing "merlinofchaos" Earl Miles, the author of those three modules). Now that's not much time saved, but the concept applies anywhere you have a lot of lines to type out. So what are some ideas of how to use this?
- Backup scripts for a production site
- Syncing databases and files on a development site
- Shortening long SSH or FTP commands so you don't need to remember all the options and parameters
- Setting up new local development sites
And the list could go on and on. Basically, you don't need to type out long things over and over again.. just put them in a script.
Now onto a userful script
So we've talked a bit about the basics. There are some more advanced things we can do, and I want to give you an example that I cooked together for a client. First I'll show you the script, then I'll go through what's happening on the important lines.
#!/bin/bash
VERSION=$2
ENVIRONMENT=$1
BASE=/var/www/vhosts/site.$ENVIRONMENT
DIR=$BASE/builds/$VERSION
if [ $# -ne 2 ]; then
echo "Usage: $0 [environment] [version number]"
exit
fi
if [ -d "$DIR" ]; then
# run a backup script of the production database before any changes are made
cd $BASE/public
drush sql-dump --result-file
# switch production to point to the appropriate build
rm $BASE/public
ln -s $DIR $BASE/public
# run drush commands to bring everything up to speed
drush updatedb --yes
# drush @$ENVIRONMENT fr-all --yes
drush cc all
else
echo "Build $VERSION does not yet exist, please use the build script to create this first."
fi
OK, so there's a lot to say about these 27 little lines. First of all, this all goes into a file which I've called switch.sh on a server I'm running. Basically, this script is a companion script to a build.sh which pulls down some code from a git repository and sets up a file structure for a new deployment of a site. It illustrates a handfull of useful concepts though which you can take to meet your own needs.
Line 1 should go at the beginning of every bash script (it will look different if you're using something besides bash). That line just says how the file should be run when you invoke it.
Lines 2-5 are how you do variable assignment. Line 2 and 3 are a special case where I'm taking input from the command line.. e.g. "switch.sh dev 1.1", where switch.sh is the name of the file, 1.1 will be assigned to the VERSION variable and dev will be assigned to the ENVIRONMENT variable. The next two lines just use those variables to assign some longer strings that will be used later in the file.
Lines 7-10 are how to do a simple conditional structure. "$#" is the number of parameters passed in, and "-ne" is "not equals", so this line literally says "if there are not exactly two parameters passed in to this script, do the following" then lines 8 and 9 print out a message and exit the script. This helps me when I forget how to use the script, because I can just run "switch.sh" and it will give me a helpful message that tells me I put in the environment first, then the version number. "fi" is just how you end an "if" in a bash script.
Line 12 does a check to see whether the DIR variable is a path to a valid directory. This prevents me from using this script if I haven't first used the build script yet. An important note here is that when you do variable assignment, e.g. lines 2-5, there is no dollar sign. When you reference those variables later on, however, there is a dollar sign.
Lines 13-24 simply run a series of commands. The specifics of these commands are very much geared towards this particular server set-up, but basically this is just switching into the root directory of a site, running a sql-dump in case something messes up in this process, switching the document root of a site to point to a new directory (the one built earlier), then running a database update and clearing caches. Note that I've used --yes at the end of some drush commands. This is very helpful in scripting so you don't have to sit and monitor this and can even automate processes further because the script can run entirely on its own.
Line 23 was something I commented out because it because impractical on this particular site, but that line reverted all features on the site using a drush alias named the same as the enviornment I was in.
Finally, line 25 shows how to do an "else" which matches up to the if statement on line 12. Line 26 prints a message to the terminal if there was not an existing directory. Then line 27 closes out the if block that started in line 12.
Running the script is fairly simple. First, save the file in a convenient directory. I like to make a directory in my home directory called "bin", so I can get to all my scripts I use on a regular basis at ~/bin/script-name.sh. This also means that you can add that ~/bin directory to your PATH so you don't have to use the long path name every time. Before you can run the script, you need to make it executable. This is done on mac & linux with the command "chmod u+x path/to/script-name.sh", so in this example, I could use:
cd ~/bin
chmod u+x switch.sh
Or just
chmod u+x ~/bin/switch.sh
Finally, I can run the script by just calling "~/bin/switch.sh", or if ~/bin is in your path, just type "switch.sh".
Now your imagination is the limit on how many things you can automate with these tips. I have a few links below that will give you more information on drush and bash scripting. So now you can go out and make the world a better place with automation!
Further reading
Do you have questions on any of this or ideas on better or more ways to use bash with drush? Please comment below.
var switchTo5x = true;stLight.options({"publisher":"dr-53823897-181c-4461-ae09-5adff87aeeb"});