メモ2ブログ

メモtoウェブログ。旧ブログはこちら。 http://sakebook.blogspot.jp/

GradleスクリプトからShellを実行

ビルド終了時に通知を飛ばしたい。

apkをビルドして、deploygateに挙げ終わったら、Slackなどに自動で通知をしたいと思いました。

Slackにテスト投稿がかさむので、PushBulletで試しに検証しました。

execute

//build.gradle
// 指定したデバイスに通知が送られます。
task push1(type:Exec) {
    "curl -u [token]: https://api.pushbullet.com/v2/pushes -d device_iden=[device_iden] -d type=note -d title=title -d body=body".execute()
}

通知は送れたのですが、build.gradleの同期の度に実行されて非常によろしくないです。
なので、次のように書きます。

executable

//build.gradle
task push2(type: Exec) {
    executable "sh"
    args "-c", "curl -u [token]: https://api.pushbullet.com/v2/pushes -d device_iden=[device_iden] -d type=note -d title=title -d body=body"
}
//build.gradle
task push3 << {
    exec {
        executable "sh"
        args "-c", "curl -u [token]: https://api.pushbullet.com/v2/pushes -d device_iden=[device_iden] -d type=note -d title=title -d body=body"
    }
}

executableは実行するコマンドを入力します。 pythonなども実行することも出来ます。
上の2つはほぼ同じ意味ですが、push2execの中身が、タスクの実行時ではなく設定時に適用されるという違いがあります。push1が、同期の度に実行されてしまったのはそのためです。
push1でも次のように書くことで、同期の度に実行されることを防げます。

//build.gradle
task push1 << {
    "curl -u [token]: https://api.pushbullet.com/v2/pushes -d device_iden=[device_iden] -d type=note -d title=title -d body=body".execute()
}
//type:Execを入れると、設定時に実行可能な引数がないということになり、エラーになります。

また、次のような書き方も出来ます。

commandLine

//build.gradle
task push4 (type: Exec) {
    commandLine 'curl', '-u', '[token]:', 'https://api.pushbullet.com/v2/pushes', '-d', 'device_iden=[device_iden]', '-d', 'type=note', '-d', 'title=title', '-d', 'body=body'
}

push4では、コマンドラインで入力する場合と同じように、引数も同様の扱いとして書きます。

たとえ一行でもこうなってくると見にくいですね。スクリプトにしましょう。

script作成

#pushBullet.sh
#!/bin/sh
curl -u [token]: https://api.pushbullet.com/v2/pushes -d device_iden=[device_iden] -d type=note -d title=title -d body=body

注意として、実行権限を与えないと動かない場合があります。

$ chmod 755 pushBullet.sh

完成

最終的に、こうなりました。

//build.gradle
task push5(type: Exec, dependsOn: 'uploadDeployGateRelease') {
    workingDir '../'
    executable "./pushBullet.sh"
    args System.properties["pushBullet_token"], System.properties["pushBullet_device_iden"],
            "title", "body"
}
#pushBullet.sh
#!/bin/sh
curl -u $1: https://api.pushbullet.com/v2/pushes -d device_iden=$2 -d type=note -d title=$3 -d body=$4

注意として、タスクが記述されているbuild.gradleをカレントディレクトリとするので、スクリプトがある場所を指定してあげなければなりません。
また、tokenなどのリポジトリに含めたくない情報は、~/.gradle/gradle.propertiesに記入しましょう。

次のようにタスクを実行すると、deploygateのアップロードが完了するとpush通知が届きます。

$ ./gradlew clean assembleRelease uploadDeployGateRelease push5
  • dependsOnで依存関係を示したので、次のように入力しても同じです。
$ ./gradlew clean assembleRelease push5

実行するタスクを変更したくない場合は、次のようにタスクの依存関係を定義することも出来ます。

//build.gradle
...
deploygate {
    ...
}
...
task push5 (...) {
    ...
}
...
uploadDeployGateRelease.finalizedBy push5

上記のように記述すると uploadDeployGateReleaseタスクの終了時にpush5タスクが実行されます。
便利ですが、uploadDeployGateRelease成功・失敗に関わらずpush5タスクが実行されるので、使う場面を考える必要があります。

ひとまずこれで、暴投をせずに連携できるようになりました。

参考

Run shell command in gradle but NOT inside a task / stackoverflow

how to execute shell command? source or dot doesn't work with exec / Gradle Forums

第15章 タスク詳解 / Gradle 日本語ドキュメント