ある人曰く、
ある製品のホームページがあるんだけどさ、これまでバージョン管理すらしてなかったから、まずいと思ってとりあえずhtmlファイルを全部Gitリポジトリにぶち込んだんだよ。で、リモートリポジトリにpushするのと、FTPでレンタルサーバーにアップロードするので二度手間だから、pushしたら自動でアップロードされるようにしたいんだよね。
……うん。で、詳しく聞くと、要するに
- 社内のファイルサーバーに、Webサイトのリモートリポジトリを置いた。
- リモートリポジトリにpushしたら、更新されたファイルを自動でFTPでレンタルサーバーにアップロードしたい。
- 各編集者は各自の作業ブランチで編集する。編集が終わったら管理者が変更内容をレビューし、OKなら管理者がmasterブランチにマージする。(プルリクエストなんてものは無い)
- だから、
- 管理者以外はmasterにpushできないようにして、
- push先がmasterだったらアップロードすればいいと思う。
……ということだったので、書きました。
レンタルサーバーにSSHでログインできるなら、FTPでアップロードする処理は不要です。公開用ディレクトリにpullしさえすればいいです。たとえば、以下のページをご覧下さい。
また、レンタルサーバーがGitHubなどのホスティングサービスと連携できるならば、そちらの機能を使ったほうが便利で安全だと思います。
本稿の内容によって生じた問題に関して、筆者は責任を負いかねます。
概要
Gitには、フックという、特定の操作が発生したタイミングで、その操作に割り込んでスクリプトを実行する仕組みがあります。フックのスクリプトは、.git/hooks/
またはhooks
に実行タイミングに応じた名前のファイルで置かれます。
今回使用するフックは、pre-push
とpost-receive
です。前者は、クライアント側のフックで、git push
でpushが行われる前に実行されます。後者はサーバー側のフックで、push処理が正常終了した後に実行されます。pre-push
フックで、push先のブランチがmasterであればpushを中止します。post-receive
フックで、push完了後に更新されたファイルをレンタルサーバーにアップロードします。
編集者がmasterへpushするのを禁止する
まず編集者がmasterに直接pushできないようにします。
下記のシェルスクリプトを各編集者ローカルリポジトリの./git/hooks/
下にpre-push
という名前で配置してもらいます。
#!/bin/bash
while read local_ref local_sha1 remote_ref remote_sha1
do
if [[ "${remote_ref##refs/heads/}" = "master" ]]; then
echo 'You cannot push to the master branch!'
exit 1
fi
done
ファイルを作ったら、スクリプトに実行権限を付与します。
$ cd {ローカルリポジトリのルート}
$ chmod +x ./git/hooks/pre-push
push時にFTPでアップロードする
リモートリポジトリはファイルの実態を持たないので、リモートリポジトリがあるファイルサーバーに、デプロイ用のローカルリポジトリを作って一旦そこにpullするようにしました。
つまり、以下のような流れになります。
- 管理者が変更をpushする。(push先がmasterブランチでなければ何もしない)
- デプロイ用のディレクトリに、その変更がpullされる。
- 直前のpushで更新されたファイル名一覧を調べ、
- デプロイ用ディレクトリに存在していれば、更新されたとみなしてアップロードする
- 存在しなければ、削除されたとみなしてサーバーから削除する
デプロイ用のローカルリポジトリの準備
リモートリポジトリのあるサーバーに、デプロイ用のローカルリポジトリを作ります。
$ git clone {リモートリポジトリのパス}
git-ftpのセットアップ
レンタルサーバーへのアップロードには、git-ftpを用います。インストール手順に従って、インストールして下さい。
続いて、デプロイ用のリポジトリで、git-ftpの初期設定をします。以下のコマンドで、レンタルサーバーに.git-ftp.log
というファイルが作られます。
$ cd {デプロイ用リポジトリのルート}
$ git config git-ftp.url "ftp://{アップロード先のホスト}"
$ git config git-ftp.user "{ユーザー名}"
$ git config git-ftp.password "{パスワード}"
$ git ftp init
post-receiveフックの配置
以下のシェルスクリプトを、リモートリポジトリのhooks/
下にpost-receive
という名前で配置します。
#!/bin/bash
# Master branch name
MASTER='master'
# Path of the root directory of the repository to deploy
DEPLOY_DIR_ROOT="${HOME}/deploy" # デプロイ用のディレクトリのパスに変える
while read prev_sha curr_sha ref
do
branch=${ref##refs/heads/}
if [[ $branch != $MASTER ]]; then
exit 0
fi
done
echo 'run post-receive...'
cd $DEPLOY_DIR_ROOT
git --git-dir=.git pull # --git-dir option must be specified
git --git-dir=.git ftp push
echo 'post-receive done.'
たとえば、上記のシェルスクリプトがローカルのホームディレクトリにあるなら、以下のようにすればコピーできます。
$ scp ~/post-receive {ユーザー名}@{共有サーバーのアドレス}:{リモートリポジトリのパス}/hooks/post-receive
$ rm ~/post-receive
続いて、リモートリポジトリに配置したpost-receive
に実行権限を付与します。
$ ssh {ユーザー名}@{共有サーバーのアドレス}
$ cd {リモートリポジトリのパス}
$ chmod +x ./hooks/post-receive
以上です。
ぶっちゃけ、まずい点たくさんあると思うので、ご指摘下さい。