Titanium で CircleCI して fabric.io の Beta で .ipa を配布するまで

## Titanium でも自動化したい方へ

### 一連の流れ

1. GitHub にプッシュ
2. CircleCI が拾う
3. Mocha（in ti-slag）テスト
4. Titanium ビルド
5. Beta へサブミット

### 準備

[一度アプリをビルドしてから Fabric の Crashlytics を導入しておきます。](http://qiita.com/k0sukey/items/4e7f30491210d8f38246)記事中、2. の Paste in the following command にある、Crashlytics の API_KEY と BUILD_SECRET が必要になります。スクリプトの第一引数が API_KEY、第二引数が BUILD_SECRET になるのでどこかにメモしておいてください。

CircleCI の project settings → Experimental Settings で Build iOS project を On にして、ページ右下にあるチャットボタンから iOS のビルドができるようにお願いしておきます。

CircleCI の project settings → Environment variables で各種環境変数を設定しておきます。[iOS アプリの継続ビルドを CircleCI に変更した](http://ja.ngs.io/2015/03/24/circleci-ios/)を参考にさせていただきました。

| Name | Value |
|-----:|:------|
| APPLE_CERTIFICATE | Apple Worldwide Developer Relations Certification Authority を Base64 エンコードしたもの |
| APPLE_DEVELOPER_ID | developer.apple.com の Apple ID |
| APPLE_DEVELOPER_PASSWORD | developer.apple.com のパスワード |
| CLASHLYTICS_API_KEY | Crashlytics の API_KEY |
| CLASHLYTICS_BUILD_SECRET | Crashlytics の BUILD_SECRET |
| CLASHLYTICS_GROUP_ALIASE | Beta で配布する際のグループ |
| DISTRIBUTION_CERTIFICATE | Distribution 証明書を Base64 エンコードしたもの |
| DISTRIBUTION_NAME | Distribution 証明書の名前 |
| DISTRIBUTION_P12KEY | Distribution p12キーを Base64 エンコードしたもの |
| P12KEY_PASSWORD | Distribution p12キーのパスワード |
| PROVISIONING_PROFILE | プロビジョニングプロファイルの UUID |

### circle.yml

CircleCI の Build iOS project を On にすると、nvm が使えなくなってしまうため、machine:pre でインストールしておきます。machine:environment の XCODE_SCHEME と XCODE_PROJECT は tiapp.xml の name で OK です（ぶっちゃけ適当で OK なはずです、無いと怒られるだけですので...）。

テストは Xcode のテストが動こうとするので、test:override で npm test が動くように変更しておきます。test:pre で Alloy のコンパイルを忘れずに行いましょう。Titanium の ビルドでも良いのですが、SDK のインストールにそこそこ時間がかかるので、Alloy のコンパイルで終わらせたほうが良いと思います（テストが失敗した時のことを考えて）。今回は [Mocha（ti-slag）](http://k0sukey.github.io/ti-slag/)でテストしています。gulp jshint はお好きにどうぞ。

テストが通過したら Titanium SDK をインストールし、実際にビルドします。その後、Crashlytics/submit で配布します。プロビジョニングプロファイルのダウンロードには cupertino を利用していますが、[最新版だとエラーが出ます](http://hack.aipo.com/archives/10810/)のでバージョンを指定しています。

```yaml
machine:
  pre:
    - curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.26.1/install.sh | bash
  timezone:
    Asia/Tokyo
  xcode:
    version: "6.4"
  node:
    version: "0.10.34"
  environment:
    XCODE_SCHEME: App
    XCODE_PROJECT: App.xcodeproj

dependencies:
  pre:
    - npm install -g titanium alloy tisdk
    - npm install

test:
  pre:
    - gulp jshint
    - alloy compile --config platform=ios
  override:
    - npm test

deployment:
  adhoc:
    branch: adhoc
    commands:
      - sudo gem install cupertino -v 1.3.2
      - /bin/bash misc/keychain.sh
      - tisdk install 4.0.0.GA
      - /bin/bash misc/adhoc.sh
```

### package.json

テストスクリプトと依存関係は以下のとおり。テストスクリプト自体は [ti-slag Titanium faker API, Titanium app running on Node.js](http://k0sukey.github.io/ti-slag/) を参考にしてください。ぼくはカバレッジまでとっています。

```json
{
  "scripts": {
    "test": "mocha --no-timeouts test.js"
  },
  "devDependencies": {
    "gulp": "^3.9.0",
    "gulp-jshint": "^1.11.2",
    "gulp-load-plugins": "^0.10.0",
    "istanbul": "^0.3.17",
    "jshint-stylish": "^2.0.1",
    "lodash": "^3.10.0",
    "mocha": "^2.2.5",
    "moment": "^2.7.0",
    "request": "^2.60.0",
    "signal-exit": "^2.1.2",
    "ti-slag": "0.0.23"
  }
}
```

### gulpfile.js

gulp jshint ですね。これはお好みでどうぞ。ぼくは jsbeautifier もしています。

```js
var gulp = require('gulp'),
	plugin = require('gulp-load-plugins')();

gulp.task('jshint', function(){
	return gulp.src('app/controllers/**/*.js')
		.pipe(plugin.jshint())
		.pipe(plugin.jshint.reporter('jshint-stylish'))
		.pipe(plugin.jshint.reporter('fail'));
});
```

### keychain.sh

こちらも[iOS アプリの継続ビルドを CircleCI に変更した](http://ja.ngs.io/2015/03/24/circleci-ios/)を参考にさせていただきました。

```bash
#!/bin/sh

DIR=tmp/certs
KEYCHAIN=$HOME/Library/Keychains/ios-build.keychain
KEYCHAIN_PASSWORD=`openssl rand -base64 48`
rm -rf $DIR
mkdir -p $DIR
echo $APPLE_CERTIFICATE | base64 -D > $DIR/apple.cer
echo $DISTRIBUTION_P12KEY | base64 -D > $DIR/dist.p12
echo $DISTRIBUTION_CERTIFICATE | base64 -D > $DIR/dist.cer
security create-keychain -p "$KEYCHAIN_PASSWORD" ios-build.keychain
security import $DIR/apple.cer -k $KEYCHAIN -T /usr/bin/codesign
security import $DIR/dist.cer  -k $KEYCHAIN -T /usr/bin/codesign
security import $DIR/dist.p12  -k $KEYCHAIN -T /usr/bin/codesign -P "$P12KEY_PASSWORD"
security list-keychain -s $KEYCHAIN
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN
rm -rf $DIR

PROVISIONING=~/Library/MobileDevice/Provisioning\ Profiles
mkdir -p "$PROVISIONING"
ios profiles:download:all --type distribution -u $APPLE_DEVELOPER_ID -p $APPLE_DEVELOPER_PASSWORD --team "$DISTRIBUTION_NAME"
echo "./App.mobileprovision -> $PROVISIONING_PROFILE.mobileprovision"
mv ./App.mobileprovision "$PROVISIONING/$PROVISIONING_PROFILE.mobileprovision"
rm -rf *.mobileprovision
```

### adhoc.sh

Titanium ビルドして、Crashlytics/Submit しています。

```bash
#!/bin/sh

ti build --platform ios --target dist-adhoc --distribution-name "$DISTRIBUTION_NAME" --pp-uuid $PROVISIONING_PROFILE --output-dir ./
misc/Crashlytics.framework/submit $CLASHLYTICS_API_KEY $CLASHLYTICS_BUILD_SECRET -groupAliases $CLASHLYTICS_GROUP_ALIASE -notifications YES -ipaPath ./App.ipa
```

### まとめ

というわけで、GitHub の adhoc ブランチにプッシュすると、テストしてビルドして配布するところまで Titanium でも自動化することができました。通常は develop ブランチで運用して、こちらはテストまで動く感じです。最終的には release ブランチで iTunes Connect へサブミットまでやっちゃいたい感じですね！