Updating Drupal core with bash and drush

Thu, 08/05/2014 - 16:30 -- James Oakley

Yesterday, Drupal 7.28 was released.

People rush to upgrade, knowing that there will be a tranche of bug-fixes that may resolve longstanding issues.

People hesitate to upgrade, because updating Drupal core is not as simple as we'd like.

Other times, the core update is a security release, and you can't afford to wait.

This does not need to be painful!!

Upgrading core in Drupal 7

You have probably read the official documentation on doing this.

There are two ways to do it. One is to download the new copy of Drupal core, and overwrite your old copy with the new version. That has the advantage that you don't lose any extra files you've inserted (like your favicon.ico file). Although if you've customised robots.txt or .htaccess there's trouble. You're also in trouble if any code in Drupal core has been refactored such that a file has been removed - you'd still have the old one kicking around.

So the other way to do it is to download the new copy of Drupal core, then copy to it all your custom files, and the sites directory. Having done this, you delete everything in your website directory, and replace it entirely with the new Drupal version you've just merged. The snag is that Drupal marks as read-only the site-specific subdirectories of the sites directory, and also the settings.php file. If you don't forget to make those writeable again, they don't get copied over, and you lose the files.

So whatever you do, take a backup before you start. That's the files, and the database.

Either way, it's a headache. After a few iterations, you start to keep a checklist of all the files and folders you need to copy over.

Drupal 8 promises to make this much easier. But Drupal 8 is not yet released, and even after it is there will be a lot of sites using Drupal 7.

Why not script it?

So why not write a bash script that will do the heavy-lifting for you. As long as you have shell access (SSH) to your website, and as long as you have access to Drush, this is really not that difficult.

And any good web host, that has built their platform to help Drupal webmasters, will give you access to the shell and to Drush. If you're looking for one, why not try OakHosting.NET.

So here's a bash script that will do all of this for you. It follows the second method above (copying your custom files into the new copy of Drupal, before copying it back).

I cannot stress too highly: Take a backup before you start. Test everything. By using this script, you are signifying that you understand what it does, and you are taking responsibility for what you do.

Setting this up

There are 5 places for you to enter custom variables into the script that specifies how your installation works, 4 of them are at the top in a configuration section.

  1. Specify the directory of your Drupal installation. I'm using cPanel hosting, so my Drupal site is in the public_html subdirectory of my home directory - ~/public_html. If yours is different, specify it here.
  2. Specify the path to a temporary directory that the script can work in. This is where Drush will download the latest copy of Drupal, change permissions, and gradually copy your other files over. The directory does not need to exist, as the script will create one if necessary. I use ~/update_drupal.
  3. Specify a text file that lists all of the files you want to keep during the upgrade. You put one file or directory (remember, in Linux, directories are a special kind of file) per line. You probably want, at least: favicon.ico and logo.png. Is there a directory named "files", where Drupal's public files go? Put "files" into your text file. Have you added a file to verify your ownership to Google Webmaster Tools? Put google12345abcde123.html in there. And so on. There's no need to put .htaccess in your text file, as that is automatically copied anyway. I call my text file ~/drupal_copy.txt, so this is what I've put in the configuration section.
  4. Specify another text file - I've called mine drupal_sites.txt - which contains a list of all the subfolders of "/sites" that need to be copied. If you're just running the one Drupal site off this codebase, you probably need a simple text file that has only one line, containing the word "default" (without the quotation marks). If you're using Drupal multisite, you need one line for each directory you have within sites - example.com, dev.example.com, stage.example.com, othersite.com and so on. Put the name of the text file in the configuration section.

Please note that these 4 entires tell the script where to find some directories (where Drupal is located, and a temp update directory), and some files (a list of files to keep, and a list of sites). Those entries must be full paths - don't use relative paths.

Finally

5. Near the bottom of the script is a Drush statement that runs the "updatedb" (update database) command. If you're not using Drupal multi-site, this is fine as it is. If you use multi-site, you need to tell the script which website you are updating. For example, you might want to have "drush -l example.com updatedb" to say that the database for site "example.com" should be updated. You could also do this with Drush aliases. How to use Drush in a multi-site environment is a subject for another day.

A note about Linux file permissions

When you use Drush to download Drupal core, it gives files 664 permission. I use the suPHP handler, which requires 644. There's therefore a recursive chmod line in there, which removes group-write permissions recursively. If you don't need this, comment out the line, or replace it with what you do need for your setup.

