使用Gulp+Git自动部署Azure Web站点
2016年5月31日

我之前使用过 Visual Studio 来部署 Azure Web 站点,VS 和 Azure 的高度集成使网站部署变得十分简单。然而我另一个站点是通过 Gulp 自动构建静态页面、文件的,所以我希望能通过一条命令使 Gulp 自动完成网站的部署,如:gulp deploy。Azure Web 站点的部署方式有很多选择:Visual Studio、Git、Powershell、FTP、手动部署等。在权衡了利弊之后,我决定采用 Gulp+Git 来实现自动部署站点。每个 Azure Web 站点都允许创建一个 Git 仓库作为部署源,不过这个选项默认是关闭的,Local Git Deployment to Azure App Service介绍了如何从管理界面中开启 Git 仓库以及如何通过 Git 命令将本地站点发布到 Azure 上。本文将重点介绍如何使用 Gulp 将文中的手动部署操作转为自动化脚本。

在阅读了 Azure 官方的教程后,我们知道将一个本地的 wwwroot 文件夹发布到 Azure Web 站点上大致需要以下几步:

git init // 将文件夹初始化成git存储库
git add -A // 将文件添加到存储库
git commit -m "First deployment" // 提交变更
git remote add azure https://<username>@localgitdeployment.scm.azurewebsites.net:443/localgitdeployment.git // 将当前存储库与云端Git存储库关联起来
git push azure master // 将本地变更推送到云端Git存储库

以上这些步骤都应按序依次执行,然而 Gulp 中的每个 Task 执行都是异步的。我们可以通过如下方式使 deploy task 依赖于另外两个 task:onetwo

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

当运行 gulp deploy 时,gulp 会先后触发 onetwo 这两个 task。不过这里有个问题:当 onetwo 这两个 task 为异步时,我们并不能保证 task two 在 task one 完成后才执行。比如:task one 用了2秒而 task two 只用了1秒,我们就会得到下面的结果。

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.

这并不符合我们的预期,我们必须确保 task one 执行完毕后再执行 task two ,所以我们需要让 task two 依赖于 task one,并且添加 callback() 回调函数来确保 task two 只在 task one 完成后才触发。而我们的入口 task deploy 只需要依赖于最后一个 task 即可,最后一个 task 依赖于前一个,以此类推,一环扣一环。当执行 gulp deploy 时,依赖链最上游的 task 会被触发,然后依次执行到最后一个 task。结合实际的部署需求,大致代码如下:

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();
    });
});

具体的内部实现就不赘述了,我用的是 child_process.exec() 来执行git命令,也可以用 gulp-gitsimple-git 等这些封装好的组件,但灵活性不是很好。