Android
iOS
Jenkins

iOS・Androidアプリの自動ビルド環境を構築した話

初めに

CYBIRDエンジニア Advent Calendar 2017
20日担当の@kanachaです。
女性向け恋愛シミュレーションの横串チームでアプリの開発・保守をしております。CYBIRD新卒入社3年目です。最近は業務でUnityも触っており、スゥパァなエンジニアを目指して日々修行の毎日です。

19日は(@cy-kenta-takahashi)さんの非エンジニア職の新卒に研修で技術とは何かを伝えるにはどうしたらいいかでした。
彼は同期エンジニアで、私も新卒研修のお手伝いに参加させていただいたのですが、
いやはや、もう3年も経ってしまったんですね。ビビります

内容

弊社の恋愛シミュレーションゲームは、シリーズとして複数のタイトルで展開しているのですが、
今まではプロパーのエンジニアがビルド、署名作業、デプロイ等を手動で行っていたために、コストがバカにならんのが課題でした。
そんなん自動化しとけ!っつーことでそれらをほぼ全て自動化した結果、とても快適になったお話をします。
※Mac環境での説明になります

Jenkinsって??

hallo.png
一言で言えば、「何でも任せて自動化できちゃうすんごいツール」です。
継続的インテグレーション(Continuous Integration)という開発の手法がありまして、
アプリ作成時の品質改善や納期の短縮を実現するためのツールとして様々な場面で活躍しており、あらかじめ「ジョブ」という単位で登録しておいたビルドやテスト、デプロイといった操作をワンクリックするだけで自動で実行してくれちゃいます。
人間が楽をするだけでなく、正確かつ定期的にビルド処理を実行をしたり、実行トリガーやパラメータで細かい設定も出来たり、プラグインで拡張機能を付与したりetc...、
品質、生産性の向上を促進してくれるファンタスティックなオジサマなのです。

さっそく導入

Bitnamiから、既に色々と設定済みなJenkinsをダウンロードできます。
https://bitnami.com/stack/jenkins
画面の指示に従い、インストールします。
ユーザー名やパスワードを設定しますが、Jenkinsへログインする時に利用するので控えておきましょう。

/Applications/jenkins-<バージョン>/manager-osxをダブルクリックで
Jenkinsのマネージャーが起動します。

jenkins1.png
「Manage Servers」タブをクリックし、「Start All」でApache、TomcatをRunning状態にします。
「Welcome」タブに戻り、「Go to Application」をクリックすると、ブラウザが立ち上がり、localhostでJenkinsを開きます。ここで先ほど設定したユーザー名とパスワードが要求されるので、入力してログインします。
あとで設定からユーザー名やパスワードを変更したり、複数ユーザーを登録し、権限管理を行うことも可能です。

Jenkinsの設定

jenkins2.png
Jenkinsの準備ができたので早速、何かジョブを作ってみましょう。
とその前に、自動ビルド環境を構築する上で必要になりそうなpluginを先にインストールしておきます。

「Jenkinsの管理」→「プラグインの管理」の順にクリックします
必要になりそうなpluginをまとめてみました

[GitHub plugin、Git plugin、Git client plugin、GitHub API Plugin]
・jenkinsは標準でGitをサポートしておらず、コチラのpluginを導入することでGithubからcloneが可能になる

[ChatWork Plugin]
・ビルド結果をチャットワークに通知できるようになる

[DeployGate Plugin]
・成果物をDeployGateにそのままアップロードできるようになる

[Flexible Publish Plugin]
・これがあると自動ビルドの条件やトリガーを細かく指定できるようになるので非常に便利

左上の「新規ジョブ作成」をクリックすると、ジョブの名前とジョブのタイプを指定する画面になるので、ジョブ名を記入して「フリースタイル・プロジェクトのビルド」をクリックします。私の場合は、iOS_○○○、GL_×××という風に、プラットフォーム毎にタイトルを分けてジョブを管理しています。

