Drupal deployments & workflows with version control, drush_make, and Aegir

It's the million dollar question. And it's rarely been answered because it's so darn hard to do so.

How to solve the dev / staging / live workflow problem in Drupal?

Today I'd like to tell you how I do it with my favourite deployment weapons: Aegir, Version control, and Drush Make.

This'll be a rather low-level article designed for power-users. For a more high-level overview of the benefits described, check out the recent blog post by Development Seed.

The Problem

While many or most of us are using version control to keep track of our code and so on, a problem still persists, and it is twofold:

1) A lot of important data gets stored in the database in many database-driven content management systems, such as Drupal
2) The transition of updates through version control to the site can be a royal PITA.

Recently 1) has pretty much been conquered thanks to Features, which provides the ability to export the 'mechanics' of a Drupal site from the database into module-like code, which can then be version-controlled.

Adrian Rossouw covered how Features can help solve the classic Dev / Stage / Production workflow problem in a great blog post. Features is one of those big guns we've all been waiting for. I mean, google 'development live workflow', and it's this post that is number 1.

With the rising popularity of the Aegir hosting system, and its ability to fire off sites like a proverbial machine gun, the other question is becoming even more important.

Because Aegir is this fantastic system for managing your sites, right? Of course, I would say that. But how can you keep a site under version control when Aegir's capable of making and manipulating sites almost on-the-fly with the click of a button?

The Solution

Enter: Drush Make

The answer has come in the form of Drush Make, which provides an extension to Drush, the command line tool I often refer to as 'apt-get' for Drupal.

What Drush Make does, is through the implementation of flat files (similar in appearance to .info files from modules), provide a mechanism to fetch data from practically any source you tell it to, and do $stuff.

This can include downloading Drupal core, as well as a bunch of contrib modules from drupal.org . However, it also supports the ability to fetch:

  • code from CVS repos, including tag-specific
  • ditto for svn
  • ditto for git, including checking out a particular branch after cloning the repo
  • grabs .tar.gz stuff, wget-style, useful for libraries
  • can fetch and apply patches

Sure, you say, that's great. It's basically doing batch drush dl's with some extra features/protocols thrown in. But how does this help with version control? Not only that, but how is that going to help keep this stuff under control while managing the site in Aegir?

Certainly for me, much of the issue has been that Aegir is inherently 'on the fly' with its actions. It has no concept of version control. But eventually I realised I had to stop expecting Aegir to handle this aspect, and realise that I could work with it by bringing version-controlled data to it for processing.

Or, put another way, I realised the very act of moving data around, does not need to be a version-control procedure in itself.

One must start to think like Aegir: and that is that the Platform and its application is the important data, that needs to be controlled. The site is just an instance of the application, and so long as your application is controlled, your site can be manipulated as an instance of it.

Adrian mentioned it in his podcast interview with Lullabot on October 15th:

It helps to think of install profiles as applications, and sites as instances of those applications.

If you can grasp that, you're on the home stretch already.

The Platform becomes the Platform Build.

One of the concepts I struggle with the most when training users on Aegir, is that of the 'Platform'. It turns out that many developers haven't been using Drupal's multisite design even prior to Aegir, despite the fact that the design's been around forever. So the idea of having multiple sites inside the /sites/ folder of Drupal core - in other words, sharing the core codebase between sites - is somehow (often, but not always) already enough of a paradigm shift.

In Aegir, one creates Platforms, which are just code bases, or a copy of Drupal core (or Open Atrium, etc). Only then can we create and manage Sites, which live on Platforms.

The analogy is directly like this:

/var/aegir/drupal-6.14

/var/aegir/drupal-6.14/sites/www.mig5.net

What makes this hard is that some users are still getting used to this idea, even though Aegir isn't doing anything different here. It's just exposing that multisite design of Drupal, by making it easier.

If you can grasp the idea of multisite, you'll find it easier to understand how Aegir works with your sites, and how a workflow can be developed to handle build management.

I mentioned Drush Make, and it's ability to grab the Drupal core.
And I mentioned shifting one's mindset to think of Install profiles as just 'Profiles', or 'Applications', and sites as instances of those Applications.

