• 日本語
  • English
  • 中文
MENU
CREATE A PLATFORM FOR DIVERSITY
Web, Game, App, Illustration
https://monstar-lab.com/
Android
2015/02/03
Travis CIでAndroidアプリをビルドしてDeployGateに置く

継続的デリバリーをAndroidアプリ開発でもしたい!ということで、

– GitHub https://github.com/
– Travis CI https://travis-ci.org/
– DeployGate https://deploygate.com/

の3つを使って、

1. GitHubにPUSHする
2. Travis CIが動いてapkが作られる
3. apkをDeployGateにアップロードされる
4. DeployGateアプリが入っている端末に通知が届く

ということを実現したいと思います。
これが出来れば、いちいちapkファイルをメールで送ったり、ファイル共有したりしなくても、開発中の最新バージョンをいつでも確認してもらえます。

.travis.yml の設定

language: android
env:
  global:
    # deploygate
    - DEPLOYGATE_USER=XXXX
    - secure: "ここには暗号化された文字列を設定"
    # secret_strings.xml
    - secure: "ここには暗号化された文字列を設定"
    - secure: "ここには暗号化された文字列を設定"
    - secure: "ここには暗号化された文字列を設定"
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.xml(twitterのsecret tokenなど)の設定

これらは、アプリ毎に変わってくる部分です。
作成については、下記のようなコマンドでそれぞれ作成しました。

travis encrypt GCM_SENDER_ID=9999999999999

個人プロジェクトでお金が無い場合、GitHubはPublicなので、認証キーなどはそのまま書けません。
ただし、ビルドするときには必要なので、暗号化して管理しておき、Travis CIに復号化してもらって、後述のgradle taskでxmlに書き込みます。
(Privateリポジトリの場合も、認証情報はGitでは管理しないほうがいい場合もあると思います。)

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()
        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して同じファイルを置き換えます。 きっと、もっと良いやり方あると思いつつ、ググっても出てこなかったので、力技です。(リポジトリにキー情報は置けないけど、CIでapkを作るときには必要っていう場合、みんなどうやってるんだろう?)

まとめ

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

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

あとは、テストコード書いて、テストが通過した場合のみアプリの配布とかされるように出来ればなーと思います。

Archives