Automate Azure Web App deployment using Gulp + Git

May 31, 2016

I have used Visual Studio to deploy Azure Website, the integration of VS and Azure makes web deployment very easy. However, I have another website that is built by Gulp automating tasks. And therefore, I was looking for a single command to deploy website using Gulp, like: gulp deploy. Azure offers many options to deploy Web App: Visual Studio, Git, Powershell, FTP, manual deployment. After weighing all the factors, I chose Gulp + Git to automate the deployment. Each Azure Web App allows user to create a Git repository as the deployment source. Local Git Deployment to Azure App Service shows how to enable the Git repository from Azure portal and how to deploy your app to Azure App Service from a Git repository on your local computer. This article will focus on how to automate the deployment using Gulp.

After reading the Azure official tutorial, we know that following steps are required in order to publish a local "wwwroot" folder to the Azure Web App.

git init // Initialize a new Git repository
git add -A // Add files to the repository
git commit -m "First deployment" // Commit the changes to the repository
git remote add azure https://<username>@localgitdeployment.scm.azurewebsites.net:443/localgitdeployment.git // Add remote reference
git push azure master // Push the content to Azure App Service

All the procedures above need to be executed in sequence. However, Gulp runs task asynchronously. We can use the code below to make task deploy depends on another two tasks: one and two.

gulp.task('deploy', ['one', 'two'], function () {
    console.log('Deploy finished.');
});

When running gulp deploy, Gulp would trigger task one and then task two. Here comes a question that when task one and task two are asynchronous, task two will run before task one completes. Following code illustrates the result when task one takes 2 seconds to complete while task two only takes 1 second.

gulp.task('one', function () {
    console.log('Task one started.');
    setTimeout(function () {
        console.log('Task one finished.');
    }, 2000);
});

gulp.task('two', function () {
    console.log('Task two started.');
    setTimeout(function () {
        console.log('Task two finished.');
    }, 1000);
});
[21:16:24] Using gulpfile D:\joji-utils\gulpfile.js
[21:16:24] Starting 'one'...
Task one started.
[21:16:24] Finished 'one' after 856 μs
[21:16:24] Starting 'two'...
Task two started.
[21:16:24] Finished 'two' after 846 μs
[21:16:24] Starting 'deploy'...
Deploy finished.
[21:16:24] Finished 'deploy' after 276 μs
Task two finished.
Task one finished.

This doesn't match our expectation, we need to ensure task two being triggered until task one completes. The solution is to make task two depends on task one and add a callback() function to ensure task two runs when task one completes. The deploy task needs to depend on the final task only, the final task depends on the previous one and so forth. When we run gulp deploy, the task on the top of the dependencies chain will run first, the subsequent tasks will then be triggered one after another. The final code looks as below:

gulp.task('deploy', ['git-push'], function () {
    console.log('Deployed the website successfully.');
});

gulp.task('git-push', ['git-remote-add'], function (cb) {
    asyncFunction('', function () {
        cb();
    });
});

gulp.task('git-remote-add', ['git-commit'], function (cb) {
    asyncFunction('', function () {
        cb();
    });
});

gulp.task('git-commit', ['git-add'], function (cb) {
    asyncFunction('', function () {
        cb();
    });
});

gulp.task('git-add', ['git-init'], function (cb) {
    asyncFunction('', function () {
        cb();
    });
});

gulp.task('git-init', function (cb) {
    asyncFunction('', function () {
        cb();
    });
});

I will not go into the details on how to implement the Git functions, I used child_process.exec() to execute git command. We can also use modules like: gulp-git and simple-git. They are basically wrappers of git commands with less flexibility.