GitLab にカスタムフックを設定する

Software Configuration Management (SCM)

これまで GitHub へプッシュしたリポジトリを GitLab へもバックアップ的にプッシュしてたのですけど、毎回両方にプッシュするのは面倒なので GitLab にプッシュすると GitHub へも転送されるようにしてみました。


以前にインストールしたバージョン管理システム こちら でGitLab を好んで使っているのですけど、この頃は GitHub を使う機会も増えてきました。

せっかくなので GitLab と GitHub の両方にアップして、片方をバックアップみたいな意味合いで使うようにしてたのですけど、そうなると両方に git push をしないといけなくて若干煩わしいところがありました。

幸い、どちらでも根底で使われている Git の カスタムフック という仕組みを使うと、たとえば「プッシュしたタイミングで何かをする」みたいなことができるようだったので、これを使って片方のリポジトリにプッシュしたときに、もう片方のリポジトリへも自動でプッシュされるようにしてみました。

方針

まず、どちらのリポジトリにフックを仕掛けようか検討していたのですけど、とりあえず GitHub にカスタムフックを仕掛けられるのかわからなかったので、今回は GitLab のリポジトリにプッシュされたときに GitHub へプッシュするようにしてみます。

GitLab から GitHub へアクセスするための準備

そのために、まずは GitLab から GitHub へアップロードできるようにしました。

自分の GitLab 環境では git という名前のアカウントを使って運用されているので、まずは GitLab を入れたサーバーにそのアカウントでログインして、GitHub に接続するための秘密鍵と公開鍵を作成しました。

GitLab サーバーで鍵ペアを作成する

GitLab サーバーに接続したら、まずはホームディレクトリに .ssh ディレクトリを作成して、そのパーミッションを 700 に設定しておきます。作成済みであればこの操作は不要です。

mkdir ~/.ssh
chmod 700 ~/.ssh

作成したらそのディレクトリに移動しておきます。

cd ~/

そして ssh-keygen コマンドを使って秘密鍵と公開鍵のセットを作ります。

ssh-keygen -t rsa -b 4096

このときに鍵のファイル名を入力することになりますが、今回は id_github みたいな名前を入力しておくことにしました。


これで鍵が作成できました。

そうしたら GitLab から GitHub に接続しようとしたときにこの鍵が使われるように、次の内容で ~/.ssh/config ファイルを作成しておくことにします。

host github.com
        identityfile    ~/.ssh/id_github
        identitiesonly  yes

公開鍵を GitHub に登録する

ここまでできたら、作成した公開鍵を GitHub に登録します。

公開鍵は、これまでの手順であれば ~/.ssh/id_github.pub というファイルに保存されています。この中に次のような形式のテキストが記録されているので、それを GitHub に登録することになります。

ssh-rsa WNMHeKNzaC1yL0NLEAADAQABAAACAQC9WULdU9UQ6Zc2nU/+CeZhXyWNMHeK0vzXaAAcL4MPRiWooLS4Y1zAgleyEqR9Q6Zc2xgNSFHOhSCNVSRMwWNMHeKjMM4HaT7PJOKT7pKy1FtpEha+emTF4Q2A5vUwjKFP51lzMFKL0NLE7JV9NN3uFU9dUVj+2twxdoai+CeZhXQ6Zc2eK0vzXaAAcL4MPRiWooLS4Y1zAgleyEqR9YnEVWxgNSFHOhSCNVSRMwDaWKhNjMM4HaT7PJOKT7pKy1FtpEha+TZRxkpeQ6Zc2QHgo4WacNgyGdTj1x6OadFNVGPzQltr9IPBRAqdhLqZHHOrjy0ss7RWNMHeKdSHmvTm8MAlHhIxJM292WMRg8ciKFU5vUwVzypc2Vd4L0NLELPgouhcgXOCy3geTBozwvt6Fd/D4c9rtm0WNMHeKV5ywM5VpccXbQnWZfSFukFJdSJh2y76Hp5OZEvAcdddldZfAypPgKTr12WoWgQVrNfmeQ6Zc2WQ6Zc2bPV8xDQuG4HMuFJW8KfVl/3Ivea5vUwpn9a3zGmYKuSOXmTLbbJ91WI1u2ay5BotbFnyOWNMHeKqSwagjA42L6uPQxMLIKRAQ6Zc2YBqQ6Zc2w== tomohiro@xxxx.xx.xx

