Gitのブランチごとに確認環境を自動作成（Github、Backlog）

* Gitにpushしたら自動でサーバーに同期したい
* ブランチごとに確認できる環境を自動でwebに用意したい

# パターンA：サブドメインでブランチを分ける
## やりたいこと
こんな感じでそれぞれのブランチにアクセスできる環境を自動化したい。

```plaintext
http://ブランチ.プロジェクト.ドメイン
http://project.domain.com - masterブランチ
http://develop.project.domain.com - developブランチ
http://feature-1.project.domain.com - feature/1ブランチ
```

## 必要なもの
* DNS設定 - *.domain.comみたいなワイルドカード指定の必要がある
* Apache - じゃなくてもいいのかもだけど、今回、調べたのがApacheなので
* GithubやBacklogなどの設定 - リポジトリと、認証鍵の設定とか
* cloneとかpullするスクリプト - 今回はPHPからのシェルコマンドでやりました

## 方法
### 1. DNS設定
必要なものに書いたけど、とりあえずDNSで*.domain.comにアクセスできるようにする。
できない場合（権力とか武力とかが足りない場合）はパターンBの方を検討してください。
DNSの設定が活きるまでにしばらく時間がかかると思うので、先にやっておいた方が良さそう。

```plaintext
AレコードでサーバーのIPアドレスと「*.domain.com」を紐づける
```