With this in mind, a bigger picture starts to form regarding the potential of Drush Make, since it can, in one fell swoop,

  • download the core,
  • fetch the install profile from your git repo
  • fetch some contrib modules
  • fetch some custom themes and modules from your other repos
  • maybe apply a patch against a module
  • fetch some extra third-party libraries, i.e jquery.ui, or geshi

No, really. Look at this (entry-level) example of mig5_net.build:

core = 6.x
projects[] =  drupal
projects[mig5_net][type] = "profile"
projects[mig5_net][download][type] = "git"
projects[mig5_net][download][url] = "git@git.mig5.net:/drupal/profiles/mig5_net"
projects[mig5_net][download][branch] = "build_2009101601"

Executable from the command line like this:

php /var/aegir/drush/drush.php make mig5_net.build /var/aegir/drupal-6.14_build_2009101601

My install profile or 'application' is the git repo that I'm cloning. Note the branch reference: after Drush_make 'git clone's it, it'll check out a specific branch, or 'build'. This is how this release is different from past and future builds: it's a specific release I'm building for.

Suddenly with this one execution of a single flat file, you've built a copy of Drupal core, and dropped in the profile or 'application' into its /profiles/ directory. That's a platform in Aegir's eyes, and not just any platform: but what I call a 'Platform build'. It's a specific release of a your application, right from the top of core, down to the last css file of your site's theme.

Hang on, .css file? Where's the contrib and all that? You only cloned the install profile from git

Yes, in the makefile above, I'm only grabbing the core and my install profile application. Here's the secret: it turns out Drush Make has a recursive nature! I tuck away another makefile inside my install profile repo, called mig5_net.make, and this is the real gem.

core = 6.x
projects[] = admin_menu
projects[] = captcha
projects[] = geshifilter
projects[] = install_profile_api
projects[] = pathauto
projects[] = recaptcha
projects[] = tagadelic
projects[] = token
projects[] = twitter
projects[] = views
projects[singular][type] = "theme"
projects[singular][download][type] = "git"
projects[singular][download][url] = "git@git.mig5.net:/drupal/themes/singular"
projects[tao][location] = "http://code.developmentseed.org/fserver"
projects[mig5][type] = "module"
projects[mig5][download][type] = "git"
projects[mig5][download][url] = "git@git.mig5.net:/drupal/modules/mig5"

What we have here is a series of contrib modules from drupal.org. Also I have a slightly hacked version of singular (css stuff), so I don't use Development Seed's feature server repo to grab this: I keep my own copy in my own git server. However, I *do* use the Tao base theme from their feature server, because I know, like contrib, I don't make any changes to it.

Alternatively, if I was smarter, I could have grabbed Singular from DevSeed's feature server, and written a patch for the css change, and asked Drush Make to apply the patch instead. Here's a snippet of such a thing, taken from the Managing News makefile in its install profile, where the Context module is patched:

; Patched.
; Explicit versions specified to ensure patches apply cleanly.
projects[context][subdir] = "contrib"
projects[context][version] = "2.0-beta7"
projects[context][patch][] = "http://drupal.org/files/issues/606816-1_node_form_context.patch"

...just so you don't think I'm making this stuff up :) Yes, such features exist, and are in active use!

So, this make file gets downloaded when the install profile git repo gets cloned and the build-specific branch is checked out. While Drush Make grabs the profile from the first makefile (I call it the .build file), it scans the directory and finds this make file, and recursively executes it - thus grabbing all the site-specific components in the process.

Time to tell Aegir about the new Platform build

Suddenly what I have is the entire platform, complete with any updated components I made in the makefile of that specific branch in git. At this point, I just have to tell Aegir about it!

So I go to Create Content > Platform in Aegir, give it a name (usually suffixed with the build number I give it as above), and tell it the path to the new platform I made (/var/aegir/drupal-6.14_build_2009101601)

Aegir goes and checks the platform, learns about it, adds the package information it finds within it to its database, and if all's well, gives me the big green tick.

But where's the settings.php? The site files? This isn't actually the *site*!

Exactly. It's only the application that we've built, and it's only the application we store in version control and generate with makefiles. The site still exists on the old platform, and Aegir knows it does. What we have to do here is stop thinking of the /sites/www.mig5.net/settings.php etc as the *site*, but rather, the current running 'instance' of the application.

