<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     >
  <channel>
    <title>mojavy.com</title>
    <link>http://mojavy.com/blog</link>
    <description></description>
    <pubDate>Tue, 07 Aug 2012 15:30:00 GMT</pubDate>
    <generator>Blogofile</generator>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>jenkinsでiosアプリをビルドしてwifi経由で実機インストールする</title>
      <link>http://mojavy.com/blog/2012/08/07/jenkins-ios-build/</link>
      <pubDate>Tue, 07 Aug 2012 15:30:00 JST</pubDate>
      <category><![CDATA[jenkins]]></category>
      <category><![CDATA[ios]]></category>
      <guid isPermaLink="true">http://mojavy.com/blog/2012/08/07/jenkins-ios-build/</guid>
      <description>jenkinsでiosアプリをビルドしてwifi経由で実機インストールする</description>
      <content:encoded><![CDATA[<p><img alt="jenkins" src="/images/jenkins-logo.png" /></p>
<p>はまりどころが多くて大変だったので今後同じことをやろうとする人のために一連の作業をメモしておきます。</p>
<h2 id="_1">目的</h2>
<ul>
<li>jenkinsでiOSアプリをビルドする</li>
<li>ビルドしたアプリは、UDIDの登録なしに実機インストールして確認してもらいたい</li>
</ul>
<h2 id="_2">手順</h2>
<h3 id="ios-developer-enterprise-program">iOS Developer Enterprise Program への参加</h3>
<p>実機インストールするだけなら通常のデベロッパーアカウントでも大丈夫ですが、UDIDの登録なしにwifiから配布するためにはエンタープライズプログラムに参加する必要があります。</p>
<p>エンタープライズプログラムに参加すればapp storeを経由せずに自由にインストールできるようになりますが、アクセスできるのが特定の組織内に限定されるように適切に制限をかけなければ規約違反になります。</p>
<p><a href="https://developer.apple.com/jp/programs/ios/enterprise/">https://developer.apple.com/jp/programs/ios/enterprise/</a></p>
<h3 id="xcode">xcodeの設定</h3>
<p><a href="http://golog.plus.vc/iphone/3355/">http://golog.plus.vc/iphone/3355/</a> などを参考に、xcodeからprovisioning profileを設定します。</p>
<p>また、最近のxcodeではデフォルトではarmv7用バイナリしかビルドしないので、armv6用もビルドするように設定しておきます。
Build SettingsのArchitectures に 'armv6' を手動で追記すれば大丈夫です。</p>
<h3 id="_3">実機確認</h3>
<p>jenkinsでビルドする際は後述のビルドスクリプトを使うのですが、provisioning profileの設定等が正しくされていることを確認するために、一旦ここでxcodeのUI上で作成したipaファイルをiPhone構成ユーティリティを使って実機インストールしてみます。
xcodeでipaファイルをビルドする方法はすぐにでてくると思うので、適宜調べて下さい。</p>
<p>ここで正常にインストールできなければ、provisioning profile等の状態を確認してください。</p>
<h3 id="jenkins">jenkinsのセットアップ</h3>
<p>jenkinsを用意します。ビルド専用macが用意できない場合でも、開発に用いているmacをJNLP経由でそのままスレーブとして使うことができます。</p>
<p>jenkins自体の細かいセットアップ方法は割愛しますが、JNLP経由でスレーブを起動する場合は以下のようなスクリプトをバックグランドで起動するようにしておくと便利です。</p>
<div class="pygments_borland"><pre><span class="c">#!/bin/sh</span>

<span class="nb">eval</span> <span class="sb">`</span>ssh-agent<span class="sb">`</span>
ssh-add ~/.ssh/id_rsa
java -jar slave.jar -jnlpUrl http://jenkins.example.com/computer/mac1/slave-agent.jnlp
</pre></div>

<p>ここでの注意点は以下のとおりです。</p>
<ul>
<li>gitやsvnにssh経由でアクセスする場合は、パスワード無しでチェックアウトできるように適切に設定しておく必要がある。(上記の場合はssh-agentをあらかじめ起動するようにして、パスワード入力は起動時のみになるようにしています。)</li>
<li>keychainに配布に使用するデベロッパーアカウントの証明書を登録しておく。</li>
</ul>
<h3 id="_4">ビルドスクリプトの作成</h3>
<p>以下のようなmakefileをxcodeプロジェクトのトップディレクトリに配置します。
(お好みのビルドツールを利用してください)</p>
<p>ソースコードのコンパイルはxcodebuild, ipaファイルの生成はxcrunというコマンドを使います。</p>
<div class="pygments_borland"><pre><span class="nv">APP_NAME</span> <span class="o">=</span> MyApp
<span class="nv">SRCTOP</span> <span class="o">=</span> <span class="k">$(</span>shell <span class="nb">pwd</span><span class="k">)</span>
<span class="nv">RELEASE_BUILD_DIR</span> <span class="o">=</span> <span class="k">$(</span>SRCTOP<span class="k">)</span>/build/Release-iphoneos
<span class="nv">BUILD_HISTORY_DIR</span> <span class="o">=</span> <span class="k">$(</span>SRCTOP<span class="k">)</span>/build
<span class="nv">DEVELOPER_NAME</span> <span class="o">=</span> <span class="s2">&quot;iPhone Distribution: XXXX.&quot;</span>
<span class="nv">PROVISIONING_PROFILE</span> <span class="o">=</span> path/to/in_house.mobileprovision

all:: dist
.PHONY: clean compile dist

dist: compile
    /usr/bin/xcrun -sdk iphoneos PackageApplication -v <span class="k">${</span><span class="nv">RELEASE_BUILD_DIR</span><span class="k">}</span>/<span class="k">${</span><span class="nv">APP_NAME</span><span class="k">}</span>.app -o <span class="k">${</span><span class="nv">BUILD_HISTORY_DIR</span><span class="k">}</span>/<span class="k">${</span><span class="nv">APP_NAME</span><span class="k">}</span>.ipa --sign <span class="k">${</span><span class="nv">DEVELOPER_NAME</span><span class="k">}</span> --embed <span class="k">${</span><span class="nv">PROVISIONING_PROFILE</span><span class="k">}</span>
    ./gen_dist_page.sh

compile:
    xcodebuild

clean:
    rm -rf build
</pre></div>

<p>配布用のplistとhtmlを生成するスクリプト(gen_dist_page.sh)は以下のようになります。(BUILD_NUMBERなどの変数はjenkinsの環境変数から取得できます)</p>
<div class="pygments_borland"><pre><span class="nv">PACKAGE_NAME</span><span class="o">=</span>MyApp

<span class="nv">PLIST</span><span class="o">=</span>build/<span class="k">${</span><span class="nv">PACKAGE_NAME</span><span class="k">}</span>.plist
<span class="nv">INSTALL_PAGE</span><span class="o">=</span>build/<span class="k">${</span><span class="nv">PACKAGE_NAME</span><span class="k">}</span>.html

cat <span class="s">&lt;&lt;__PLIST__ &gt; $PLIST</span>
<span class="s">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
<span class="s">&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;</span>
<span class="s">&lt;plist version=&quot;1.0&quot;&gt;</span>
<span class="s">  &lt;dict&gt;</span>
<span class="s">    &lt;key&gt;items&lt;/key&gt;</span>
<span class="s">    &lt;array&gt;</span>
<span class="s">      &lt;dict&gt;</span>
<span class="s">        &lt;key&gt;assets&lt;/key&gt;</span>
<span class="s">        &lt;array&gt;</span>
<span class="s">          &lt;dict&gt;</span>
<span class="s">            &lt;key&gt;kind&lt;/key&gt;</span>
<span class="s">            &lt;string&gt;software-package&lt;/string&gt;</span>
<span class="s">            &lt;key&gt;url&lt;/key&gt;</span>
<span class="s">            &lt;string&gt;${DIST_URL}/build/${PACKAGE_NAME}.ipa&lt;/string&gt;</span>
<span class="s">          &lt;/dict&gt;</span>
<span class="s">          &lt;dict&gt;</span>
<span class="s">            &lt;key&gt;kind&lt;/key&gt;</span>
<span class="s">            &lt;string&gt;display-image&lt;/string&gt;</span>
<span class="s">            &lt;key&gt;needs-shine&lt;/key&gt;</span>
<span class="s">            &lt;true/&gt;</span>
<span class="s">            &lt;key&gt;url&lt;/key&gt;</span>
<span class="s">            &lt;string&gt;http://jenkins.example.com/job/myapp/ws/icon.png&lt;/string&gt;</span>
<span class="s">          &lt;/dict&gt;</span>
<span class="s">        &lt;/array&gt;</span>
<span class="s">        &lt;key&gt;metadata&lt;/key&gt;</span>
<span class="s">        &lt;dict&gt;</span>
<span class="s">          &lt;key&gt;bundle-identifier&lt;/key&gt;</span>
<span class="s">          &lt;string&gt;com.example.myapp.MyApp&lt;/string&gt;</span>
<span class="s">          &lt;key&gt;kind&lt;/key&gt;</span>
<span class="s">          &lt;string&gt;software&lt;/string&gt;</span>
<span class="s">          &lt;key&gt;subtitle&lt;/key&gt;</span>
<span class="s">          &lt;string&gt;b${BUILD_NUMBER}&lt;/string&gt;</span>
<span class="s">          &lt;key&gt;title&lt;/key&gt;</span>
<span class="s">          &lt;string&gt;${PACKAGE_NAME}&lt;/string&gt;</span>
<span class="s">        &lt;/dict&gt;</span>
<span class="s">      &lt;/dict&gt;</span>
<span class="s">    &lt;/array&gt;</span>
<span class="s">  &lt;/dict&gt;</span>
<span class="s">&lt;/plist&gt;</span>
<span class="s">__PLIST__</span>

cat <span class="s">&lt;&lt;__HTML__ &gt; $INSTALL_PAGE</span>
<span class="s">&lt;html&gt;</span>
<span class="s">  &lt;head&gt;</span>
<span class="s">    &lt;title&gt;${PACKAGE_NAME}: build #$BUILD_NUMBER&lt;/title&gt;</span>
<span class="s">    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0&quot;&gt;</span>
<span class="s">    &lt;/head&gt;</span>
<span class="s">    &lt;body&gt;</span>
<span class="s">    &lt;div style=&quot;text-align: center; vertical-align: middle; margin-top: 100px&quot;&gt;</span>
<span class="s">      &lt;div style=&quot;padding: 10px;&quot;&gt;$JOB_NAME&lt;/div&gt;</span>
<span class="s">      &lt;a href=&quot;itms-services://?action=download-manifest&amp;url=${DIST_URL}/${PLIST}&quot;</span>
<span class="s">         style=&quot;border: none; text-decoration: none; color: #888;&quot;&gt;</span>
<span class="s">        &lt;img src=&quot;http://jenkins.example.com/job/myapp/ws/demo.png&quot;/&gt;</span>
<span class="s">        &lt;br /&gt;&lt;br /&gt;</span>
<span class="s">        Install ${PACKAGE_NAME} b$BUILD_NUMBER</span>
<span class="s">      &lt;/a&gt;</span>
<span class="s">    &lt;/div&gt;</span>
<span class="s">  &lt;/body&gt;</span>
<span class="s">&lt;/html&gt;</span>
<span class="s">__HTML__</span>
</pre></div>

<p>htmlはリンク先さえあっていれば大丈夫なのですが、plistの方はいくつか注意が必要です。</p>
<ul>
<li>display-imageは必須。iOSのバージョンによってはなくてもよいようなのですが、iOS 5.1.1だとdisplay-imageがplistにないと「Appをダウンロードできません」というエラーになります。</li>
<li>bundle-identifierはxcodeのBundle Identifierと一致させる。</li>
</ul>
<p>ここではxcodeデフォルトのsdkバージョンを使用していますが、必要に応じてxcodebuild等のコマンドラインオプションを追加してください。</p>
<p>参考:  <a href="https://developer.apple.com/jp/devcenter/ios/library/documentation/FA_Wireless_Enterprise_App_Distribution.pdf">https://developer.apple.com/jp/devcenter/ios/library/documentation/FA_Wireless_Enterprise_App_Distribution.pdf</a></p>
<h3 id="jenkinsjob">jenkinsのjob作成</h3>
<p>通常通りjenkinsのjobを作ります。適切にソースコードをチェックアウトして、ビルドフェーズで上記スクリプトを実行するようにします。</p>
<div class="pygments_borland"><pre>make clean <span class="o">&amp;&amp;</span> make <span class="nv">DIST_URL</span><span class="o">=</span><span class="k">${</span><span class="nv">BUILD_URL</span><span class="k">}</span>artifact
</pre></div>

<p>ビルドに成功するとipa, html, plistファイルが生成されるので、これらを成果物として保存します。
成果物のhtmlページにiPhoneでアクセスしてみて、アプリがインストールできれば成功です。</p>
<h2 id="_5">その他</h2>
<p>今回は触れませんでしたが、以下についても考慮しておいたほうがよいと思います。</p>
<ul>
<li>iOSのSDKバージョン</li>
<li>依存ライブラリ</li>
<li>プロビジョニングプロファイルの管理</li>
</ul>
<p>また、<a href="https://testflightapp.com/">TestFlight</a> というサービスもあるのでそちらの利用も検討してみてもいいと思います。</p>
<h2 id="_6">まとめ</h2>
<p>iOSアプリをコマンドラインからビルドするスクリプトを作成して、jenkins上でビルドから配布までを行なうための手順を紹介しました。</p>
<h2 id="_7">参考</h2>
<ul>
<li><a href="https://developer.apple.com/jp/programs/ios/enterprise/">https://developer.apple.com/jp/programs/ios/enterprise/</a></li>
<li><a href="http://www.apple.com/jp/support/iphone/enterprise/">http://www.apple.com/jp/support/iphone/enterprise/</a></li>
<li><a href="https://developer.apple.com/jp/devcenter/ios/library/documentation/FA_Wireless_Enterprise_App_Distribution.pdf">https://developer.apple.com/jp/devcenter/ios/library/documentation/FA_Wireless_Enterprise_App_Distribution.pdf</a></li>
<li><a href="http://support.apple.com/kb/DL1465?viewlocale=ja_JP">http://support.apple.com/kb/DL1465?viewlocale=ja_JP</a></li>
</ul>]]></content:encoded>
    </item>
  </channel>
</rss>