The actual script

So here's the script. If you'd prefer, you can download a copy from this link. (Rename it, to give it a .sh extension)

And remember, backup first.

Upload it somewhere, like your home directory. Then run

sh core_update.sh


# The directory the Drupal site is installed in
DRUPAL_DIR=~/public_html

# The working directory for a temp copy of the new version
UPDATE_TMP_DIR=~/update_drupal

# A text file containing all the resources to copy across
# to the new version (favicon.ico, logo.png). One per line
# Note that .htaccess is copied anyway and should not be listed.
FILES_TO_KEEP=~/drupal_copy.txt

# A text file containing a list of all the site-specific
# subfolders within the sites directory. For most Drupal
# installations, the file should just contain one line with
# the word default. But you may list example.com,
# staging.example.com and so on, if you use multi-site
DRUPAL_SITES=~/drupal_sites.txt

# Near the bottom of the script is a Drush command to
# updatedb. You may need to add a -l switch to specify
# the sites you want to update, or use Drush aliases.
# This could be picked up from the sites txt file, but
# sometimes not all the databases referenced in
# sites/*/settings.php are accessible on this server.

# End configuration section

# 1. Clear out the temp directory from last time.
mkdir -p $UPDATE_TMP_DIR
cd $UPDATE_TMP_DIR
rm drupal* -rf

# 2. Download Drupal into temp directory,
# and get rid of version number in directory name.
drush dl drupal
mv $UPDATE_TMP_DIR/drupal* $UPDATE_TMP_DIR/drupal

# 3. Remove group-write permissions for suPHP.
chmod -R g-w $UPDATE_TMP_DIR/drupal/*

# 4. Make the sites directories writeable, so they don't get lost.
for sites in `cat $DRUPAL_SITES`; do
  chmod +w $DRUPAL_DIR/sites/$sites
  chmod +w $DRUPAL_DIR/sites/$sites/settings.php
done

# 5. Move all the files we wish to keep into the new version.
for keeps in `cat $FILES_TO_KEEP`; do
  mv $DRUPAL_DIR/$keeps $UPDATE_TMP_DIR/drupal
done
mv $DRUPAL_DIR/.htaccess $UPDATE_TMP_DIR/drupal

# 6. Copy the sites directory into the new version.
cp -r $DRUPAL_DIR/sites/* $UPDATE_TMP_DIR/drupal/sites

# 7. Scary bit: Delete everything left in the Drupal directory.
rm $DRUPAL_DIR/* $DRUPAL_DIR/.ht* -rf

# 8. Move the temp working copy into the main directory.
mv $UPDATE_TMP_DIR/drupal/* $UPDATE_TMP_DIR/drupal/.??* $DRUPAL_DIR

# 9. Run updates.
cd $DRUPAL_DIR
# drush -l example updatedb
drush updatedb

# 10. Remove temp working copy.
chmod -R +w $UPDATE_TMP_DIR/drupal
rm $UPDATE_TMP_DIR/drupal -rf

AttachmentSize
core_update.sh (rename from .txt)2.24 KB
Blog Category: 

Comments

Wim Leers's picture
Submitted by Wim Leers on

This only works for Drupal core. Why not use something more generic, that works also for Drupal modules, themes, and actually… for anything — even non-Drupal things? And automatically apply patches?

See http://wimleers.com/article/mr-drupal — curious to see what you think of it :)

The only thing that it doesn't do, is calling `drush updatedb`, but you could add support for that also — pull requests welcome!

James Oakley's picture
Submitted by James Oakley on

Thanks, Wim - I'll take a look at that at some point.

I guess I was going for the least work to set something up - but you're right: Why reinvent the wheel.

My
own route to writing this script was: Do everything in the File Manager
-> Discover Drush's pm-update command -> Discover that using
pm-download + updatedb is much safer -> Realise that Drush makes
loads of assumptions if it's core you're updating, and it usually breaks
my sites at that point -> Start scripting the ways I get round this
-> Write a generic script that works for all my sites.

So now I can use vanilla Drush for updating contrib modules and themes, and this script when it's core to update.

But, yes - if there's another tool that does the same thing equally simply, that also has other benefits ... I'll take a look.

[No actual name left]'s picture
Submitted by [No actual name... on

Nice script, maybe to add some "backup" part which could create automatic backup of existing site

Add new comment

Additional Terms