It sounds a bit weird, doesn't it? But you don't realise, if you're already using Aegir, you're already probably doing this already. Drupal 6.14 came out recently, and your sites were on 6.13. You built a new platform (6.14, by downloading it), told Aegir about it, and you used the Migrate task to upgrade all your 6.13 sites to 6.14 at the click of a button.

We're doing exactly the same thing here: you've got your new platform, with your updates. Now you just need to Migrate the site onto that new build. Your settings.php and files from /sites/www.mig5.net/ get carried across to the new build, and any component code/database updates will get applied in the process.

Suddenly, you've just exercised the perfect workflow:

  • You kept the application, important stuff, in version control
  • You managed to keep *less* of a) in version control because drush make can fetch the contrib you don't hack, from drupal.org and other sources
  • You built the whole new release with just one command on the shell
  • You managed to seamlessly upgrade your site onto the new release, applying all the updates, and
  • you did that last one via Aegir, which means you were protected with the rollback functionality of Provision and Drush, meaning if it all went pear-shaped, you'd be back on the previous release as though nothing had happened.

This is how Aegir can cooperate with version control: all it needs from you is to get that Platform, or build, in place, so it can use it and take care of the release process for you.

And while it used to be difficult to build an entire distribution complete with all the nitty gritty components required, it's now never been easier to do this step in a matter of seconds, because Drush Make does this job for you.

This application, www.mig5.net, is literally two files in my git repository, plus the theme (I hacked on Young Hahn's Singular theme a bit). The two files are the install profile itself (which, of course, I only ever once: first time install of the site) and the mig5_net.make file, that does everything else.

Dealing with the dev > live process

This really packs a powerful punch when you're working on a development version of your site to make changes prior to sending live (and if you're not, you should be). The process above fits perfectly into this model. This is how one does it:

Need to make changes?

Dev

  • Make a new build using drush_make to grab the latest bits of everything. Add that platform to Aegir. I might call this build 'drupal-6.14_build_dev_2009102601'
  • Use the 'Clone' feature to clone your current Live site to the test build.
  • Make your changes to the dev site (maybe you are changing the theme, or adding a module)
  • Commit those changes back to the repo (the theme changes, maybe adding that new module as a ''project' in your drush_make file inside your install profile). Make a branch of the repo in preparation for a new live build.

Live

  • Make a new build using drush_make to grab the latest bits of everything (this grabs the changes you just committed). Add that platform to Aegir. I might call this build 'drupal-6.14_build_live_2009102601'
  • Use the 'Migrate' feature to migrate your live site to the new build, which picks up and applies any changes/upgrades

I shared some ideas with Adrian Simmons (@adrinux) who has taken the build/deployment logic I describe here and generated an absolutely fantastic workflow diagram showing the transition of data from dev to live, as well as the relationship between drush_make, version control, and Aegir. Here is the diagram - it does a hell of a better job explaining it all than me and my wordy ways :)

The status of Drush Make

Awesomeness.

No, you'd know by now that I'd say that, but that's actually the release notes for the 6.x-2.0-beta1 release, which Dmitri unleashed today, after an amazing night (well, my time) by Adrian Rossouw who obliterated a bunch of bugs after being given commit access. The Aegir project now depends on drush_make to build the frontend system and its relevant components, as of HEAD and the upcoming release 0.4-alpha3 (stay tuned for that very shortly). For this reason, we have a direct interest in the project and its future, and so we were lucky to work with Dmitri and squash a few issues very quickly - some of which were actually identified during the writing of this article :)

The recent changes to Drush Make include bringing it in line with changes in the also-recently-released Drush 2.1, adding support for checking out git branches after a git clone, and supporting absolute paths for destination of builds on the server.

Need to play with it some more to try it out? You can see examples of my 'build' makefiles in my git repo as well as my application install profile for mig5.net, which contains the 'bigger' drush_make file. Or check out the Managing News install profile and its make file - it's the first project other than Aegir to use a make file to build itself.

If you're new to Aegir, Drush and Drush make, I hope this article gives you the incentive to try it out and see why it rocks. If you're using Aegir and Drush already, I hope Drush Make makes more sense now, and that this gives you that 'ah hah!' moment if you've been scratching your head about how to handle build management in an Aegir environment.

(thanks to Adrian Simmons, Adrian Rossouw and Eric Gundersen for their contributions/ideas for this article, and most of all to Dmitri Gaskin for his mad skills.. thanks for changing the game with Drush Make!)

Tags: