在使用 git 管理 Web 項目,以及其它 Server 項目時,我們需要自動部署功能。 在推送後,能將代碼部署到工程目錄,並執行一些部署命令。 這就用到了 git 爲我們提供的 post-update 鉤子。

   将脚本放在 git-dir/hooks 目录下,命名为 post-update,并增加可执行权限。 當遠端 git 倉庫對本倉庫發起推送,並推送成功後,該文件將被執行。 你可以用任何你喜歡的腳本語言 bash、python、ruby、julia、PHP、Node.js…… 當然,編譯型語言也行,只是不如腳本語言那样,便于阅读和修改。

#例 0.

   對於簡單的部署腳本,使用 bash 即可,例如:

#!/bin/bash
git --work-tree=/path-to/project-dir/ checkout "`git rev-list --tags --max-count=1`" -f >/dev/null

該腳本會將最新的 tag 檢出到工程目錄,是个常用的用例。

#例 1.

   這是一个 Laravel 项目的例子,使用 PHP 编写:

#!/usr/bin/env php
<?php

$product_dir= '/path-to/project-dir/';
$test_dir= '/path-to/test-dir/';

switch( $argv[1] )
{
	case 'refs/heads/migrate':
		`git --work-tree=$test_dir checkout -f migrate`;
		`$test_dir/artisan migrate:fresh --seed`;
	
	case 'refs/heads/test':
		`git --work-tree=$test_dir checkout -f test`;
		`composer --working-dir=$product_dir dump-autoload`;
		`$product_dir/artisan migrate`;
	break;
	
	case 'refs/heads/composer':
		`git --work-tree=$test_dir checkout -f composer`;
		`composer --working-dir=$test_dir update`;
		`$test_dir/artisan vendor:publish --all`;
		`git --work-tree=$test_dir checkout -f test`;
		`composer --working-dir=$product_dir dump-autoload`;
		`$product_dir/artisan migrate`;
		
		`git --work-tree=$product_dir checkout -f composer`;
		`composer --working-dir=$product_dir update`;
		`$product_dir/artisan vendor:publish --all`;
	
	case 'refs/heads/product':
		`git --work-tree=$product_dir checkout -f "\`git rev-list --tags --max-count=1\`"`;
		`composer --working-dir=$product_dir dump-autoload`;
		`$product_dir/artisan migrate`;
		`$product_dir/artisan view:clear`;
		`$product_dir/artisan route:cache`;
	break;
}

   此例中,我们检查了被推送的分支,并对不同的分支进行不同的操作。 当 test 分支被推送,将该分支检出到测试目录,并应用新增的数据库迁移文件。 当 migrate 分支被推送,将测试环境中的数据库刷新。 当 product 分支被推送,将最新 tag 检出到生产目录,并处理数据库迁移、视图和路由的缓存。

#例 2.

   這是本博客项目的部署脚本:

#!/usr/bin/env ruby

# 引入 git 的 API 庫 rugged (基於 libgit2),需要事先使用 gem 安裝
require 'rugged'

# 引入 jekyll,需要事先使用 gem 安裝
require 'jekyll'

git_repository= '/path-to/git-repository/'
project_dir= '/path-to/project-dir/'
web_root_dir= '/path-to/web_root_dir/'

repo= Rugged::Repository.new( git_repository, )

# 獲取 tag 列表
tags= repo.tags.map{ |tag| tag.target }

# 構建回溯器,以 master 分支爲起點進行回溯
walker= Rugged::Walker.new( repo, )
walker.push( repo.head.target, )

# 回溯獲取 master 分支上最新的 tag
latest_tag= walker.find{ |commit| tags.include?(commit) }

# 檢出此 tag 到項目目錄
repo.workdir= project_dir
repo.checkout( latest_tag.oid, :strategy=>:force, )

# 構建 jekyll 項目
config= Jekyll.configuration( { 'source'=>project_dir, 'distination'=>web_root_dir, }, );
site= Jekyll::Site.new( config, );
site.process

   與上面兩個例子使用 CLI 不同,本例使用 API 來操作 git 倉庫和構建項目。 這看起來略顯麻煩,但優勢也是明顯的。API 往往可以做得比 CLI 更多,也更加靈活。 這裏使用的 libgit2 就是非常棒 git API。它涵蓋了 git 的所有功能,支持十餘種編程語言或環境。 並且它還是 git 的官方推薦 API 庫。

   以上是我在項目中使用的不同風格的 git post-update 例子。 分享代碼,更是分享思路,希望對帶來參考價值。