Introducing Incremental Deploy
I've spent a good portion of the past several months working on a content deployment solution for one of our clients at Work [at] Play. The system is built on top of heyrocker's deploy/services paradigm but needed to expand on it in two important ways:
- We needed the ability to deploy things other than core entities, e.g. nodequeue contents
- We needed the workflow to be: "Deploy everything that's changed since the last deployment"
What I came up with was Incremental Deploy module and I have just released a beta version at http://drupal.org/project/incremental_deploy.
The module automatically adds changes to the current active plan, as they happen in the source environment. This includes changes to variables and block configurations, as well as the items already taken care of by Deploy module, such as nodes, users, comments and taxonomy terms. There are submodules that deal with the deployment of the following:
- Nodequeue contents (i.e. nodes added, removed or rearranged)
- Global flags (such as when an editor marks an item as "featured"
- Meta tags (as provided by the nodewords module
Knowing when content has changed
How can we tell when content has changed on a Drupal site? When a user submits a form? When a hook gets fired? When it changes in the database? Unfortunately, the answer varies depending on the type of entity involved as Drupal has as yet no standardized interface into pieces of content. So the various entities that get scheduled for deployment on change get triggered in one of three ways:
- api hooks (this works for items like nodes, nodequeues, global flags)
- hook_form_alter (for capturing when a new language has been added or enabled)
- database triggers
This last method is a bit of a hammer approach and is currently used for the variables, blocks and nodewords tables. I'm not completely confident of the wisdom of this decision but it made sense at the time and, well, it works.
A note on Nodequeue deployment
We didn't feel it necessary to be able to deploy new nodequeues across environments, as we generally define our nodequeues in code. But deployment of Nodequeue contents from one environment to another depended on the ability to uniquely identify nodequeues across environments, and for this we needed machine names. My co-worker, Owen Loy (a.k.a. mundanity) submitted a patch to Nodequeue module, which was committed very recently and so machine names currently only exist in the 6.x-2.x-dev branch of the module. So, the set-up is: you use the latest dev version of Nodequeue, you have your nodequeues defined in code, with machine names of your choosing, replicated across all environments, and you can push the contents of your queues up the chain.
Post Deployment
Incremental Deploy automatically adds a "post deploy tasks" item to the end of every plan it deploys. What this does on the remote is invoke a hook that allows other modules to act on the fact that a deployment has just taken place. We used this for the purpose of clearing caches other than the Drupal cache, e.g. Varnish, xcache, akamai.
What about Drupal 7?
Um, good question. It does feel odd to be releasing a shiny new module to the community and for it only to be available for Drupal 6 when all anyone cares about these days is Drupal 7. Well, surely that's not really the case - many of us will have Drupal 6 sites to maintain for some time. But as Incremental Deploy depends on Deploy module, the question of whether this functionality will ever be available in D7 hangs on whether Deploy module gets ported. I'm not sure if anyone currently has plans to do so. I'd love to jump up and say "Sure, I'll do it!" but my gut feeling is that it will be a non-trivial undertaking, and so I'm afraid it would suck more of my time than I can realistically spare.
In the meantime...
But for those who are still interested in the whole area of content deployment, albeit only for Drupal 6, stay tuned for my next post, where I'll discuss some of the biggest gotchas we came up against in doing deployments involving things like multi-language content, complex custom cck fields and more.