Drupal Continuous Integration: Get Hooked Up
Drupal Developers Guide to Better Hook-ups
Continuous Integration is not a single method or a single technology that does it all for us. There are movers like Github, Bitbucket, or Gitlab that allow the developers to move their code changes. There are shakers like TravisCI or Jenkins that shake a copy of the website to test that the website still works. Then there are doers, that actually carry out any Drupal configuration or data changes on the website. It is the harmony of movers, shakers and doers that bring it all together to make Continuous Integration work effectively. But let’s not forget, no matter how smooth this harmony, it usually comes down to just one person deploying the code release who is left to answer the question, “Did it work?” That person, the deployer, let’s call him Vlad… Vlad the Deployer, has to know what happened, because Vlad is the one who has to decide whether to roll-back the site to what it was 2 minutes ago, or call it a success. At deployment time, Vlad is not dependent on the movers, or the shakers. Vlad is dependent on the doers. He needs to know if the doers were successful.
In Drupal, the doers come in three varieties. The simplest is a list of steps that Vlad has to carry out by hand (the shakers hate those because they can’t do the steps and therefore can’t test the end results). The second is a list of drush commands that Vlad has to run in order (the shakers can’t run those either). The third is a collection of hook_update_N() functions, that carry out the commands when you run a Drupal update (drush updb). They can be used to enable modules, revert Features, change configuration settings and anything else that could be done by hand. Update hooks offer pretty simple ways to inform Vlad what they are about to do and what they did. However, care has to be taken to make sure the update hooks don’t give Vlad faulty information. I presented a session at Florida Drupalcamp last week entitled “Look Ma, No Hands … Deployment” that was simply about how to make update hooks for Drupal deployments that follow the four tenets of good update hooks:
- Do something.
- Verify it was done.
- Give accurate feedback and log it.
- Fail the update if the operation was not successful.
It’s 2 a.m. do you know what your update hook is doing?
The majority of update hooks I see in use for Continuous Integration give all their attention to #1 and fail to address 2, 3 and 4. It is not enough to call module_enable() in an update hook and assume that it enabled the module. The module might not be in the codebase, or it might have an unmet dependency. Either way, module_enable would fail silently. Vlad would go on thinking that the deployment was a success. So it is better if the update hook checks to see if the module actually ended up being enabled. Then you have to craft two different messages; one for if it failed and one for if it succeeded. Then you have to log those messages and throw an update exception if it failed. Here is an example of having an update hook enable a module while following the 4 tenets.
/**
* Enable module Google Analytics. (This tells what's going to happen.)
*/
MY_MODULE_update_7001(&$sandbox) {
$module = ‘google_analytics’;
// Tenet 1: Do what you said you were going to do.
$success = module_enable(array($modules));
// Tenet 2: Verify it was done but from success and double check that it is actually enabled.
if ($success && module_exists($module)) {
$msg = “Google analytics was enabled.”;
}
else {
// This module is not enabled, throw an exception.
// Tenet 3: Give accurate feedback and log it.
$msg = “The module ‘google_analytics was supposed to be enabled by this update, but was not. Please investigate the problem and re-run update.”;
watchdog('deploy', $msg, array(), WATCHDOG_ERROR);
// Tenet 4: Fail the update if the operation is not successful.
throw new DrupalUpdateException($msg);
}
// Tenet 3: Give accurate feedback and log it.
watchdog('deploy', $msg, array(), WATCHDOG_INFO);
// Whatever is returned gets output to the terminal or update.php.
return $msg;
}
As you can see, good communication in an update hook takes time and practice … unless of course you use Hook Update Deploy Tools. Because good communication is so important for our team work, test deployment, and our actual deployments, we built a module that wraps many of the common things you would have an update hook do, in the 4 tenets. This sample hook_update does exactly the same thing as the one above
/**
* Enable module Google Analytics.
*/
MY_MODULE_update_7001(&$sandbox) {
$msg = HookUpdateDeployTools\Modules::enable('google_analytics');
return $msg;
}
Currently Hook Update Deploy Tools is capable of applying the 4 tenets of good update hooks to the following tasks*:
- Enable, disable, or uninstall modules
- Revert Features
- Delete node fields (instances and bases)
- Update node properties and simple field values
- Update node aliases
- Set Drupal variables {alter site config}
- Create / update Menus from menu import file
- Create / update Rules from a rule import file
- Provide messaging, logging and exception handling of for your own custom hook update events.
For developers at the command line it can also:
- Generate a custom site_deploy module
- Export Rules with Drush
- Lookup or set the hook_update_N numbers for testing purposes.
*For the most up-to-date list, consult the module page as we are constantly teaching Hook Update Deploy Tools new tricks.
Keep in mind that using Hook Update Deploy Tools is not just a help for Vlad, it also helps developers. When a developer pulls new code from the repo and runs the updates, they are informed about what’s coming in, what changes are happening, and what changes failed. When the shakers (like Jenkins) start alarming that a test is failing, a developer can look at the terminal output captured by the shaker and see what happened when the update hooks ran. When Vlad deploys to the live site in the middle of the night, all he has to do is copy his terminal output and send it to the rest of the team so they know the results of the deployment. It’s all about good communication.
Hook Update Deploy Tools in Action
Pull down a copy of Hook Update Deploy Tools (drush en hook_update_deploy_tools) and use it to harmonize your code deployments.
If update hooks are a new concept for you, or you want to see more specifics of how this works, take a quick run through the slides from my presentation “Look Ma, No Hands … Deployment” below.