Drupal Deployment with a Git post-receive hook
Drupal deployment and maintenance often proves challenging when teams of developers are involved in a given Drupal project. A great way to reduce deployment pains is to use a distributed version control system, and integrate deployment efforts in your DVCS. As Drupal core is using Git, I am focusing on Git as a DVCS and using Git’s hooks to handle deployment. I’ll also point out that the Git hook deployment technique discussed here works for deploying many (most) web sites, but I am going to add details on drush and features modules, and get more specific to Drupal eventually (hence the title).
There are a lot of different ways to host Drupal, and I’m going to gloss over pros/cons and many of the techniques here (it’s another post). For now, I assume the following:
- A web server capable of serving PHP is running.
- You have server(s) representing at least staging and production (and development on a local machine).
- Git is installed and configured locally and installed on your server(s).
I am going to demonstrate with the fake domain: drupaldeploy.com. I assume you either have DNS configured or hosts file entries to direct drupaldeploy.com and staging.drupaldeploy.com to the same server. On that server you have users ‘git’ and ‘drupaldeploy’. Your ssh public key (and keys for anyone who needs to deploy) is present in the /home/git/.ssh/authorized_keys file. I assume the ‘git’ user can write to the webroots and they are:
- drupaldeploy.com: /home/drupaldeploy/drupaldeploy.com/html/
- staging.drupaldeploy.com: /home/drupaldeploy/staging.drupaldeploy.com/html/
I assume your local git repository is initialized and populated with In your git repository, I am going to assume you are creating multiple branches and not working solely in master. I assume you have at least:
- master – this is production
- staging – this is for staging (almost ready for production, but likely needs review or QA)
- development – where most of your development work occurs
- etc. – feature branches and bug fixes (use these to keep the other three highly organized and relatively stable)
This certainly isn’t the only option for branching techniques, but it’s well organized and manages to keep important branches safe. I’m basing this model predominantly on the branching model at nvie.com. Now let’s get to it.
Create a git bare repository on the server
ssh email@example.com mkdir ~/drupaldeploy.git cd ~/drupaldeploy.git/ git init --bare
Add a remote repository on local and push to the server
cd /your/local/repo/ git remote add drupaldeploy firstname.lastname@example.org:/home/git/drupaldeploy.git git push drupaldeploy master
Check to see what is being pushed, and deploy it to the appropriate location. On the server, create the /home/git/drupaldeploy.git/hooks/post-receive file (make sure it is executable by the ‘git’ user):
#!/bin/bash read oldrev newrev refname if [ $refname = "refs/heads/master" ] then echo "### Deploy production" GIT_WORK_TREE=/home/drupaldeploy/drupaldeploy.com/html git checkout -q -f master elif [ $refname = "refs/heads/staging" ] then echo "### Deploy staging" GIT_WORK_TREE=/home/drupaldeploy/staging.drupaldeploy.com/html git checkout -q -f staging fi
Now anyone on the team can make some changes to their local repository’s master or staging branch, pull from the bare repository to make sure they’re current, and then deploy updated code with:
git push drupaldeploy master
git push drupaldeploy staging
It’s easy to expand this technique to allow Drupal (and module) core installation via git. In some circumstances (particularly if you are hacking core), running Drupal and module maintenance updates through git can ease the burden of maintenance on heavily customized sites.
If you are using the Features module (hint: you should be), feature updates can be committed and imported/updated via careful commit locations and updates to your git hook. This is made even easier if you are using Drush…
If you are using Drush, you can do awesome things like importing/updating Features (i.e., check for file presence in some directory, and if present execute ‘drush content import –file %filename’), or automatically clearing the cache whenever a web root update occurs (via ‘drush cc’).
Depending on your server config, it may also be appropriate to restart services. There are permissions considerations to account for, but restarting Nginx, Apache, PHP-FPM, Varnish, etc. can be handled in Git hooks.