この記事はPonos Advent Calendar 2019 8日目の記事です。
はじめに
Scalaで記載されたオープンソースのGitHub CloneとしてGitBucketというものがあります。
このGitBucketですがいくつかのプラグインが公開されています。
今回はこの中のCIに関連するpluginのgit-bucket-ci pluginに機能追加した話をしようかと思います。
(ちなみにGitBucketはApacheライセンスです。)
この記事の対象者
- GitBucketのソースコードを読んでみたい人
-
プルリクエストするまでに何を考えて作業しているのかを知りたい人
- (免責)私のやり方のためこの記事の方法は一般的ではないかもしれません
追加した機能
- プルリクエストをマージした直後にCIを走らせる機能
- 上記機能をプラグインの設定画面で有効化/無効化する機能
- 今はない機能を追加実装するためデフォルトの挙動は無効化にする
事前に試したこと
- GitBucketにgitbucket-ci-pluginを設定して利用した
- プラグインのインストール方法(プラグインのホイットデプロイに対応 の部分)
- gitbucket-ci-pluginのjarファイルはこちら
- プルリクエストを作成してマージするときにCIが走らないことを確認した
- GitBucketやgitbucket-ci-pluginのREADMEを読み上記の機能が実装されてなさそうなことを確認した
機能の確認と追加
1. プルリクエストをマージした直後にCIを走らせる機能の追加
1-1. 本当に機能が実装されていないかどうかを確認する
1-1-1. GitBucketのディレクトリ構造を確認する
- ビルドツールを確認する
- build.sbtがあるのでビルドツールはsbtを使っている
- (ビルドツールを確認しなくても大抵は)src/main の下にコードが入っている
- src/main/scala/gitbucket/core の下に主なコードが入っている
1-1-2. gitbucket-ci-pluginのディレクトリ構造を確認する
- ビルドツールを確認する(こっちもbuild.sbtがあるのでsbt)
- src/main の下にコードが入っている
- src/main/scala/io/github/gitbucket/ci の下に主なコードが入っている
1-1-3. gitbucket-ci-pluginのプルリクエスト時に実行されそうなコードを探す
- CIの拡張機能はHookで動作する筈なのでhookに処理が記載されていそう。次点で処理が記載されていそうなのはサービス層なのでserviceも確認候補に入れる
-
hookの中にCIPullRequestHook.scalaというプルリクエストと関係ありそうなファイルがあるので確認する
-
override def created
override def addedComment
とオーバーライドされている処理があるのでこれがプルリクエスト時に動いている機能だと推測する - この関数を定義しているクラスはGitBucketのgitbucket.core.plugin.PullRequestHookを継承しているのでこちらにフックを受け付ける処理が入っている筈
-
1-1-4. GitBucketにプルリクエストのマージ後に実行される(かつ、上書きできる)処理があるかを調べる
- gitbucket.core.plugin.PullRequestHookの中を確認する
- GitHubの検索窓で検索するとsrc/main/scala/gitbucket/core/plugin/IssueHook.scalaで定義されていることが分かる
- ***IssueHook.scala(下のコード)***は以下の機能拡張を提供しているので機能追加はできそう
created
addedComment
closed
reopened
assigned
closedByCommitComment
merged
- GitHubで
merged
を検索したところ、この関数のオーバーライドで目的が達成できそう-
merged
はsrc/main/scala/io/github/gitbucket/ci/hook/CIPullRequestHook.scalaで利用されている -
mergePullRequest
関数の終わり(プルリクエストのマージ処理の直後)に実行されている
-
- gitbucket-ci-pluginで
merged
はオーバーライドされておらず、GitBucketでmerged
のフックが提供されているのでこの機能は実装されていないと判断する
IssueHook.scala
package gitbucket.core.plugin
import gitbucket.core.controller.Context
import gitbucket.core.model.{Account, Issue}
import gitbucket.core.service.RepositoryService.RepositoryInfo
import gitbucket.core.model.Profile._
import profile.api._
trait IssueHook {
def created(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
def addedComment(commentId: Int, content: String, issue: Issue, repository: RepositoryInfo)(
implicit session: Session,
context: Context
): Unit = ()
def closed(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
def reopened(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
def assigned(
issue: Issue,
repository: RepositoryInfo,
assigner: Option[String],
assigned: Option[String],
oldAssigned: Option[String]
)(
implicit session: Session,
context: Context
): Unit = ()
def closedByCommitComment(issue: Issue, repository: RepositoryInfo, message: String, pusher: Account)(
implicit session: Session
): Unit = ()
}
trait PullRequestHook extends IssueHook {
def merged(issue: Issue, repository: RepositoryInfo)(implicit session: Session, context: Context): Unit = ()
}
1-2. 機能を追加する
1-2-1. src/main/scala/io/github/gitbucket/ci/hook/CIPullRequestHook.scalaにoverride def merged
を実装する
-
コードを修正する
-
merged
はプルリクエストの後で動く。マージ後のコードに対してCIしたいのでbase repositoryではなくhead repositoryのコードでCIが走るようにする -
created
やaddedComment
はプルリクエストのコード修正の確認をすることが目的なのでbase repositoryに対してCIが走るようになっている
-
GitBucketのプラグインを置き換えたら動いた!!
2. 上記機能をプラグインの設定画面で有効化/無効化する機能の追加
2-1. GitBucketとgitbucket-ci-pluginの構成を把握する
2-1-1. GitBucketの構成を把握する
-
build.sbtの中の
libraryDependencies
を確認し、どんなライブラリが利用されているのか調べる- scalatra: Web application framework
- solidbase: GitBucketと同じ作者が作成したDBマイグレーションツール
-
project/plugins.sbtの中も確認する
-
twirl: Playフレームワークのテンプレートエンジン
2. scalatraのテンプレートエンジンはscalateが標準だと思っていたがそうでもないらしい
-
twirl: Playフレームワークのテンプレートエンジン
2-1-1. gitbucket-ci-pluginの構成を把握する
-
build.sbtの中の
libraryDependencies
を確認し、どんなライブラリが利用されているのか調べる- 特筆すべき項目は特になかった
-
project/plugins.sbtの中も確認する
- 特筆すべき項目は特になかった
2-2. 調査結果から変更が必要そうなものを考える
- gitbucket-ci-pluginの設定画面のGUI(twirlのテンプレートの修正)
- gitbucket-ci-pluginの設定画面で保存する設定項目(gitbucket-ci-pluginのコードを読んで確認する)
- DBマイグレーションツールの設定(上記2.はDBに保存されている筈なので)
- gitbucket-ci-pluginのユニットテスト(必要に応じて)
2-3. コードを修正する
2-3-1. 方針を決める
- gitbucket-ci-pluginの管理画面にRun a build script after the mergeという項目を新たに作り、この項目のオン/オフで挙動を制御する
2-3-2. コードを修正する
- src/main/twirl/gitbucket/ci/config.scala.htmlにチェックボックスを追加する
-
src/main/scala/io/github/gitbucket/ci/controller/CIController.scalaに上記画面から値を受け取るための処理を定義する
- このファイルの
saveCIConfig
の部分でDBに設定を永続化しているのが分かるのでCIConfigに関連する部分も変更する
- このファイルの
-
src/main/scala/io/github/gitbucket/ci/model/CIConfig.scalaに
runAfterMerge: Boolean
を追加する -
merged
の処理をrunAfterMerge
の項目を評価するように変更する- 変更するファイルはsrc/main/scala/io/github/gitbucket/ci/hook/CIPullRequestHook.scala
- DBの変更を自動反映するためにDBマイグレーションに関連する部分を変更する
- src/main/resources/update/gitbucket-ci_1.9.1.xml
- src/main/scala/Plugin.scala
- DBスキーマの変更が入るため(勝手に)バージョン番号をあげたのでgitbucket-ci-pluginの
build.sbt
も修正する - ユニットテストを修正する(本当は最初にユニットテストを修正してから本番コードを書くのが良い筈)
-
created
やaddedComment
に対するユニットテストが特になかったので実施しなかった - また、どのようなユニットテストを行うのかについてもこの段階ではあまり考えていなかった
-
2-3-3. 動作確認する
-
Run a build script after the mergeオフの状態
- プルリクエストの作成とマージ後の挙動の確認
-
Run a build script after the mergeオンの状態
2. プルリクエストの作成とマージ後の挙動の確認
想定通りに動いた!!
2-3-4. 最後に本体にプルリクエストする
https://github.com/takezoe/gitbucket-ci-plugin/pull/85
(プルリクエストは実施前にgit rebase -i HEAD~xx
(xxには数字が入る)等して自分の変更をfixupしてから行うのが良い。
私自身のプルリクエストが中途半端なfixupの状態になっているのであまり大きな声では言えないが。。。)