LoginSignup
0

More than 1 year has passed since last update.

CIのお供にGroovyもいいと思う

Last updated at Posted at 2022-01-12

現在、サーバーサイド Kotlin を使ったプロジェクトの実装をしており、CIに CircleCI を使っています。

CircleCIでは、各JobをDockerコンテナを使って動かせるので、そこでGroovyを使ってみてもいいかなと思って実践してみました。

なぜGroovy?

例えば、ビルド前に以下のようなチェックを実施したいとします。

  • Flyway のマイグレーションファイルのバージョン番号が重複していないかチェックしたい

複数人で並行して開発していると、マージするタイミングによって、Flywayのマイグレーションファイルのバージョン番号が重複するかもしれません。 (本当はちゃんとマージされる前に気づいてbumpしろって話ですが難しい)

もし間違って重複したままマージされても、デプロイされなければ(リポジトリは汚れるけど)動作の実害は起きないので、CIでビルド前・デプロイ前にチェックしたいです。

おそらく、どんなCIツールにしてもシェルスクリプトは使えるはずですが、シェルスクリプトで書くのはちょっとしんどい…。
そして、Kotlinを使ってるので、Java的言語ならみんな読めるはずだろう。
だけど、JavaやKotlinで書くと、ビルドしてクラスファイルを作って java コマンドで…とするほど大きな処理ではない。

そこでGroovyです。

実際に書いてみた例がこちらです。

Flywayのマイグレーションファイルは、以下のフォーマットです。 V1.2.3.4__description.sql

SQL-based migrations

前提条件

  • Flywayのマイグレーションファイルは、 application/src/main/resources/db/migration に保存されている
  • チェック用のGroovyスクリプトは devops フォルダに保存されている
  • JobでのGroovyの実行環境は、オフィシャルのDockerコンテナを使う

ソースコード

devops/check_duplicated_flyway_migration.groovy
// マイグレーションファイルのパターン
def regexFileName = "^V([\\d+.])*\\d__.+\\.sql\$"

// ファイルが保存されているパスを指定
def directory = new File("${System.getProperty("user.dir")}/application/src/main/resources/db/migration")

// ファイル一覧を取得
def fileList = directory.listFiles()

// マイグレーションファイルのうち、バージョン番号でグループ化してカウント
def duplicatedVersions = fileList
        .findAll { it.name.matches(regexFileName) }
        .countBy { getVersion(it.name) }
        .findAll { it.value > 1 }

// もしリストが空でないならエラー
if (duplicatedVersions.size() > 0) {
    println("Error: Duplicated flyway migration found:")
    duplicatedVersions.keySet()
            .sort { sortByVersion(it) }
            .each {
                println("  $it")
                fileList.findAll { f -> f.name.startsWith(it) }
                        .collect { it.name }
                        .sort()
                        .each {println("    $it")}
            }

    // 終了コードを0以外にして、CIでエラー扱いとする
    System.exit(1)
} else {
    println("All flyway migration files have valid name:")
    fileList.findAll { it.name.matches(regexFileName) }
        .sort { sortByVersion(getVersion(it.name)) }
        .each { println("  ${it.name}") }
}

private static sortByVersion(String fileName) {
    def versionNumbers = fileName.replaceFirst("V", "")
    if (versionNumbers.length() == 0) {
        return []
    }

    versionNumbers.split("\\.").collect { Integer.parseInt(it) }
}

private static String getVersion(String fileName) {
    fileName.split("__", 2).first()
}
.circleci/config.yml
version: 2.1

jobs:
  check-flyway-migration:
    docker:
      - image: groovy:4.0-alpine
    steps:
      - checkout
      - run: groovy devops/check_duplicated_flyway_migration.groovy
  test-build:
    steps:
      - checkout
      - echo テストしてビルドする
  deploy:
    steps:
      - echo デプロイする

workflows:
  version: 2
  build-deploy:
    jobs:
      - check-flyway-migration
      - test-build
      - deploy:
          requires:
             - check-flyway-migration
             - test-build

実際にCircleCIで動かしてみる

重複している場合

#!/bin/sh -eo pipefail
groovy devops/check_duplicated_flyway_migration.groovy
WARNING: Using incubator modules: jdk.incubator.vector, jdk.incubator.foreign
Error: Duplicated flyway migration found:
  V3.0.0.0
    V3.0.0.0__test1.sql
    V3.0.0.0__test2.sql
  V3.2.1.0
    V3.2.1.0__test5.sql
    V3.2.1.0__test6.sql
  V3.10.0.0
    V3.10.0.0__test3.sql
    V3.10.0.0__test4.sql

Exited with code exit status 1
CircleCI received exit code 1

これで、 deploy ジョブは動きません。

OKの場合

#!/bin/sh -eo pipefail
groovy devops/check_duplicated_flyway_migration.groovy
WARNING: Using incubator modules: jdk.incubator.foreign, jdk.incubator.vector
All flyway migration files have valid name:
  ここにファイル名

CircleCI received exit code 0

これで、 deploy ジョブが動きます( test-build も成功している場合)。


このように、シェルスクリプトで書くのは難しい、PythonとかNodeとか知らない、PythonやNodeだとvenvとかpackage.jsonとか色々…という場合、Groovyを使うこともアリかなと感じました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0