Travis CIでAndroidアプリをビルドしてDeployGateに置く

継続的インテグレーションAndroidでもしたい!ということで、

ここらへんを使い、

  1. GitHub(Publicリポジトリ)にPUSH
  2. Travis CIが動いてapk作成
  3. apkをDeployGateにアップロード
  4. DeployGateアプリが入っている端末に通知が行く

ということを実現したいと思います。

対象は、現在全リファクタリング中のこちら。

https://github.com/noboru-i/kyouen-android/tree/renew

.travis.yml の設定

language: android
env:
  global:
    # deploygate
    - DEPLOYGATE_USER=noboru-i
    - secure: "SOev0rrhdJ6fp5CkJeQAh6mm6EmmWEWCVrSt7hn3mvjSbaz/yKdmiGsHp8NcTeLZrXuk6ay6mFee3TVqsT17Xn5+cGG5nnWFPqI2Os3AlSWdHkhLrv8f3KdfwJeTiBSymw4TDWSd/kVCB42q51BdnODda+GozCymA6qoZpf/GQs="
    # secret_strings.xml
    - secure: "ZXnouETJ67n/DCjCFPdWoIm3eo0WsYhfuy9Y25IjYn6gij4F8v038bjYr+Z5QBsnLri1mU0QQeNy14cPaaYBoblTsmPm0cZ+Vv0wmYgAkVc2C+E67k/YUAD0puvUzUGsQ1z6N7gQ0tFjL4l4map/4iEkGtney6OYdyuljE75D2g="
    - secure: "Sqi+dwZcd6qGpBLYUq+deqQBSX056N0eoQ9RfMjWB7gG/WfQn4XQaTF13jKdqp0MKzRj8P61uunoxZIpHNYNWeqzxfIpXKrzOdGv85r8PnbjDgH87N22ufqwPdbfAVydUEPP8hEEJ5vbCN2Fyoq1CHPNjwBpMIiD27LMVjiytn8="
    - secure: "iUO0ldYPT3jYjoRafH0Milc3DNhgqzZQGk7tFkvg7WPF+YPEYDOMjwmhY0kTDjbxU+iqkHE7WOL66MtJSlvTJjNfs2uWqrXrmgMAXLusbRukscJmL2ZwGhNwBi4tOOl4V1FO8NRqYcdRkkHgJRUXyKAafLrdNePVm7uWUwjl2e0="
    - secure: "PCeJAQ8/XrDWktlTbKtJLCcjhjOfsw/tcPPCrG1urvQMJrgzi6m6pTWo62RSpWVo1iYePiqvDgPGACrT082vtnoJ7TJ3FZEhty1xsDUhDBJ9qvmHEPpxLrtLGwZ1+doT/yxinQAqqucx8vsFJcTh8zOrGLknwEvqxhNj5HHKzkg="
    - secure: "MyGGNdkyv+qEf8yhORcSzjsHbDjT2yILka/aMYgInt0oyF3T6O6JwwRvrXHEulPVHWsWu4LCxX8e3HeyyQ0OkIjc67nwIsajCDwDlVUcnkjqyV3r9xPn0HlltwvS63QuQGln6NQIUBk9nVIuaA+vno7bod1Px0VESu9kDgUiqgI="
notifications:
  email: false
android:
  components:
    - build-tools-19.1.0
    - android-19
    - sysimg-19
    - extra-android-support
  licenses:
    - android-sdk-license-5be876d5
    - '.*intel.+'
before_install:
  - android list sdk --no-ui --all --extended
script:
  - ./gradlew uploadDeployGateDebug

env.globalには下記を記載しています。

  • deploygateの設定
  • secret_strings.xmltwitterのsecret tokenなど)の設定

作成については、下記のコマンドで。

travis encrypt GCM_SENDER_ID=9999999999999

これを、後述のgradle taskでxmlに書き込みます。

Projectのbuild.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:0.12.+'
        // deploygate
        classpath 'com.deploygate:gradle:0.6'
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

DeployGateのgradle pluginをここで設定しています。

アプリのbuild.gradle

apply plugin: 'com.android.application'
apply plugin: 'deploygate'

android {
    // Error:duplicate files during packaging of APK /XXX/app-debug-unaligned.apk 対策
    packagingOptions {
        exclude 'META-INF/license.txt'
        exclude 'META-INF/notice.txt'
    }

    compileSdkVersion 19
    buildToolsVersion "19.1.0"

    defaultConfig {
        applicationId "hm.orz.chaos114.android.tumekyouen"
        minSdkVersion 12
        targetSdkVersion 19
        versionCode 18
        versionName "0.10.0"
    }
    buildTypes {
        release {
            runProguard false
//            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    lintOptions {
        abortOnError false
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:support-v4:19.0.+'
    compile 'com.android.support:appcompat-v7:19.+'

    // deploygate
    compile 'com.deploygate:sdk:3.1'
}
apt {
    arguments {
        androidManifestFile variant.processResources.manifestFile
        resourcePackageName 'hm.orz.chaos114.android.tumekyouen'
    }
}

deploygate {
    userName = "$System.env.DEPLOYGATE_USER"
    token = "$System.env.DEPLOYGATE_TOKEN"

    apks {
        debug {
            sourceFile = file("${project.buildDir}/outputs/apk/app-debug-unaligned.apk")
        }
    }
}

uploadDeployGateDebug.dependsOn assembleDebug

task secretKeyReplace {
    description 'Replace secret keys from system environment.'
    doLast {
        def filePath = "app/src/main/res/values/secret_strings.xml"
        def br = new BufferedReader(new FileReader(filePath))
        def lines = br.readLines()
        br.close()

        // key情報の抽出
        def secretKeys = new HashSet()
        def rExpression = /INPUT_YOUR_([A-Z_]+)/
        for (String line : lines) {
            if (line.contains("INPUT_YOUR_")) {
                def matcher = (line =~ rExpression)
                secretKeys << matcher[0][1]
            }
        }

        // 文字列の置換
        def newLines = new ArrayList<String>()
        for (String line : lines) {
            String newLine = line
            for (String key : secretKeys) {
                if (!System.getenv()[key]) {
                    continue
                }
                newLine = newLine.replace("INPUT_YOUR_" + key, System.getenv()[key])
            }
            newLines << newLine
        }

        def pw = new PrintWriter(new BufferedWriter(new FileWriter(filePath)))
        for (def line : newLines) {
            pw.println(line)
        }
        pw.close()
    }
}

preBuild.dependsOn secretKeyReplace

buildToolsVersion は、最新が20.0.0だけど、ymlに書いた android-sdk-license-5be876d5 がまだ対応してないようなので、19.1.0になってます。

deploygateタスクの設定(ユーザ名 / パスワード)は環境変数から取得しています。
環境変数への設定は.travis.ymlに書いてあります。
DEPLOYGATE_TOKEN も、暗号化して書いてあります。

secretKeyReplace taskで、secret_strings.xmlにある、"INPUT_YOUR_XXX"を取得、その"XXX"をキーとして環境変数を取得、replaceして同じファイルを置き換えます。
きっと、もっと良いやり方あると思うんだけど、ググっても出てこなかったので、力技です。
Publicリポジトリにキー情報は置けないけど、Travis CIでapkを作るときには必要。みんなどうやってるんだろう?

まとめ

というわけで、GitにPUSHするとDeployGateでアプリが配布されるところまで出来ました。

Publicリポジトリに置けない認証情報を設定する方法が、あんまりいけてないのはなんとかしたいところ。

あとは、テストコード書いて、リグレッションテストを回していきたいと思います。
Robolectricを使うつもりで書いてるので、まとまったらまた書こうと思います。