GeekFactory

int128.hatenablog.com

Gradle SSH Plugin 2.4.0をリリースした

Gradle SSH Plugin 2.4.0、Groovy SSH 2.4.0をリリースしました。

github.com

2.4.0の変更点

New features:

  • Host key checking for gateway access
  • Put files filtered by given closure
  • Get files filtered by given closure
  • Add ssh.runtime object in CLI

Bug fixes:

  • Skip lecture message from sudo result
  • Specify null as UserInfo to prevent changing known_hosts

Host key checking for gateway access

ゲートウェイサーバを経由して多段接続する場合でも known_hosts が使えるようになりました。例えば、localhost→A→Bのように踏み台Aを経由してBに接続する場合、Bは実際にはポートフォワードの接続先(localhost:xxxxx)に見えるため、ホスト鍵の検証に失敗する問題がありました。2.4.0からは known_hosts に含まれるBをポートフォワードの接続先に読み替える実装を追加しました。

Get/Put files filtered by given closure

これまで get put メソッドはすべてのファイルを再帰的に転送する機能しか提供していませんでしたが、2.4.0からはクロージャでフィルタする機能も提供するようになりました。

get from: '/remote/folder', into: buildDir, filter: { it.name =~ /\.xml$/ }

put from: buildDir, into: '/remote/folder', filter: { it.name =~ /\.xml$/ }

SFTP/SCP×GET/PUTのすべての組み合わせに手を入れる必要があったため、実装は随分と骨の折れる作業になりました。

Add ssh.runtime object in CLI

Groovy SSHスタンドアロンJARを利用する場合に ssh.runtime.jar でJAR自身を参照できるようになりました。これにより、リモートホストにJARを転送してスクリプトを実行する処理を簡単に記述できます。まあ、スタンドアロンJARのテストでうれしいだけかもしれませんが。

ssh.run {
    session(ssh.remotes.tester) {
        put from: ssh.runtime.jar, into: '.'
        execute 'java -jar gssh.jar --version'
        execute 'java -jar gssh.jar --help'
    }
}

Skip lecture message from sudo result

sudoコマンドを実行して最初にlecture messageが表示された場合に、コマンドの実行結果にlecture messageが含まれてしまう問題を修正しました。

振り返り

ここに書いている以外にもOS Integration TestをすべてEC2に追い出すとか、Circle CIへの移行に取り掛かるといった改善をやっていました。ビルドやCIの改善に手を付け始めるとあっという間に時間を使ってしまい、気が付けば2.4.0のリリースが月末になってしまいました。

現状ではCIに7〜8分を要しているので、以下の改善に手を付けたいです。

  • DockerのSSHコンテナを利用してOS Integration Testを実行する(EC2は廃止)
  • Plugin Integration Testの対象バージョンを1系に限定する
  • 効率的なキャッシュ

ITのビジネス価値を最大化するには

2016年になってもアジャイルはテストをしないとか、計画を立てないとか、1日10回デプロイするための技法といった誤解が広まっているのは残念すぎる。

まず、ITのビジネス価値を最大化するという視点が必要なはず。事業の売上や費用を改善するためにシステムを活用するのであって、システムを作ること自体は目的にならない。

事業を取り巻く環境は変化が速いので、ITもそれに合わせて変えていく必要がある。1年後にシステムが完成したら事業環境が変化していて役に立たないかもしれない。投資が無駄になるリスクを抑えるために、数週間で軌道修正を繰り返す。

だから、事業環境の変化にITを対応させるためにアジャイルが必要、という話の流れになるはず。システムを作るためにアジャイルが必要とはならない。事業上の必要性からアジャイルを選択する。

ここまできて初めて、ITのビジネス価値を最大化するためにスプリントや自動デプロイといったやり方が出てくる。やり方だけでは不十分で、事業と開発が一体になるための組織構造や開発スキルを評価するための人事制度も必要になる。そもそも、事業環境の変化に対応できる事業計画の立て方が必要なはず。

なので、適度に偉い人が「事業上の必要性」を言い出して「事業環境の変化が速い領域」で試行錯誤するのが王道なんじゃないかなぁ。これはEnterpriseでもServicerでも同じはず。クラウドもこんな感じで広まったはず。

もうアジャイルという言葉をやめた方が誤解が生じなくてよいのでは。マックスバリューデベロップメントあたりでいいと思う*1

*1:誰か突っ込んでくれるかな

TestKitによるGradleプロジェクトのテスト

最近のGradleで導入されているTestKitを使ってみたのでメモします。

TestKitでできること

TestKitを利用すると、Gradleプロジェクトに対するテストを実行できます。例えば、何かの設定ファイルを自動生成するタスクをGradleで定義している場合に、そのタスクに対するテストコードを記述できます。TestKitはSpockと併用できるので、BDDスタイルでテストコードを書くこともできます。

この仕組みを応用すると、Gradleプラグインを適用したプロジェクトに対するテストを実行できます。Gradleプラグインの受け入れテストとも言えますね。詳細は The Gradle TestKit - Gradle User Guide Version 2.14 を参照してください。

使い方

ここでは、テスト対象のプロジェクトがGradle 2.13と1.12の両方でちゃんと動作するかテストする例を考えます。

  • build.gradle - テストを実行するプロジェクト
  • src/test/groovy/ExampleSpec.groovy - テストコード
  • fixture/build.gradle - テスト対象のプロジェクト

まず、テスト対象のプロジェクトを作ります。単にGradleバージョンを表示する雑なビルドスクリプトにしておきます。

// fixture/build.gradle
task show << { println gradle.gradleVersion }

それから、TestKitを利用してテストを実行するプロジェクトを作ります。

// build.gradle
plugins {
    id 'groovy'
    id 'java-gradle-plugin'
}

repositories {
    jcenter()
}

dependencies {
    testCompile('org.spockframework:spock-core:1.0-groovy-2.4') {
        exclude module: 'groovy-all'
    }
}
// src/test/groovy/ExampleSpec.groovy
import org.gradle.testkit.runner.GradleRunner
import spock.lang.Specification
import spock.lang.Unroll

class ExampleSpec extends Specification {

    @Unroll
    def "test should be success on Gradle #version"() {
        when:
        GradleRunner.create()
                .withProjectDir(new File('fixture'))
                .withArguments('show')
                .withGradleVersion(version)
                .forwardOutput()
                .build()

        then:
        noExceptionThrown()

        where:
        version << ['2.13', '1.12']
    }

}

テストを実行してみます。

./gradlew check

テストレポートを確認すると、テストケースが2つ実行されて、それぞれ2.13と1.12が表示されていることがわかると思います。

Gradle SSH Pluginへの適用

Gradle SSH PluginはGradle 1系も動作対象に含めているため、受け入れテストを2.13と1.12で実施しています。これまではGradle Wrapperのプロパティファイルを無理やり書き換えて複数のバージョンでテストを実行しましたが、TestKitを利用することで独立した環境でテストを実行できるようになりました。

以下の流れでビルドを実行しています。

  • プラグインプロジェクトをビルドする(test
  • プラグインプロジェクトの成果物をMaven Localにリリースする(publishToMavenLocal
  • 受け入れテストプロジェクトではMaven Localに存在するプラグインを適用する(buildScriptrepositoriesmavenLocal()を指定し、apply pluginを実行)
  • 受け入れテストプロジェクトを実行する

プラグインの受け渡しはGradle 2.8以降であれば withPluginClasspath() が使えるのですが、今回はGradle 1系でテストする必要があるのでMaven Localを採用しました。

TestKitはプラグイン開発者には便利かもですね。