### 2. サーバーの準備
以下の記事を参考にしました。
[Gitプッシュでブランチごとにステージング環境生成or本番反映させる](https://qiita.com/laineus/items/8c3add07e3154311b11d)

こんな感じのディレクトリが最後に完成します。
キモは、master/html、develop/htmlなどを、各プロジェクトで揃えておくこと。
ここが揃ってないとApacheのconfを毎回書くのでイマイチです。
最初は手動でディレクトリを作っておいてテストしたほうが楽かもしれません。

```plaintext:ディレクトリ構成
GithubやBacklog
  ├プロジェクトA.git
  └プロジェクトB.git

自サーバー内
  └var
    └www
      └html
        ├sync-git ←同期用のPHPを置くところ
        │ └master ←一応、他と同じに合わせておく
        │    └html
        │      ├git-ssh.sh ←鍵指定をするところ
        │      ├sync_github.php ←Githubからの受け付け用
        │      └sync_backlog.php ←Backlogからの受け付け用
        │
        ├project-a
        │ ├master ←プロジェクトAのmaster
        │ │  ├html ←ここがmasterの公開ディレクトリ
        │ │  └cgi-bin ←お好みでcgi-binもここ
        │ │
        │ └develop ←プロジェクトAのdevelop
        │    ├html ←ここがdevelopの公開ディレクトリ
        │    └cgi-bin
        │
        ├project-b
        │ └...
        │
        ├...
```

あと、/var/www/htmlのユーザーをapache:apacheにして書き込みできるようにしておいてください。
apacheさんじゃない場合は以下のPHPとかで確認できると思います。

```php:ユーザーの確認
<?php echo exec('whoami'); ?>
```

#### Apacheの設定
/etc/httpd/conf.d/vhost.confあたりにVirtualHostを書きます。
既存の設定が違うところにあるかもなので、無かったら/etc/httpd/conf/httpd.confなども見てあげてください。

ちなみにcgi-binの指定がうまくいかない場合、/etc/httpd/conf/httpd.confなどに全共通のScriptAliasが書いてあるとダメみたいなのでコメントアウトすると良いです。

以下のようなVirtualHostとDirectoryを書きます。
これが共通設定みたいな感じです。

```apache:conf
## Project Alias
# branch
<VirtualHost *:80>
ServerName domain.com
ServerAlias *.*.domain.com
VirtualDocumentRoot "/var/www/html/%2/%1/html"
VirtualScriptAlias "/var/www/html/%2/%1/cgi-bin/"
</VirtualHost>

# master
<VirtualHost *:80>
ServerName domain.com
ServerAlias *.domain.com
VirtualDocumentRoot "/var/www/html/%1/master/html"
VirtualScriptAlias "/var/www/html/%1/master/cgi-bin/"
</VirtualHost>

# DocumentRoot
<Directory "/var/www/html/*/*/html/">
Options +Indexes +Includes +MultiViews +FollowSymlinks
AddOutputFilter Includes;Deflate html
DirectoryIndex index.php index.html
AllowOverride All
Satisfy Any
Allow from all
</Directory>

# Script
<Directory "/var/www/html/*/*/cgi-bin/">
Options +Indexes +Includes +MultiViews +FollowSymlinks +ExecCGI
AddHandler cgi-script .cgi
AddHandler cgi-script .pl
AddOutputFilter Includes html
AllowOverride All
Satisfy Any
Allow from all
</Directory>
```

個別のプロジェクト設定は、VirtualHostだと共通設定の前、Directoryなら共通設定の後の方が良いみたい。
この辺は理解が足りなくて試行錯誤の結果なので、分かる人はいい感じにしてください。
以下はプロジェクトAにBasic認証をかける場合。

```apache:conf
## プロジェクトA
# master
<VirtualHost *:80>
ServerName project-a.domain.com
ServerAlias project-a.domain.com
DocumentRoot "/var/www/html/project-a/master/html"
ScriptAlias /cgi-bin/ "/var/www/html/project-a/master/cgi-bin/"
</VirtualHost>

# branch
<VirtualHost *:80>
ServerName project-a.domain.com
ServerAlias *.project-a.domain.com
VirtualDocumentRoot "/var/www/html/project-a/%1/html"
VirtualScriptAlias "/var/www/html/project-a/%1/cgi-bin/"
</VirtualHost>

## Project Alias
...

## プロジェクトA
# DocumentRoot
<Directory "/var/www/html/project-a/*/html/">
...
AuthType Basic
AuthName "Development"
AuthUserFile /var/www/password/project-a/.htpasswd
Require valid-user
Satisfy Any
Order allow,deny
</Directory>

# Script
<Directory "/var/www/html/project-a/*/cgi-bin/">
...
AuthType Basic
AuthName "Development"
AuthUserFile /var/www/password/project-a/.htpasswd
Require valid-user
Satisfy Any
Order allow,deny
</Directory>
```

設定できたらconfigtestして、Apacheを再起動。

### 3. 同期用PHPの設置
とりあえず、今回使ったPHPは以下に置いておきました。
GithubとBacklogで、使うPHPが違います。
https://github.com/yukihirog/server-sync-git

これらを、
/var/www/html/sync-git/master/html
の中に置いてみます。

設定がうまくできていれば以下のようなURLでアクセスできるはず。
http://sync-git.ドメイン/sync_github.php
http://sync-git.ドメイン/sync_backlog.php
http://master.sync-git.ドメイン/sync_github.php
http://master.sync-git.ドメイン/sync_backlog.php

### 4. Webhookの設定

#### 鍵の準備
以下を参考にしました。
[apache のデフォルト実行ユーザ www-data で github や bitbucket にSSH 接続する](https://qiita.com/Ouvill/items/02faa51d04b76c60d942)

GithubもBacklogも鍵を使ってアクセスします。
なので、サーバーで鍵を作って、それぞれのユーザーに設定しておくと便利です。

```shell:シェルコマンド
$ cd /var/www/.ssh
$ sudo ssh-keygen -t rsa
$ cat id_rsa.pub
$ sudo vi config
```

```plaintext:/var/www/.ssh/config
Host github.com
    Hostname github.com
    IdentityFile /var/www/.ssh/id_rsa
    User git
    Port 22
```

```shell:シェルコマンド
$ sudo -u apache ssh -vT github.com
$ sudo -u apache ssh -vT スペース名@スペース名.git.backlog.com
```

/var/www/.sshディレクトリがない場合は作ってください。
ssh-keygenの設定はエンターエンターツターンツターンでいいはず。
最後のcatした後に公開鍵の文字列が出てくるのでコピーして登録します。
（プロジェクトごとのDeploy keyの扱いが大変なのでユーザーに設定してます。）

Githubだとログインして以下。
https://github.com/settings/keys

Backlogはスペースごとに、ログインして以下。
https://***.backlog.com/EditUserSshKey.action

#### Webhookの追加
GithubのリポジトリやBacklogのプロジェクトごとに以下の設定でWebhookを追加します。

```plaintext:Githubの場合
「リポジトリ ＞ Settings ＞ Webhooks」で以下の設定
Payload URL: http://sync-git.ドメイン/sync_github.php
Content type: application/x-www-form-urlencoded
Secret: sync_github.phpの「$SECRET_KEY = 'SECRET_KEY';」の中身
Just the push event.
Active
```

```plaintext:Backlogの場合
「プロジェクト ＞ プロジェクト設定 ＞ Webhook」で以下の設定
WebHook URL: http://sync-git.ドメイン/sync_backlog.php?space=スペース名
（スペース名は***.backlog.comの***の部分）
通知するイベント: Gitプッシュ
```

うまくいっていると、これで次のpushから動きます。
以降はプロジェクト作ったらWebhooksの設定をすればOKです。

#### うまくいかない場合
* Githubの場合は上記設定のページにあるRecent DeliveriesでResponseを見ることができます。
* PulledとかCloneとか言う割に、肝心のそこだけエラーチェックとメッセージが出ません。
* cloneされるはずなのにmasterとかのディレクトリができていない場合、だいたい鍵の指定ができてなくてcloneできてません。すみませんがその辺は弱いので「Github deploy key サーバー同期」などで検索してみてください。
* もしくはディレクトリの書き込み権限です。


# パターンB：CookieとRewriteRuleでブランチを分ける
## やりたいこと
/branch.phpとかにアクセスして、プルダウンでブランチ選択したら全体がそのブランチになるようにしたい。

ただし、プロジェクト選択とかcgiはうまくないかも。

## 必要なもの
* Apache - じゃなくてもいいのかもだけど、今回、調べたのがApacheなので
* GithubやBacklogなどの設定 - リポジトリと、認証鍵の設定とか
* cloneとかpullするスクリプト - 今回はPHPからのシェルコマンドでやりました

## 方法
### 1. サーバーの準備

パターンBでもパターンAとほぼ同じです。

```plaintext:ディレクトリ構成
GithubやBacklog
  ├プロジェクトA.git
  └プロジェクトB.git

自サーバー内
  └var
    └www
      └html
        └project-a
          ├git-ssh.sh ←鍵指定をするところ
          ├sync_github.php ←Githubからの受け付け用
          ├sync_backlog.php ←Backlogからの受け付け用
          ├branch.php ←ブランチを選択するためのページ
          ├master ←プロジェクトAのmaster
          │  ├html ←ここがmasterの公開ディレクトリ
          │  └cgi-bin
          │
          └develop ←プロジェクトAのdevelop
             ├html ←ここがdevelopの公開ディレクトリ
             └cgi-bin
```

#### Apacheの設定
/etc/httpd/conf.d/vhost.confあたりにVirtualHostを書きます。
以下のようなVirtualHostを書きます。

```apache:conf
<VirtualHost *:80>
ServerName domain.com
ServerAlias domain.com
DocumentRoot "/var/www/html/project-a/"

<IfModule mod_rewrite.c>
  RewriteEngine On

  # Cookieありの時のCGI
  RewriteCond %{REQUEST_FILENAME} !branch.php
  RewriteCond %{REQUEST_URI} ^/cgi\-bin/.*
  RewriteCond %{HTTP_COOKIE} (.*)BRANCH=([^;]+)(.*)
  RewriteRule ^(.*)$ /%2$1 [L]

  # Cookieありの時のCGI以外
  RewriteCond %{REQUEST_FILENAME} !branch.php
  RewriteCond %{HTTP_COOKIE} (.*)BRANCH=([^;]+)(.*)
  RewriteRule ^(.*)$ /%2/html$1 [L]

  # Cookie無しの時のCGI
  RewriteCond %{REQUEST_FILENAME} !branch.php
  RewriteCond %{REQUEST_URI} ^/cgi\-bin/.*
  RewriteRule ^(.*)$ /master$1 [L]

  # Cookie無しの時のCGI以外
  RewriteCond %{REQUEST_FILENAME} !branch.php
  RewriteRule ^(.*)$ /master/html$1 [L]
</IfModule>

</VirtualHost>
```

mod_rewriteを使って、Cookieに「BRANCH=develop」とか書かれていたら「/index.html」を「/develop/html/index.html」とかに読み変える設定をしています。

### 2. ブランチ選択用のPHPの設置
こんな感じのPHPを、プロジェクト/html/branch.phpとして設置します。

```php:branch.php
<?php
	$current = '';
	if (isset($_GET['BRANCH'])) {
		setcookie('BRANCH', $_GET['BRANCH'], time()+60*60*24*30*12, '/');
		$current = $_GET['BRANCH'];
	} else if ($_COOKIE['BRANCH']) {
		$current = $_COOKIE['BRANCH'];
	}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Branch Selector</title>
</head>
<body>
<p>現在のブランチ：<?php echo $current; ?></p>
<form>
<p>
<label>
ブランチ：
<select name="BRANCH">
<?php
	$files = scandir('.');
	foreach ($files as $index => $file) {
		if (is_dir($file) && $file != '.' && $file != '..') {
			if ($current === $file) {
				echo "<option selected>$file</option>";
			} else {
				echo "<option>$file</option>";
			}
		}
	}
?>
</select>
</label>
<input type="submit" value="決定">
</p>
</form>
</body>
</html>
```

### 3. 同期用PHPの設置
あとはパターンAと同じです。

### 4. Webhookの設定
あとはパターンAと同じです。