Much of the work you do when building Drupal sites revolves around enabling and configuring modules, clicking various admin forms and updating settings. These settings are stored in the database. You should make changes to Drupal sites locally first and not directly on the live site. But changes you make to your local Drupal site need to be replicated on your live site. This tutorial is a walk through of how to automate these changes by storing them in code in a Site Deployment module. No more clicking and configuring multiple times!

Drupal deployment

Before we carry on, what exactly do we mean by deployment and why do we need to worry about it?

In its simplest terms, deployment is when you release changes to your website from your local environment to your live site. In professional environments, you are likely to have multiple stages before live, such as QA and staging. Each of these stages require deployment for each release.

Manually making configuration changes is a serious waste of valuable time and increases the risk of human error. The goal should always be to automate the deployment process as much as possible.

The Features module can take care of a lot of it, such as custom Content Types, Views, Image Presets, Context and Variables (using Strongarm). However, there are always other changes that are stored in the database that either can’t be exported into a Feature, or Features is just not appropriate. You still need a way to deploy these. You also need a way to control Features. You need to enable, revert and update Features.

Not sure how to use Features? Check out how to create your first Feature.

The deployment module

The concept of a deployment module is pretty simple. You have one custom module for your site which handles all of the deployment needs. Most, if not all, of the real work takes place in the .install file. In that file, you implement hook_update_N. Typically you would write one hook_update_N function per release. In that function, you write code to make changes to your Drupal site.

To execute changes in the Site Deployment module, all you need to do is ask Drupal to run its update. This can be achieved by going to update.php for your site, or running a drush command (drush updb). Every time you do a deployment, you can run Drupal update again, and your site will be up to date. All changes will be automatically executed.

Intro to hook_update_N

This hook is normally used for modules where a change to the database is required . When a user upgrades to a newer version of the module, updates should be run and every implementation of hook_update_N will be since the last one that was run.

Over time, you might end up adding more and more updates. In order for Drupal to know which updates have already been run, you need to add a number to the function name for each one. When each update is run, Drupal stores that number as the schema version in the system table for that module. Drupal runs updates in numerical order. Then when Drupal runs the next update, it will run the update(s) greater in number than the previous update that has run.

For the Site Deployment module, the number we will use is 7000. So the update function will be site_deployment_update_7000().

Why 7000? There is a pattern for the numbers. The first digit is the core version of Drupal. In this case, it is Drupal 7, so you need to use 7. The second digit is the major release version for the module. This module is still a dev version, so you can use 0. Eventually, it will become major release version 1, in which case use 1, making the updates 71xx. You may even have a second release, so in that case, the update numbers will be 72xx. The final two digits are for counting sequentially. You can start with 00. The next update will be 01 (7001) and then 02 (7002) and so on.

Makeup of update number:

Using hook_update_N is a very convenient way to store and execute changes for deployment because each site knows which update was run last. When you run update again, each site will automatically run all new updates and therefore all of the required deployment code.

Let’s look at a simple example - you have two versions of a Drupal site, local and live. You need to make some changes, so you implement hook_update_N. Since this is the first time you have done this, the number you use is 7000. You then deploy your Site Deployment module, and any other code changes, to your live site. You then run the Drupal updates either by going to update.php or running drush updb. This will then execute the update function with 7000.

The next time you want to make a change, you create a new function with the number 7001. You deploy the code to your live site and run the Drupal updates again. Drupal looks at the schema version in the system table for your Site Deployment module and sees that 7000 has run but 7001 has not. So it goes ahead and executes 7001.

Advantages of the Site Deployment Module

  • Because changes are in code and are not made manually in an admin form, it can be put under version control along with the rest of the code base.
  • Each site in your pipeline will “remember” the last deployment that was run on it by storing the last update increment in the system table. When you run Drupal Update on a each site, it will run all of the updates since the last one that was run.
  • By scripting all changes, you dramatically reduce the chances of human error. You do not need to replicate all that clicking and form saving. It also makes it much easier to test.
  • It will dramatically reduce the time it takes to make a deployment. Manually repeating all the manual changes on a production site on deployment day can take a substantial amount of time. Scripting all of it and running one command is insanely quick in comparison.
  • When working in a team environment, your fellow team members just need to do a git pull to get your code changes and then run the Drupal update. They will then get all of the database updates that you have made (and you will get theirs) without labour intensive manual updates or sharing databases locally.