GitHub の Web ページにある Settings を開いて、SSH keys の画面を開きます。

そして Add SSH keyボタン を押して、先ほど作成した公開鍵をコピーして Key の欄にペーストして登録すれば、これでこの鍵に対応する秘密鍵を持っている人が接続できるようになりました。

GitLab にカスタムフックを登録する

それでは、GitLab のリポジトリにプッシュされたブランチを、そのまま GitHub にもプッシュするカスタムフックを作成してみます。

このようなフックを作成したい場合は、リポジトリにプッシュされたとき(Git サーバーがプッシュを受信したとき)に処理される pre-receive フックと post-receive フックあたりを使って追加の処理を実装することになるようでした。

リポジトリ毎にフックを作成する

これらのフックは、それを発動させたいリポジトリ毎に登録する必要があるようです。

通常の git であれば、この名前のファイルを .git/hooks ディレクトリに作成して実行権限を与えれば動くらしいのですが、GitLab 環境では既にこれらのフックが登録されているようで、代わりに .git/custom_hooks ディレクトリに作成する仕組みになっていました。

保存場所が変わる以外は、本来の Git と同じように作っていくことになる様子です。

なお、GitLab では Git リポジトリが ~/git-data/repositories/[USERNAME] に保存されることになっているようです。

プッシュされたブランチを転送するスクリプトを作る

それでは、リポジトリへ転送されてきたブランチを、別のリポジトリへ転送するスクリプトを作成してみます。

今回のスクリプトでは、転送先のリポジトリをあらかじめ git remote add で登録しておくことにします。このとき、リモートリポジトリ名は今回は 'github' としておきます。


作成するスクリプトは、今回は GitLab へブランチがプッシュされて、それらの内容をすべて受け取ったあとに実行される post-receive を使うことにします。

このスクリプトは、プッシュされた Git 参照リストを標準入力で受け取る仕組みになっていて、このスクリプトが exit(0) で終了すればコミット完了、それ以外で終了したときはコミット却下という動きになります。処理の最中で標準出力に出力した内容は、そのまま push 処理時に出力されるメッセージの一環として表示されます。

標準入力で受け取れる値は「更新前のコミット情報」「更新後のコミット情報」「ブランチの参照情報」という 3 つがセットで渡ってきます。これが複数ある場合があるらしいので、それぞれをループで処理することにします。

GIT_REMOTE="github"

while read OLDREV NEWREV REFNAME
do
        BRANCH=`git rev-parse --symbolic --abbrev-ref "${REFNAME}"`

        echo "Push '${BRANCH}' branch to remote '${GIT_REMOTE}'"
        git push --tag ${GIT_REMOTE} ${BRANCH}
done

このファイルを、目的のリポジトリ内の custom_hooks/post-receive に作成して、実行可能なパーミッションを設定すれば、これで準備完了です。

chmod u+x custom_hooks/post-receive

動作を確認してみる

これで、先ほどのカスタムフックを登録したリポジトリにコミットをプッシュしてあげると、カスタムフックが実行されて、あらかじめ登録しておいたリモートリポジトリにも同じコミットがプッシュされるようになりました。

つまり、カスタムフックを設定したリポジトリが「リモートリポジトリ」として登録されている環境から、いつものようにプッシュすることで利用できます。

git push origin master

このようにすると、たとえば次のような出力がされて、プッシュ操作が行われます。ここの remote: で記載されている内容が、カスタムフックで出力された文字列になるようです。

Counting objects: 59, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (50/50), done.
Writing objects: 100% (59/59), 31.79 KiB | 0 bytes/s, done.
Total 59 (delta 12), reused 0 (delta 0)
remote: Push 'master' branch to remote 'github'
remote: To git@github.com:EZ-NET/ESSwim.git
remote:  * [new branch]      master -> master
To ssh://git@gitlab.ez-net.jp/tomohiro/ESSwim.git
 * [new branch]      master -> master

このとき、もしカスタムフック側でプッシュできなかったりして失敗すると、そもそものこのリモートリポジトリへのプッシュ操作も取り消されることになるので、どちらか片方だけが更新されるみたいなことがなくて安心です。