Help us understand the problem. What is going on with this article?

gitbucket-ci-plugin に機能追加してみた

この記事はPonos Advent Calendar 2019 8日目の記事です。


はじめに

Scalaで記載されたオープンソースのGitHub CloneとしてGitBucketというものがあります。
このGitBucketですがいくつかのプラグインが公開されています。

今回はこの中のCIに関連するpluginのgit-bucket-ci pluginに機能追加した話をしようかと思います。

(ちなみにGitBucketはApacheライセンスです。)

この記事の対象者

  • GitBucketのソースコードを読んでみたい人
  • プルリクエストするまでに何を考えて作業しているのかを知りたい人
    • (免責)私のやり方のためこの記事の方法は一般的ではないかもしれません

追加した機能

  1. プルリクエストをマージした直後にCIを走らせる機能
  2. 上記機能をプラグインの設定画面で有効化/無効化する機能
    1. 今はない機能を追加実装するためデフォルトの挙動は無効化にする

事前に試したこと

  1. GitBucketにgitbucket-ci-pluginを設定して利用した
    1. プラグインのインストール方法(プラグインのホイットデプロイに対応 の部分)
    2. gitbucket-ci-pluginのjarファイルはこちら
  2. プルリクエストを作成してマージするときにCIが走らないことを確認した
  3. GitBucketやgitbucket-ci-pluginのREADMEを読み上記の機能が実装されてなさそうなことを確認した

機能の確認と追加

1. プルリクエストをマージした直後にCIを走らせる機能の追加

1-1. 本当に機能が実装されていないかどうかを確認する

1-1-1. GitBucketのディレクトリ構造を確認する

GitBucket.top.png

  1. ビルドツールを確認する
    1. build.sbtがあるのでビルドツールはsbtを使っている
  2. (ビルドツールを確認しなくても大抵は)src/main の下にコードが入っている
  3. src/main/scala/gitbucket/core の下に主なコードが入っている
1-1-2. gitbucket-ci-pluginのディレクトリ構造を確認する

gitbucket-ci-plugin.top.png

  1. ビルドツールを確認する(こっちもbuild.sbtがあるのでsbt)
  2. src/main の下にコードが入っている
  3. src/main/scala/io/github/gitbucket/ci の下に主なコードが入っている
1-1-3. gitbucket-ci-pluginのプルリクエスト時に実行されそうなコードを探す

gitbucket-ci-plugin.ci.png

  1. CIの拡張機能はHookで動作する筈なのでhookに処理が記載されていそう。次点で処理が記載されていそうなのはサービス層なのでserviceも確認候補に入れる
  2. hookの中にCIPullRequestHook.scalaというプルリクエストと関係ありそうなファイルがあるので確認する
    1. override def created override def addedCommentオーバーライドされている処理があるのでこれがプルリクエスト時に動いている機能だと推測する
    2. この関数を定義しているクラスはGitBucketのgitbucket.core.plugin.PullRequestHookを継承しているのでこちらにフックを受け付ける処理が入っている筈
1-1-4. GitBucketにプルリクエストのマージ後に実行される(かつ、上書きできる)処理があるかを調べる
  1. gitbucket.core.plugin.PullRequestHookの中を確認する
  2. GitHubの検索窓で検索するとsrc/main/scala/gitbucket/core/plugin/IssueHook.scalaで定義されていることが分かる
  3. IssueHook.scala(下のコード)は以下の機能拡張を提供しているので機能追加はできそう
    1. created
    2. addedComment
    3. closed
    4. reopened
    5. assigned
    6. closedByCommitComment
    7. merged
  4. GitHubでmergedを検索したところ、この関数のオーバーライドで目的が達成できそう
    1. mergedsrc/main/scala/io/github/gitbucket/ci/hook/CIPullRequestHook.scalaで利用されている
    2. mergePullRequest関数の終わり(プルリクエストのマージ処理の直後)に実行されている
  5. 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.scalaoverride def mergedを実装する
  1. コードを修正する
    1. mergedはプルリクエストの後で動く。マージ後のコードに対してCIしたいのでbase repositoryではなくhead repositoryのコードでCIが走るようにする
    2. createdaddedCommentはプルリクエストのコード修正の確認をすることが目的なのでbase repositoryに対してCIが走るようになっている

