Drush make and install profiles with Drupal 7

Packaging Drupal

Install profiles are core to our development process; everything from the data structure down to the smallest module settings are captured in code, using Features, Context and Strongarm and packaging everything using our beloved Drush make and install profiles. This approach has significantly helped us improve the quality and speed of our developments.

Why do we bet on that approach?

Relying on Drush make and install profiles is mainly a matter of best practice, ensuring we keep a clean separation between structure and data; at any point we can rebuild a fully functional site by simply using the installer. We usually build a separate importer (when needed) for data, most of the time using the awesome Feeds module (more about this in a future post).

Install profile support has been greatly improved in Drupal 7; not everything is as easy when using Drupal 6. However, the awesome Profiler module make that simpler, and with a bit of work, you should be able to reach a solid result.

Building our Drush make file

There are a few advantages to using Drush make;

  • You can make sure you keep track of what goes in the build; every single component of the site is clearly stated with a version number.
  • It allows us to retrieve resources beyond the regular Drupal.org modules; you can for example specify modules from Github, require external libraries and apply patches. That last one is especially useful when you build large scale or cutting edge websites that may need some patches that haven't made their way into a release yet, and gives the incentive or properly creating and sharing our patches directly in the issue queues on Drupal.org.

And there is much more to it. In practice, a make file simply is a list of resources we need to retrieve and combine together. For example, to retrieve the Boxes module, version 1.0 Beta 2:

projects[boxes][subdir] = contrib
projects[boxes][version] = 1.0-beta2

We can include other make files, avoiding to constantly start off of a blank slate. We systematically build our Drupal 7 distributions out of Build Kit:

includes[] = http://drupalcode.org/project/buildkit.git/blob_plain/dd1c740967b139a03002848bc1ec83e20ca929f7:/drupal-org.make

And as I said previously, we can also add patches and handle external resources (as in "not from Drupal.org"). We for example recently applied a patch to Reliefweb.int to address an issue we had with case sensitivity in some of our MySQL queries:

; #966210 - DB Case Sensitivity.
projects[drupal][patch][966210] = https://raw.github.com/gist/858251/1e3d2206821de8b1a4e7743be78d4da17795a034/06_15_966210_db_case_sensitivity.patch

Building the install profile

Now that we've put together the files (modules, themes, patches, libraries and core), we need to tell Drupal how to install things up; what modules to enable, what values to apply to settings... That's what the install profile is going to do (mainly). Install profiles are fairly similar to modules, with the main difference being the presence of a .profile file along with the regular .info file.

The .info file looks like any other;

name = "Example"
description = "Drupal 7 example install profile."
version = "7.x-1.0"
core = "7.x"

If you need to enable modules, you just need to add a dependency as you would do for a module:

dependencies[] = "dblog"

We can also perform a fair amount of simple tasks within the .install file, using the hook_install, again like any other module;

<?php

/**
 * Implements hook_install().
 *
 * Perform actions to set up the site for this profile.
 */
function profile_example_install() {
  // Disable user register.
  variable_set('user_register', USER_REGISTER_ADMINISTRATORS_ONLY);

  // Enable the admin theme.
  db_update('system')
    ->fields(array('status' => 1))
    ->condition('type', 'theme')
    ->condition('name', 'rubik')
    ->execute();
  variable_set('admin_theme', 'rubik');
  variable_set('node_admin_theme', '1');
}

?>

Now, if you need to perform some more complex tasks, say for example batch importing a set of taxonomy terms, you probably want to do so in the .profile file. These will effectively be run as batch operations and will be less likely to run into a PHP timeout.

We have a light example of what things look like when put together (Drush make + Install profile) on github, feel free to have a look and mess with it:

git clone https://github.com/Wiredcraft/example.git

What's next?

This is only one piece of the puzzle; there are quite a few other best practices we follow or developed that help us keep things in control. That approach is nonetheless at the core of all our development practices, and we rigorously stick to it whenever possible.

Next time we'll see how we handle the second part of the equation; content. What we've seen today delivers the structure, now the question is how do we get data into it?

Comments

Good! Good!

Makara, good paper! I'm recomending to read it all Drupal developers in my team. Thanks!
How would you handle a multi-site installation with the process like this? All sites share a most of the modules and are similarly configured.

Well depending on how different the sites are from each other, you should be able to use one common install profile. If need there is for overriding some variables or core components of your install profile, you could simply do so on the production or maintain the override in an include file for the settings.php files of your sites. If the sites are really far away from each other, you could maintain several distributions.

But how about configuration management with features when you have an installation profile providing all the features? Do you recreated those feature-generated modules and put them into site directories?

I would probably maintain these differences independently, either through features that are only enabled on each of the sites, or using overrides (using settings.php or some hooks like hook_strongarm_alter()). I recommend you have a look at Nuvole's presentation on Open Atrium customization; we really like these gus and their approach is very similar to ours.

Hey, I made this super easy to use tool for building Drush Make files. Just for Drupal 6 right now.

Actually, as Makara said in his post, inheritance (which is I guess make inclusion) is supported as of now. Moreover, your tool seems to act a simple interface to compose a Make file, which is not complex to start with but moreover remove the ability to tightly control versions and does not allow for leveraging the powerful few features of Drush make files (like patches, external libraries...).

Easily the best approach I've seen so far to integrating git and drush make into a professional Drupal workflow. New to git myself, I'm still trying to figure out how to lay out my repository to take advantage of these excellent new Drupal code export+management tools...and on that note, I wonder if you could elaborate upon the advantages that Buildkit affords your team. I've taken a look at its spartan project page, cloned the repo and had a poke about; clearly you've found it beneficial, but it's not clear to me how. Perhaps a couple of lines describing why you've found it useful?
The link to your example repository (https://github.com/Wiredcraft/example.git) is wrong. I needed to remove ".git": https://github.com/Wiredcraft/example
Would love to see the followup post about importing content. Thanks for the write up.
How are you?