Gitプッシュでブランチごとにステージング環境生成or本番反映させる


概要

「masterをプッシュで本番反映」をやっている記事はいくつかあったものの、ブランチごとに自動でステージング(テスト環境)が作られるような仕組みは見当たらなかったので書いてみる。

マージ未定の実験的なブランチをWeb上で確認したり、ローカル環境のないディレクターやデザイナーに作業ブランチの内容を確認してもらう際に役に立つはず。

Gitフック経由のサイト更新はFTPもサーバーへのログインも必要なく、ただpushするだけなので非常に便利だと思う。

※以下、ある程度のサーバーサイドの知識を要する説明になっているので注意

最終的に実装する機能

  • masterをリモートリポジトリにpushすると、masterの内容が本番サイトであるhttp://プロジェクト名.com/に取り込まれる。

  • hogeというブランチを切ってプッシュすると、http://hoge.プロジェクト名.com/というhogeブランチの内容が反映されたステージングができあがる。

  • hogeブランチを削除するとステージングも消える。

これらを、gitのサーバーサイドフック(シェルスクリプト)+Apacheの設定で実装する。
他に何かのサービスを利用したりインストールしたりは特にない。

前提条件

  • 対象サーバーにgitリポジトリと本番環境・ステージングが同居している ※1
  • 「*.プロジェクト名.com」のDNSレコードが対象サーバーに向いている

※1 git hubなど外部のリポジトリを利用している場合、各サービスのwebフックなどを利用し、本稿で作るgitフックをHTTP経由で発火させる必要がある。

完成後のディレクトリ構成

var
 └www
   ├git
   │ ├プロジェクトA.git ←リモートリポジトリたち
   │ └プロジェクトB.git
   │
   └html
     ├プロジェクトA
     │ ├master ←プロジェクトA.gitのmasterブランチをクローンしたもの
     │ └develop ←プロジェクトA.gitのdevelopブランチをクローンしたもの
     │
     └プロジェクトB
       ├master
       ├develop
       └etc...

masterやdevelopに各ブランチのソースが入りweb公開される。

実装

下準備

以下についての手順は省略する。

  • サーバーにApacheとgitをインストールする。
  • サーバーにgitリポジトリを作成し、ローカルから利用できるようにする。
  • /var/www/htmlにプロジェクトディレクトリを作成する。※2
  • プロジェクト名.com*.プロジェクト名.comのDNSレコードをサーバーに向ける。

※2 この配下のmasterやdevelopといったディレクトリは勝手に生成されるので作成不要

手順1:Apacheを設定

「ブランチ名.プロジェクト名.com」でアクセスされた際に「/var/www/html/プロジェクト名/ブランチ名」を参照するように設定する。
また、ブランチ名を省略して、「プロジェクト名.com」でアクセスされた場合はmasterをみるようにする。

VirtualDocumentRootを使うので、mod_vhost_aliasが無効になっている場合有効にしておく。

/etc/httpd/conf/httpd.conf
LoadModule vhost_alias_module modules/mod_vhost_alias.so
# 場所は環境によって異なる

バーチャルホストの設定ファイルを開き、以下の設定を追加する。

http://ブランチ名.プロジェクト名.comのようにプロジェクト専用のドメインを取得している場合、

/etc/httpd/conf.d/vhosts.conf
<VirtualHost *:80>
    ServerName プロジェクト名.com
    ServerAlias *.プロジェクト名.com
    VirtualDocumentRoot "/var/www/html/プロジェクト名/%1"
</VirtualHost>
<VirtualHost *:80>
    ServerName プロジェクト名.com
    ServerAlias プロジェクト名.com
    DocumentRoot "/var/www/html/プロジェクト名/master"
</VirtualHost>

http://ブランチ名.プロジェクト名.ドメイン名.comのように、サブドメインへ複数のプロジェクト名を動的に設定したい場合、

/etc/httpd/conf.d/vhosts.conf
<VirtualHost *:80>
    ServerName ドメイン名.com
    ServerAlias *.*.ドメイン.com
    VirtualDocumentRoot "/var/www/html/%2/%1"
</VirtualHost>
<VirtualHost *:80>
    ServerName ドメイン名.com
    ServerAlias *.ドメイン.com
    VirtualDocumentRoot "/var/www/html/%2/master"
</VirtualHost>

記述後、apacheを再起動する。

手順2:gitのサーバーサイドフックを記述

サーバーサイドフックは、リモートリポジトリへのプッシュをトリガーに発火する処理。
今回は、/var/www/git/プロジェクト名.git/hooks/内にpost-updateというファイルを作成する。

post-updateはプッシュされた際にブランチごとに発火する。その際「refs/heads/master」のような引数が設定される。

今回シェルスクリプトでやること、
1. 引数を元に、git rev-parseでプッシュされたブランチ名を取得する
2. git rev-parseに失敗(結果と引数がイコール)した場合ブランチ削除とみなす
3-A. ブランチ削除の場合、対応するステージングを削除
3-B. ブランチに対するステージングが存在しないならクローンして作成
3-C. 既に存在する場合はクローン済みのステージングをプル

/var/www/git/プロジェクトA.git/hooks/post-update
#!/bin/bash

repo=/var/www/git/プロジェクト名.git
dir=/var/www/html/プロジェクト名/
name=$(git rev-parse --symbolic --abbrev-ref $1)

# delete chk
delete=0
if [ $name = $1 ]; then
  name=${name#refs/heads/}
  delete=1;
fi

# pull or clone or delete
if [[ $name =~ ^[a-z0-9-]+$ ]]; then
  if [ $delete = 1 ] && [ $name != master ]; then
    echo $dir$nameを削除
    rm -rf $dir$name
  elif [ -e $dir$name ]; then
    echo $dir$nameをプル
    cd $dir$name
    git --git-dir=.git pull
  else
    echo $dir$nameをクローン
    cd $dir
    git clone -b $name $repo $name
  fi
fi

※ハイフン以外の記号や大文字が含まれるブランチは無視する
 (つまりステージング不要なブランチは「feature/hoge」のような名前にすればよい)
rm -rfしている箇所もあるので実装は慎重かつ自己責任で。

フックには実行権限を付与する。

$ sudo chmod +x /var/www/git/プロジェクトA.git/hooks/post-update

手順3:パーミッションの設定

プッシュするユーザーが「git配下の書き込み権限」と「html配下の書き込み権限」を持っている必要がある。

gitリモートリポジトリをhttps経由で利用する場合:

プッシュもフックの実行もapacheが行うことになるので、各ディレクトリの所有者をapacheにしておけばよい。

$ sudo chown apache:apache -R /var/www/git
$ sudo chown apache:apache -R /var/www/html

gitリモートリポジトリをSSH経由で利用する場合:

ログインユーザーがプッシュやフックを実行することになる。
好みにもよるが、例えば各ディレクトリの所有ユーザーとグループをapacheに設定しておいて、プッシュするユーザーをapacheグループに追加するなど。

$ sudo chown apache:apache -R /var/www/git
$ sudo chown apache:apache -R /var/www/html
$ sudo chmod g+w -R /var/www/git
$ sudo chmod g+w -R /var/www/html
$ sudo usermod -aG apache ユーザー名

確認

プッシュ後、設定したURLでそのブランチの内容が閲覧できることを確認する。

ステージング削除の際は下記コマンドでリモートブランチを削除する必要がある。

$ git push origin :ブランチ名
# ブランチ名の前のコロンを忘れずに

恐らく、SourceTreeなどのgitクライアント経由でリモートブランチを削除しても、ステージングは削除されないので注意。