Gradle SSH Pluginをオフラインで利用する(Fabricぽいことしたい)

# Gradle SSH Pluginをオフラインで利用する

Gradleの本分はビルドツールですが、その柔軟性のおかげで単なるビルドツールを超えたいろいろなオートメーションに応用できる可能性を秘めています。
今回はインフラ作業の自動化にGradleを応用することを試行してみました。一応オフラインで動かすことに成功しましたので、備忘を兼ねてまとめておきます。

なお、本記事は「[Gradleでオフラインビルドの方法を考えてみた](http://wadahiro.hatenablog.com/entry/20120324/1332562767)」のアイデアを若干アレンジして適用したものです。

## 背景
つい最近[Fabric](http://www.fabfile.org/)を使う機会があり、そのシンプルさと実用性の高さに感銘を受けました。
ChefやAnsibleが話題になっていますが、インフラ作業では必要最低限「SSH接続してシェルコマンドを並列実行」「SCP(SFTP)でファイル送受信」ができれば十分、というケースも多いのではないでしょうか。そういう観点では、Fabricの潔い割り切りは良いポイントを突いているなと感じました。

## Fabricを利用するにあたっての課題
環境による制限(大人の事情)によってFabricの導入が制限される場合があります。
Fabricの稼動前提はPython2.5以上なので、最近のLinuxディストリビューションではクリアできることが多いでしょう。問題はFabric本体のインストール方法で、[Installing](http://www.fabfile.org/installing.html)の情報だけでは閉鎖環境で導入する方法が判別できませんでした。(私のPythonスキルが低いためです。。。)
また、仮に導入に必要なモジュールが判別できたとしても、Pythonのモジュールを追加導入する場合はOS全体への影響を考慮する必要があり、導入の許可が下りない場合もあります。

## なぜGradle?
PythonやRubyの追加モジュール導入の許可が下りない環境でも、JavaVM上で稼動するものなら許可が下りる場合が多いです。
JDK(JavaVM)導入済みの環境であれば、基本的にJARファイル一式をコピーするだけで導入できるため、OS全体への影響を考慮する必要がないということが理由だと思われます。
Gradleの場合、Gradleラッパーとバイナリのzip、および依存JARをまとめてパッケージングすることで、ポータブルな環境を構築することができます。

# Gradle SSH Pluginのall-in-one配布物を作る

## Gradle SSH Pluginのビルドスクリプトを修正する
まず、以下よりGradle SSH Pluginのソースをcloneします。
https://github.com/int128/gradle-ssh-plugin
そして、build.gradleに以下のタスクを追加します。

```groovy:build.gradle
task archiveDependencies(type: Zip, dependsOn: jar) {
    classifier = 'all'
    final excludeLibs = ['jsch-0.1.46.jar']
    runtimeLibs = configurations.runtime - configurations.runtime.filter{ it.name in excludeLibs }
    into('dep-libs') {
        from runtimeLibs
        from 'build/libs'
    }
}
```

このタスクはGradle SSH Pluginの実行に必要なすべてのJARを含むZIPファイルを作成します。以下がポイントです。

* configurations.runtime から依存ライブラリを取得し、それをdep-libs/以下にコピーする
* ただし、jsch-0.1.46は除外する (依存関係によって古いJARが入ってしまうためで、オリジナルのbuild.gradleでも同様の処理をしています)
* 作成するZIPファイル名には"-all"を付加する

## 全部入りZIPを作る
プロジェクトのルートディレクトリで以下のコマンドを実行します。

```
./gradlew archiveDependencies
```

無事に実行できれば、build/distributions/ 以下に **gradle-ssh-plugin-0.3.5-SNAPSHOT-all.zip** が生成されます。
これを展開すると、dep-libs/ 以下にすべてのJARが格納されているはずです。

# Gradle SSH Pluginをオフライン実行するプロジェクトを作る

## Gradle SSH Pluginのテンプレートを修正する
まず、以下よりGradle SSH Pluginのテンプレートプロジェクトをcloneします。
https://github.com/gradle-ssh-plugin/template
そして、build.gradleの **buildscript** を以下のように修正します。

```groovy:build.gradle
buildscript {
    //repositories {
    //    mavenCentral()
    //}
    dependencies {
        //classpath 'org.hidetake:gradle-ssh-plugin:0.3.4'
        classpath fileTree(dir:'dep-libs', include:'*.jar')
    }
}
```

## 依存ライブラリを追加する
さきほど作成したZIPを展開し、プロジェクトのルート直下に dep-libs/ をコピーします。

## Gradleラッパーをオフライン対応にする
http://services.gradle.org/distributions/gradle-1.10-all.zip
からGradleのバイナリをダウンロードし、プロジェクトのルート直下にコピーします。(取得元は通常のGradleダウンロードサイトでも大丈夫なはずですが、念のためラッパーの定義に従います。)
次に、Gradleラッパーの初回実行時にこちらのファイルを参照するように、ラッパーの定義ファイル(gradle/wrapper/gradle-wrapper.properties)を修正します。

```properties:gradle/wrapper/gradle-wrapper.properties
#distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
distributionUrl=../../gradle-1.10-all.zip
```

## Gradle SSH Pluginで実行するタスクを定義する
build.gradleを修正し、タスクの定義を追加/変更します。デフォルトでは以下が定義されています。

```groovy:build.gradle
remotes {
    localhost {
        host = 'localhost'
        user = System.properties['user.name']
        identity = file("${System.properties['user.home']}/.ssh/id_rsa")
    }
}

task showPlatformVersion(type: SshTask) {
    session(remotes.localhost) {
        execute('uname -a')
        execute('cat /etc/*-release || true')
    }
}
```

http://gradle-ssh-plugin.github.io/ を参考に、build.gradleをいろいろ書き換えて試してみましょう。

## Gradle SSH Pluginを実行する
プロジェクトのルートディレクトリで以下のコマンドを実行します。

```
./gradlew showPlatformVersion
```

ビルドエラーが出なければ、Gralde SSH Pluginのオフライン実行は成功です。
なお、テンプレートプロジェクトのデフォルト設定ではlocalhostに現在のユーザーでRSA鍵認証ログインを試みます。そのため、Gradle SSH Pluginの実行に成功していても"Connection Refused"となる場合があります。

# 手っ取り早く試したい人のために
以上の作業を実施済みの状態でGitHubに用意してありますので、ご活用ください。

## Gradle SSH Pluginオフライン実行テンプレートプロジェクト
https://github.com/nobusue/gradle-ssh-plugin-template

## Gradle SSH Plugin all-in-one ZIP作成対応版
https://github.com/nobusue/gradle-ssh-plugin