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が無効になっている場合有効にしておく。

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

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

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

```apache:/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`のように、サブドメインへ複数のプロジェクト名を動的に設定したい場合、

```apache:/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. 既に存在する場合はクローン済みのステージングをプル

```bash:/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`している箇所もあるので実装は慎重かつ自己責任で。

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

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

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

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

###gitリモートリポジトリをhttps経由で利用する場合:
プッシュもフックの実行もapacheが行うことになるので、各ディレクトリの所有者をapacheにしておけばよい。

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

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

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

```bash
$ 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でそのブランチの内容が閲覧できることを確認する。

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

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

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