Setting up the Site Deployment Module

Because this is a custom module, you can call it what ever you want. Common names are sitename_deployment or deploy_update. For this example, I am going to call it site_deployment.

Create your module with these three files:

  • site_deployment.info
  • site_deployment.module
  • site_deployment.install

The info file

The info file should contain the following:

  
name = Site Deployment
description = Deployment module, used to automated changes to this site
core = 7.x
version = "7.x-1.x-dev"

The module file

The module file doesn’t need to contain any working code. We will simply include a comment.

<?php
/**
 * @file
 * Module file for Site Deployment
 */

The install file

In the install function, you can write an implementation of hook_update_N for each deployment. This is the heart of the site deployment module.

Let’s take a look at the code for the first function.

<?php
/**
 * @file
 * Install file for Deploy Update
 */

/**
 * Deployment function for 1st deployment
 */
function site_deployment_update_7000() {

}

So what goes in each function? Let’s look at some examples of four different deployments.

Deployment One

Let’s say that with the first deployment, you want to enable the Contact and Blocks modules and disable the Devel and Coder modules.

You just need to add three lines of code to do that:

/**
 * 1st Deployment: enable contact, book; disable devel, coder; enable garland;
 */
function site_deployment_update_7000() {
  module_enable(array('contact', 'book'));
  module_disable(array('devel', 'coder'));
  theme_enable(array('garland'));
}

The first calls the core module_enable function, which takes an array of modules that you want to enable. In this case, the modules to enable are Contact and Book. The second calls the core module_disable function, which takes an array of modules you want to disable. In this case, the modules to disable are Devel and Coder. And the third line calls the core theme_enable function, which takes a list of themes you want to enable. In this case, the theme to enable is Garland.

Deployment two

In the second deployment, you want to do two things: change the settings for a block and change the path for the site frontpage.

/**
 * 2nd Deployment: update system navigation block; set new path for frontpage;
 */
function site_deployment_update_7001() {
  db_update('block')
    ->fields(array(
        'region' => 'sidebar_second',
      )
    )
    ->condition('module', 'system')
    ->condition('delta', 'navigation')
    ->execute();

  variable_set('site_frontpage', 'node/1');
}

The first chunk of code is a db_update to change the navigation block that comes from the system module and set its region to sidebar_second.

Before the change, the navigation block was in sidebar_left:

After update is run, the navigation block is in sidebar_second:

The final line of code changes the path of the default frontpage. You would normally do this in the site information page. This then saves it to the variable table. You can bypass using the site information form and set the variable directly in code. To do this, use the variable_set() function. This takes two arguments, the name of the variable and the value you want to set for that variable.

The variable for the frontage is site_frontpage. This update sets the site_frontpage to node/1.

Before the change, the site frontpage field is blank:

After the update is run, the site frontpage field is populated with the new path, node/1:

Deployment Three

In the third deployment, we add a new link to the main menu.

/**
 * 3rd Deployment: Add Register link in the main menu
 */
function site_deployment_update_7002() {
  $item = array (
    'link_title' => 'Register',
    'link_path' => 'user/register',
    'menu_name' => 'main-menu',
    'weight' => 0,
    'plid' => 0,
    'weight' => 11
  );
  menu_link_save($item);
}

This will add a new menu item with a title of Register. It will link to the detail user registration path and will be added to the Main Menu.

Deployment Four

In the fourth deployment, we want to revert a feature that has become overridden. Features become overridden when one of the settings changes in the database and it the Feature code has not been updated. You will want to revert it when you want to remove the database change and revert it back to what is stored in code.

/**
 * 4th Deployment: Revert News feature
 */
function site_deployment_update_7003() {
  features_revert(array('news' => array('field')));
}

To revert a feature, you call the features_revert function. It takes a multidimensional array as its argument. This array contains the feature and components to revert. In this case, we are reverting the field component of the news feature.

Wrapping Up

Automating all changes for a Drupal site is an essential element in creating robust, risk free and testable deployments. In this introduction, we have looked how to set up your own site deployment module and some examples of this in action.

Example Module on drupal.org

I have set up an example module on drupal.org. This contains the code outlined above. Over time, I will add more examples which you can use for inspiration.

Further Information

If you would like more information on using the Features module, check out my book Master Drupal Module Development, which includes two chapters on using the Features module.