前回の記事でGitListの導入について書きました。GitListは非常に気に入っているのですが、一つだけ解決したいのがリポジトリの作成がWebからできない点です。
なんとか自分で解決できないかと考えたとき、問題となるのが、Webサーバのユーザとgitリポジトリの所有ユーザが異なる点です。つまり、そのままでは、Webサーバのユーザがgitリポジトリに書き込むことができません。Webサーバのユーザをgitユーザにしてしまう方法や、ファイルのパーミッションを変更して無理やり書き換えられるようにする方法などが考えられますが、どれもあまりスマートではありません。
setuidを使えばWebからgitユーザでコマンドを実行することができますが、この場合、シェルスクリプトにsetuidを付加できないので、実行ファイルを作る必要があります。少し調べてみると、libgit2なるライブラリがあり、簡単にリポジトリの初期化等が行えるようです。なので、ちょっとしたコマンドをC++で作成しsetuidによりgitユーザで実行できるようにして、解決することにしました。
ソースコード
コードを作成するに祭して、libgit2が提供している以下のドキュメントが参考になります。
https://libgit2.github.com/docs/guides/101-samples/#repositories_init_options
https://libgit2.github.com/docs/examples/init/
これを見てわかるように、libgit2のリポジトリ初期化処理では、ディレクトリの作成とdescriptionファイルの変更まで指示することができます。なので、コードとしては以下のように非常にシンプルなものになりました。
#include <iostream>
#include <git2/global.h>
#include <git2/tree.h>
#include <git2/repository.h>
int main(int argc, char *argv[])
{
if (argc != 3) {
std::cout << "usage: " << argv[0] << " <path> <description>\n";
return -1;
}
git_libgit2_init();
git_repository *repo = nullptr;
git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
opts.flags |= GIT_REPOSITORY_INIT_MKPATH;
opts.flags |= GIT_REPOSITORY_INIT_BARE;
opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
opts.description = argv[2];
int error = git_repository_init_ext(&repo, argv[1], &opts);
git_libgit2_shutdown();
return error;
}
上のコードで、以下の処理をまとめて行っていることになります。
$ mkdir <path>
$ git -C <path> --bare init --share
$ echo <description> > <path>/description
ビルド
まず、libgit2の開発パッケージをインストールします。
$ sudo apt install libgit2-dev
ビルドします。
$ g++ -std=c++17 -o git_init git_init.cpp -lgit2
オーナーをgitに変更し、setuidを付加します。
$ sudo chown git.git git_init
$ sudo chmod ug+s git_init
実行
第一引数にリポジトリのパス、第二引数に説明を指定してgit_initを実行すれば、リポジトリが初期化されます。例えば、test1.gitを初期化するには以下のようにします。
$ git_init /home/git/gitrepos/test1.git "this is test1 repository"
後は、これをPHPから実行すれば、gitリポジトリがWebから作成できるようになります。