Jenkinsを使った継続的デリバリー(2)〜Groovyによるインストーラ〜

今までであればインストールスクリプトも、Windows用のバッチファイル、Linux用のシェルスクリプトとそれぞれ作っていたのだけど、少しやり方を変えて、共通のGroovyスクリプトで実行できるようにしてみる。

メリットとしては、まずスクリプトが1本に収まることによる生産性、保守性の高さ。まがりなりにも一番長く触れてきた言語はJavaなので、親和性の高いGroovyなら、それ単独で見ても、バッチファイルやシェルスクリプトよりも効果的に開発できるし、バッチとシェルの違いに気をつけながらほとんど同じ振る舞いをするスクリプトを2つ作らなくて済む。

ちなみに今回、インストール対象のアプリは全てJava製で、JREも各インストール先に導入済み。そのため、GroovyならJREさえ入っていれば動くので、既存の環境に手を加えずにそのまま導入できる。インストーラを実行するために実行環境を新たに入れる必要はない。アプリがRuby製だったらインストーラRubyで書く、って選択肢になったかも。

で、別のメリットとして、Jenkinsにおけるデプロイ用のジョブのビルド手順がとてもシンプルになった。デプロイ用のジョブでやるべき本質的な処理は以下のものだけ。

  1. 成果物(groovy-all.jarとGroovyスクリプトを含むパッケージ)を「Copy Artifact Plugin」で選択したスレーブ(インストール先)にコピー
  2. java -jar groovy-all.jar Deploy.groovy」的なコマンドを実行。

過去に作ったJenkins(Hudson)の設定だと、Jenkinsで定義したビルド手順が膨大になりすぎて、効率が悪かったんだよな…。
まぁ結局細かい手順はGroovyスクリプトで書いていかなきゃならないんだけど、これまでの方法よりはずっと生産的な気がする。

ちょいと脇道に逸れるけど、「java -jar ...」みたいなコマンドを実行したい場合のビルド手順って、「Windowsバッチコマンドの実行」と「シェルの実行」のどっちにするのがいいんだろうか…?とりあえず「シェルの実行」にしたけど(Windows用デプロイでも)

ちなみに、パッケージファイル(.zip, .tar.gz)の展開もgroovyスクリプトの内部で行うことにした。理由は、Windows 2003 Serverでunzipが使えなかったから。unzipコマンドが使えるかどうかといった環境の差異もインストーラ側で吸収してしまう。

Zipを回答するgroovyスクリプトは以下のように作った。

// ZIPファイルの解凍
def unzip(srcFile, destDir, overwrite=false) {
   assert srcFile != null && srcFile.exists()
   assert destDir != null && destDir.exists()

   def zipFile = new java.util.zip.ZipFile(srcFile)
   zipFile.entries().each {
       def destFile = new File(destDir, it.name)
       if (it.directory) {
           destFile.mkdirs()
           println "mkdir: ${it}"
       } else {
           if (!overwrite && destFile.exists()) {
               destFile.delete()
           }
           if (!destFile.getParentFile().exists()) {
               destFile.getParentFile().mkdirs()
           }
           destFile << zipFile.getInputStream(it).getBytes()
           println "extract: ${it} (${it.size} bytes)"
       }
   }
   zipFile.close()
}

unzip(new File("test.zip"), new File("."))

実はGroovy使うの今回がはじめてなので、あやしい点も色々あるけど、使った感想として、「エレガントに書くことにこだわらず、困ったら愚直にJavaコード書いちゃえ」って思えるのがいいと感じた(その是非はおいといて)。RubyとかPythonだと、Rubyらしく、Pythonらしく、書くことに気を回しすぎて手が止まったりするんだよね。
Javaで冗長になったり、痒いところだけGroovyの力を借りればいいか、くらいの気持ちで書くと、実に気持ちよく書けた。

で、せっかくGroovyで書いたなら、そのうち、AntからGradleへの移行もしたいなぁ…。ただ、build.xmlをある程度かっちり作ってしまった後で、Gradleへ移行するのは、移行のためのコストを加味すると、しばらく保留にしておこう。