作成したjobは
/Applications/jenkins-<バージョン>/apps/jenkins/jenkins_home/jobs内に生成されます

ジョブの設定

次にジョブの設定を行います。
ジョブを選択後、左上の「設定」を押下します。

[ビルドのパラメータ化]
・チェックボックスをクリックすると文字列入力や真偽値ボタン、選択プルダウンなどをジョブ実行の際のパラメータとして追加することが可能
(環境変数として利用できる)

[ソースコード管理]
・Gitを選択、Repository URLに対象プロジェクトのURLを記入
・Credentialsにread権限のあるgitユーザを登録

[ビルド手順の追加の追加→シェルの実行]
・ジョブ実行の際のコマンドを登録できる

[ビルド後の処理の追加→成果物を保存]
・Android/iOSのビルドの場合は「build/*.ipa」、または「build/outputs/*.apk」みたいな感じで記入しておく
(ここで設定したPATHのファイルがビルド履歴からダウンロードリンクとして表示される)
(PATHの開始地点は/Applications/jenkins-<バージョン>/apps/jenkins/jenkins_home/jobs/<ジョブ名>/workspace)

これだけでiOS、Androidアプリのビルド自動化がほぼできてしまいます。
ジョブの設定が完了したら、
「ビルド実行」を押下するだけで設定した内容通りにJenkinsが仕事をしてくれます。

処理の流れは
① ビルド実行を押下(パラメータを設定している場合は実行前に入力)
② gitからcloneまたは差分がある場合はpullを実行する
③ プロジェクトの用意ができたらそのディレクトリで登録したコマンドを実行
④ 成果物を保存
となります。

iOS、Androidでのビルド設定

jenkinsはカスタマイズ次第で、人によって環境構築手順が多種多様かと思われますが、
ここでは私のiOS、Androidの自動ビルド環境設定を簡単にご紹介します。

iOSでのビルド設定

【署名について】
provisioning profileの登録は
Keychains and Provisioning Profiles Pluginを使用しています。
「Jenkinsの管理」→「Keychains and Provisioning Profiles Management」からローカルのファイルを選択してアップロードすることができ、ジョブ毎に環境変数で登録したprovisioningを呼び出すことが出来ます。

【使用するXcode】
タイトルによっては最新or古いXcodeを利用しているため、ジョブ毎にXcodeを出し分ける必要が出てきます。
私の場合はビルドマシンに複数Xcodeをインストールし、アプリ名をXcode8.3.3、Xcode9.1といった感じにリネームしています。
ジョブ設定内の「ビルドプロセスに環境変数をインジェクト」をチェックし、

DEVELOPER_DIR=/Applications/${XCODE_VERSION}.app/Contents/Developer

で実行パラメータから出し分けております。

【ビルドコマンド】
こちらがiOSビルドで実行しているコマンドです

# .xcodeprojのあるディレクトリへ移動
cd ${WORKSPACE}/<ディレクトリ>

if [ ${PROVISIONING} = "ADHOC" ]; then
provisioning=${ADHOC_PROVISIONING_PROFILE}
codeSignIdentity="iPhone Distribution: HOGEHOGE Co., Ltd. (XXXXXXXX)"
elif [ ${PROVISIONING} = "ENTERPRISE" ]; then
provisioning=${ENTERPRISE_PROVISIONING_PROFILE}
codeSignIdentity="iPhone Distribution: FUGAFUGA CO.,LTD."
fi

# アプリをArchive
xcodebuild \
  -sdk iphoneos \
  -scheme "${TARGETS}" \
  -archivePath "${WORKSPACE}/build/archive/<タイトル>_${TYPE}.xcarchive" \
  -configuration Release \
  CODE_SIGN_IDENTITY="${codeSignIdentity}" \
  PROVISIONING_PROFILE_SPECIFIER="${provisioning}" \
  archive

if [ ${XCODE_VERSION} = "Xcode9.1" ]; then
# Xcode9からの仕様変更 (exportOptions.plistにprovisioning情報を書き込む)
sed -e "s/!env-provisioning!/${provisioning}/g" \
${WORKSPACE}/../Plist/${XCODE_VERSION}/${PROVISIONING}/exportOptionsModel.plist \
> ${WORKSPACE}/../Plist/${XCODE_VERSION}/${PROVISIONING}/exportOptions.plist
fi

# Archiveからipaを吐き出し (Archiveの際に、ジョブ配下のPlistディレクトリ内のexportOptions.plistが必要)
xcodebuild \
  -exportArchive \
  -archivePath "${WORKSPACE}/build/archive/<タイトル>_${TYPE}.xcarchive"  \
  -exportOptionsPlist "${WORKSPACE}/../Plist/${XCODE_VERSION}/${PROVISIONING}/exportOptions.plist" \
  -exportPath "${WORKSPACE}/build/<タイトル>_${TYPE}" 

Xcode pluginというUI操作で簡単に設定できるプラグインもあるのですが、Xcode8系からxcodebuildコマンドの仕様変更により使えなくなってしまったため、一からスクリプトを用意するはめに。。。
adhocかenterpriseで環境を選択したら、xcarchiveを作成し、そのxcarchiveに対してexportArchiveコマンドでipaファイルを出力するという流れです。

ここで注意点が一つ、
exportArchiveを行う前に、Xcode8からはexportOptions.plistというファイルが必要になりました。さらに、Xcode9からはそのexportOptions.plistに追記すべき情報が増えました。
(このplistファイル、Xcode8とXcode9で互換性が無いです。ほんとヒドスギル)

exportOptions.plistの中身は以下のようにしています

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>ad-hoc</string>
    <key>provisioningProfiles</key>
    <dict>
        <key>jp.co.hogehoge.hoge.hogege</key>
        <string>!env-provisioning!</string>
    </dict>
    <key>signingCertificate</key>
    <string>iPhone Distribution: HOGEHOGE Co., Ltd. (XXXXXX)</string>
    <key>installerSigningCertificate</key>
    <string>iPhone Distribution: HOGEHOGE Co., Ltd. (XXXXXX)</string>
</dict>
</plist>

これを/Applications/jenkins-<バージョン>/apps/jenkins/jenkins_home/jobs/<ジョブ名>/Plist/<Xcode9.1 or Xcode8.3.3/><ADHOC or ENTERPRISE>に配置して読ませています。

Androidでのビルド設定

【署名について】
キーストアファイルはまとめてビルドマシンに配置しています。
gradle.propertiesに

storeFile=<キーストアのPATH>
storePassword=<キーストアのパスワード>
keyAlias=<エイリアス名>
keyPassword=<エイリアスのパスワード>

を記入し、

build.gradleに以下を追加する。

android {
    signingConfigs {
        releaseConfig {
            storeFile=file(project.properties.storeFile)
            storePassword=project.properties.storePassword
            keyAlias=project.properties.keyAlias
            keyPassword=project.properties.keyPassword
        }
    }
    buildTypes {
        release {
            signingConfig signingConfigs.releaseConfig
        }
    }
}

これでreleaseビルドの際に自動で署名してくれるようになります。

【ビルドコマンド】
Android Studioのプロジェクトなので

cd <プロジェクト名>
./gradlew clean
./gradlew build

こんだけです
Android大好きです

おわりに

今回はJenkinsのインストールからiOS・Androidのビルド環境作成までをご紹介させて頂きました。
この記事を読んでくださった方の作業効率化のきっかけになれば幸いです。
Jenkinsは非常にカスタマイズ性の高いツールですので、私のビルド環境なんかよりも良い手段が腐るほどあると思います。
ぜひ、ぼくのかんがえたさいきょうの自動ビルド環境を追求してくださいませ。
CYBIRD エンジニア Advent Calendar 明日は、(kenchang100kg)さんの
2017年の振り返り(インフラ)です。お楽しみに!