2020年4月10日金曜日

Gitlab で自作の Plugin (File hooks) を実行する方法

概要

Gitlab には File hooks と呼ばれる機能がありあるイベント (例えばプロジェクトの作成) が実行されたときにスクリプトを自動で実行する機能があります
File hooks という機能は Gitlab 12.8 までは「Plugins」という名前で呼ばれていました

環境

  • macOS 10.15.5
  • GitLab Community Edition 12.9.2
    • Ruby 2.6.5p114 (on gitlab)

docker を起動する際にプラグインディレクトリをマウントする

今回は gitlab は docker 上で動作させることを想定しています
VM で rails プロセスを動作させる場合でも仕組みは同じで「plugins」というディレクトリを特定の場所に設置しその配下に実行可能なスクリプトを作成すれば OK です

docker run -d -h gitlab.example.com \
-p 443:443 -p 80:80 -p 22:22 \
--name gitlab \
-v $(pwd)/gitlab/config:/etc/gitlab \
-v $(pwd)/gitlab/logs:/var/log/gitlab \
-v $(pwd)/gitlab/data:/var/opt/gitlab \
-v $(pwd)/gitlab/plugins:/opt/gitlab/embedded/service/gitlab-rails/plugins \
gitlab/gitlab-ce:latest

docker の場合は /opt/gitlab/embedded/service/gitlab-rails/plugins というディレクトリがあるのでここにスクリプト配置するようにすれば OK です

スクリプト作成

docker にマウントしたパスにスクリプトを作成します
ホスト側で同じパスをマウントしているのでホスト側でスクリプトを作成すれば OK です

  • vim gitlab/plugins/app.rb
#!/opt/gitlab/embedded/bin/ruby
require 'json'

input = JSON.parse(STDIN.read)
File.open('./test.txt', 'w') do |f|
  f.write(input)
end

とりあえずイベントの内容をコンテナ内にファイルとして保存するスクリプトです
イベントの内容は標準入力として来るのでそれを読み込みます
またスクリプトには必ずシェバングを入力する必要があります
docker バージョンの gitlab の場合は ruby は /opt/gitlab/embedded/bin/ruby にあるのでこれを指定します

また Python や Perl などのスクリプトを実行したい場合は予め自分で python や perl をインストールする必要があります

スクリプトに実行権限を与える

  • chmod 755 gitlab/plugins/app.rb

これも重要です
内部で実行される際にはファイルが直接実行されます
そのためにシェバングも書きました
権限がない場合はスクリプトが実行されないので注意しましょう

動作確認

適当にプロジェクトを作成しましょう
そしてファイルが作成されているか確認します

  • docker exec gitlab find / -name "test.txt"
  • docker exec gitlab cat /var/opt/gitlab/gitlab-rails/working/test.txt
{"event_name"=>"project_destroy", "created_at"=>"2020-04-09T23:56:27Z", "updated_at"=>"2020-04-09T23:57:26Z", "name"=>"test3", "path"=>"test3", "path_with_namespace"=>"root/test3", "project_id"=>7, "owner_name"=>"Administrator", "owner_email"=>"admin@example.com", "project_visibility"=>"private"}

こんあ感じで JSON (ハッシュ) としてイベントの情報が記載されていることが確認できると思います

スクリプトを更新する場合は

コンテナにマウントしているので直接ホスト側からスクリプトを修正すれば OK です
特に gitlab コンテナの再起動などは必要ありません

スクリプトのログを標準出力に出すには

スクリプト内で普通に puts などを実行しても docker logs には表示されませんでした
いろいろ調べたのですが見つからなかったので素直にファイルに吐き出すと良いと思います

#!/opt/gitlab/embedded/bin/ruby
require 'json'
require 'logger'

input = JSON.parse(STDIN.read)

logger = Logger.new('/var/log/gitlab/gitlab-rails/my_plugin.log')
logger.info(input)

File.open('./test.txt', 'w') do |f|
  f.write(input)
end
  • docker exec gitlab cat /var/log/gitlab/gitlab-rails/my_plugin.log

ちなみにスクリプトが動作しなかった場合のエラーログは

  • docker exec gitlab cat /var/log/gitlab/gitlab-rails/plugin.log

で確認できます

E, [2020-04-09T23:56:30.964800 #494] ERROR -- : File Hook Error => /opt/gitlab/embedded/service/gitlab-rails/plugins/app.rb: /opt/gitlab/embedded/service/gitlab-rails/plugins/app.rb:2:in `sysopen': Permission denied @ rb_sysopen - /proc/1/fd/1 (Errno::EACCES)

特定のプロジェクトや issue にフックするには

例えば issue であれば object_kind あたりを見ればいいと思います
また特定のプロジェクトにのみ反応するプラグインを作成したいのであれば project_id あたりを見れば良いと思います

#!/opt/gitlab/embedded/bin/ruby
require 'json'
require 'logger'

input = JSON.parse(STDIN.read)

if input['project_id'] == 9 || input['project']['id'] == 9
  logger = Logger.new('/var/log/gitlab/gitlab-rails/my_plugin.log')
  logger.info(input)

  File.open('./test.txt', 'w') do |f|
    f.write(input)
  end
end

issue を作成した場合は input['project']['id'] という感じで取得する必要があるようです
イベントによって入力される JSON の情報が若干異なるようなので注意が必要です

最後に

Gitlab で File hooks (Plugins) を試してみました
インストールはシステム全体に行うのが少し不安というかトラブルの元になる感じがします
プロジェクトごとやイベントごとのハンドリングはすべてスクリプト内で行います

ruby gems などのライブラリを使いたい場合は事前に gitlab server にグローバルインストールしておく必要がありそうです
また複数の gitlab server がある場合はすべての server にスクリプトを配置する必要があるのも注意が必要です

参考サイト

0 件のコメント:

コメントを投稿