Deploying Drupal Updates with Gulp
Let's breakdown the deployment process for a Drupal site.
- Place site in maintenance mode
- Upload new files
- Run update.php
- Take site out of maintenance mode
- This is the kind of thing Gulp was made for.
Introducing Gulp
Gulp automates tasks. A task might be to compile SASS files, minify JavaScript, or upload files. The tasks for the project are defined in gulpfile.js using the following structure.
gulp.task('task-name', function() {
// Do stuff
});
We can now run our task from the command line.
gulp task-name
Often tasks will need to happen in a specific order. For example, we wouldn't want to upload any CSS if we haven't yet compiled our SASS files. To enforce this order we list the tasks that need to happen first (referred to as "dependencies"). Before a task runs, Gulp will first run the task's dependencies.
gulp.task('task-name', ['this-task-first', 'and-this'], function() {
// OK, now our turn
});
It's worth noting that Gulp will run tasks in parallel. Assuming the dependencies don't have any dependencies of their own, they will run at the same time.
Task 1: Place site in maintenance mode
To place our site in maintenance mode, we'll SSH into the server and use Drush. Gulp can do this for us, but we will first need to install the Gulp SSH module.
npm install gulp-ssh --save-dev
Add the following to gulpfile.js.
var ssh = require('gulp-ssh')({
sshConfig: {
host: 'yourdomain.com',
username: 'chris',
privateKey: require('fs').readFileSync(
process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'] + '/.ssh/id_rsa'
)
}
});
The module does not read your .ssh/config file, so you will need to specify your private key, and port if applicable. If you're not using SSH keys, take a moment to set them up.
gulp.task('offline', function() {
return ssh.shell([
'cd /path/to/your/website',
'drush vset maintenance_mode 1'
]);
});
Task 2: Upload new files
To upload the new files, we'll use rsync. If you're not familiar, rsync is a utility to keep files on two separate systems in sync. It's better than FTP for our case as it will only upload files that have changed, and delete files that have been removed.
There is a Gulp rsync module.
npm install gulp-rsync --save-dev
The following task will sync the "htdocs" folder in our project, with the destination folder on the server. We want to leave the settings file and the files folder on the server alone. If we don't, any files uploaded through the Drupal site, or files dynamically generated by the site, will be deleted.
var rsync = require('gulp-rsync');
gulp.task('rsync', ['offline'], function() {
return gulp.src('htdocs')
.pipe(rsync({
root: 'htdocs',
hostname: 'yourdomain.com',
username: 'chris',
destination: '/path/to/your/website',
recursive: true,
clean: true,
exclude: [
'.git',
'sites/default/settings.php',
'sites/default/files/'
]
}));
});
Step 3: Run update.php
We'll use Drush again to apply any updates to the database. (This is the same as loading update.php in your browser.)
gulp.task('update-db', ['rsync'], function() {
return ssh.shell([
'cd /path/to/your/website',
'yes | drush updatedb'
]);
};
##Step 4: Take site out of maintenance mode
The task to take our site out of maintenance mode is almost the same as placing it in maintenance mode. We of course need to wait for the database update to complete.
gulp.task('online', ['update-db'], function() {
return ssh.shell([
'cd /path/to/your/website',
'drush vset maintenance_mode 0'
]);
});
Piecing it Together
Lastly we'll create a single deploy task that runs all of the above tasks.
gulp.task('deploy', ['offline', 'rsync', 'update-db', 'online']);
Since each step is the process is dependant on the previous, we could have listed "online" as the only dependency. I believe listing all of the steps makes it clearer to the reader what will happen when we run the task.
You could, and should, include a step 1a which performs a backup of the website and database. This could be done with the SSH module and a few bash commands. (Hint: "drush sql-dump" will export the Drupal database.)