GitBucketのプラグインを置き換えたら動いた!!

2. 上記機能をプラグインの設定画面で有効化/無効化する機能の追加

2-1. GitBucketとgitbucket-ci-pluginの構成を把握する

2-1-1. GitBucketの構成を把握する
  1. build.sbtの中のlibraryDependenciesを確認し、どんなライブラリが利用されているのか調べる
    1. scalatra: Web application framework
    2. solidbase: GitBucketと同じ作者が作成したDBマイグレーションツール
  2. project/plugins.sbtの中も確認する
    1. twirl: Playフレームワークテンプレートエンジン
      1. scalatraのテンプレートエンジンはscalateが標準だと思っていたがそうでもないらしい
2-1-1. gitbucket-ci-pluginの構成を把握する
  1. build.sbtの中のlibraryDependenciesを確認し、どんなライブラリが利用されているのか調べる
    1. 特筆すべき項目は特になかった
  2. project/plugins.sbtの中も確認する
    1. 特筆すべき項目は特になかった

2-2. 調査結果から変更が必要そうなものを考える

  1. gitbucket-ci-pluginの設定画面のGUI(twirlのテンプレートの修正)
  2. gitbucket-ci-pluginの設定画面で保存する設定項目(gitbucket-ci-pluginのコードを読んで確認する)
  3. DBマイグレーションツールの設定(上記2.はDBに保存されている筈なので)
  4. gitbucket-ci-pluginのユニットテスト(必要に応じて)

2-3. コードを修正する

2-3-1. 方針を決める
  1. gitbucket-ci-pluginの管理画面にRun a build script after the mergeという項目を新たに作り、この項目のオン/オフで挙動を制御する
2-3-2. コードを修正する
  1. src/main/twirl/gitbucket/ci/config.scala.htmlにチェックボックスを追加する
  2. src/main/scala/io/github/gitbucket/ci/controller/CIController.scalaに上記画面から値を受け取るための処理を定義する
    1. このファイルのsaveCIConfigの部分でDBに設定を永続化しているのが分かるのでCIConfigに関連する部分も変更する
  3. src/main/scala/io/github/gitbucket/ci/model/CIConfig.scalarunAfterMerge: Booleanを追加する
  4. mergedの処理をrunAfterMergeの項目を評価するように変更する
    1. 変更するファイルはsrc/main/scala/io/github/gitbucket/ci/hook/CIPullRequestHook.scala
  5. DBの変更を自動反映するためにDBマイグレーションに関連する部分を変更する
    1. src/main/resources/update/gitbucket-ci_1.9.1.xml
    2. src/main/scala/Plugin.scala
  6. DBスキーマの変更が入るため(勝手に)バージョン番号をあげたのでgitbucket-ci-pluginのbuild.sbtも修正する
  7. ユニットテストを修正する(本当は最初にユニットテストを修正してから本番コードを書くのが良い筈)
    1. createdaddedCommentに対するユニットテストが特になかったので実施しなかった
    2. また、どのようなユニットテストを行うのかについてもこの段階ではあまり考えていなかった
2-3-3. 動作確認する
  1. Run a build script after the mergeオフの状態
    1. プルリクエストの作成とマージ後の挙動の確認
  2. Run a build script after the mergeオンの状態
    1. プルリクエストの作成とマージ後の挙動の確認

想定通りに動いた!!

2-3-4. 最後に本体にプルリクエストする

https://github.com/takezoe/gitbucket-ci-plugin/pull/85
(プルリクエストは実施前にgit rebase -i HEAD~xxxxには数字が入る)等して自分の変更をfixupしてから行うのが良い。
私自身のプルリクエストが中途半端なfixupの状態になっているのであまり大きな声では言えないが。。。)

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした