中年エンジニアの開発と生活の日々

中年エンジニアがソフトウェア開発や日々の生活で得た知見の備忘録

自動署名のXcodeプロジェクトをfastlaneでビルドする

自動署名とは

iOS アプリの開発においてXcode8 から導入された機能で、アプリケーションをビルド&アーカイブする際に選択した使用方法に合わせた署名をつけてくれます。 通常、署名の種類は開発用(Development)、検証用(Adhoc)、リリース用の3種類の署名があります。

自動署名機能を使用するかどうかはXcodeプロジェクトの設定ファイル(.xcodeproj)に格納されます。

自動署名で困ること

fastlane を使用して CI 環境でビルドする際にはビルドするブランチによって、使用する署名を指定する機会が多いです(例:develop ブランチなら Adhoc 用の署名をつけて、masterブランチなら Release用の署名をつけるとか )

この時にプロジェクトの設定が自動署名になっていると fastlane を使用して署名の種類を指定してビルドするとプロジェクトの設定とfastlaneの設定が食い違い、ビルドエラーになってしまいます。

解決策

自動署名機能を使わないという極端な解決策もあるのですが、せっかくの機能を使わないと勿体無いし開発者の開発効率を CI のために落とすのも本末転倒なので、以下のように運用しています。

  • fastlaneを使用してビルドを行う場合、最初にプロジェクトの設定をマニュアル署名に設定する
  • ビルド&アーカイブ処理を実行
  • ビルド&アーカイブが終わったらプロジェクトの設定を自動署名に戻す

xcodeproj ファイルは XML ファイルなので頑張れば自分で解釈して書き換えも頑張ればできるのですが、fastlaneにはプロジェクトの自動署名設定を書き換えるためのコマンドがあります。disable_automatic_code_signing と enable_automatic_code_signing です。引数にxcodeprojファイルのパスを指定して設定を変更するプロジェクトを指定します。

例えば、検証用(AdHoc)の ipa ファイルを作成する場合は以下のようになります。 xcargs の引数は各自の署名を識別できる正しい値を使用してください

  lane :beta do
    #真っ先にプロジェクトの自動署名をオフにする
    disable_automatic_code_signing(path: "XXXX.xcodeproj")
    sigh(adhoc: true)
    gym(
      clean: true, 
      workspace: 'XXXX.xcworkspace', 
      scheme: 'XXXXXX',
      configuration: 'Release',
      xcargs: "CODE_SIGN_IDENTITY='XXXX' PROVISIONING_PROFILE_SPECIFIER='XXXXXXXXXXXX'"
      ) 
    #ビルドが終わったら自動署名を元に戻す
    enable_automatic_code_signing(path: "MyApp.xcodeproj")
  end

おまけ

本来なら fastlane 側でもう少し柔軟に対応できないかとも思うのですが、今の所はこの方法で乗り切るしか無いようです。

限られた工数でテストケースの品質を上げる

最近、検証エンジニアも兼務すること多く、2年くらい前からテスト設計からテストの実行までやるようになりました。 その際に、見極めが難しいのが、テストケースをどこまで掘り下げて書くかと言うことでした。

基本

おおよそのテスト設計の教科書には以下のように書かれています。

  • 誰が読んでも同じ手順を再現できるように作る
  • 誰が実行しても、実行結果を判定できるように作る

最初は教科書通りにテストケースを書こうとしていたのですが、なんと言っても時間がかかるし、プラットフォームやブラウザやOSのバージョンによって手順が変わったりするので、現実的に上記を100%実行するのは不可能に近いし、工数をかけてまでやることではないという結論にいたっています。

テストケースを作成するに当たって重視すること

限られた工数を使って、有効なテストケースを作成しようとする場合、教科書の中から優先度の低いところをそぎ落としていきます。

  1. OS やブラウザなど、業界標準の知識に依存する手順については省略する
  2. 仕様書を見ればわかる手順は省略する

そして、テストの仕様に対するカバー率は100%を目指します。仕様に書かれているが検証されない機能をゼロにするためです。 そんなの当たり前じゃん、と思う方もいるかもしれませんが、仕様書も完璧に書かれているわけではないケースが多いため、希に抜けることがあったりします…

そんなわけで、結論としてはテストケースの項目を作成するときは気合いを入れる。テスト項目の中身を記載する場合、ある程度読み手を限定して手を抜けることろは手を抜くとメリハリをつけています。

テストケースを書く際に気をつけていること

自分がテストケースを書くときに気をつけていることは大体以下のようなことです。

  • 読み手の仕様に対する理解率を想定しておく
  • 手順を詳細にすることに力を注ぐより、カバー率を上げることに注力する
  • なるべくほかのドキュメントに書いてあることは書かない

ドキュメントというモノは誰かに向けて書いているわけで、読み手の理解率を想定しておくことがとても重要です。 国内の同じ拠点で開発している気心の知れたメンバー向けにオフショアベンダー向けに作るようなテスト手順書を作成するのはオーバースペックな上に、読み手にとっても読みにくいモノになるでしょう。

appium-desktop を Android アプリで使ってみる

先日 appium-desktop を紹介しましたが、Android 編のリクエストがありましたので、追加して見ました。

android で appium-desktop を使う利点

android については 古い desktop 版の appium Ver.15.x でも Inspector 機能を使うには支障がなかったため、appium-desktop 必須という訳ではありません。 しかしながら、設定パラメータの与え方などは appium-desktop の方がコマンドライン版に近いため、appium-desktopの方が断然オススメです。

アプリケーションの起動からサーバーの起動まで

アプリケーションの起動からサーバーの起動までは全く iOS と同じです。

Session を起動する時の注意

iOSアプリでは事前に Simulator を起動しておく必要はありませんが、Androidアプリを動作させる時はコマンドライン版と同じように、エミュレータをあらかじめ起動しておく必要があります。 エミュレータコマンドラインからでも Android Studioから起動しても同じように動作します。

Desired Capability

Desired Capability はコマンドライン版と同じオプションを指定しておく必要があります。 - platformVersion - 起動してあるエミュレータとバージョンを合わせておかないとセッション開始時にエラーになります - deviceName - avd の名称と合わせる必要はないようですが、無いとエラーになりました - app - apkのパス。Android アプリはリリース版もエミュレータで起動できるのが嬉しいです。 - appPackage - パッケージ名 - appActivity - メインクラスの名称

{
  "platformName": "Android",
  "platformVersion": "7.0",
  "deviceName": "Nexus 5",
  "app": "/Users/muneakiosawa/MyAndroidApp/app/build/outputs/apk/app-release.apk",
  "appPackage": "com.MyApp",
  "appActivity": ".MainClass"
}

Android アプリ向けにappiumでテストスクリプトを書かないといけない方は appium-desktop を是非とも使って見ましょう。

fastlane のプラグインを使ってみよう

iOS アプリの継続的インテグレーションで有名な fastlane ですが、今日はそのプラグイン機能について紹介します。

fastlane plug-in とは

fastlane はかなり多機能ですが、標準のコマンドでサポートされていない機能をプラグインを使用して機能を拡張できます。以下は公式のサイトです。

セットアップ方法

Fastfile の隣に Pluginfile を作成し、 そこに必要なプラグインを定義します。以下の例は Pluginfile の内容で fastlane-plugin-update_project_codesigning を追加しています

# Autogenerated by fastlane
#
# Ensure this file is checked in to source control!

gem 'fastlane-plugin-update_project_codesigning'

以下のコマンドを実行し、プラグインをセットアップします

fastlane install_plugins

このコマンドを実行すれば plugin の環境が構築され、Fastfileの中などから plugin を使用することができるようになります。

Plug in の種類

使用可能な plug in は以下のコマンドで一覧することができます。

fastlane search_plugins

fastlane の通常コマンドでできないことがある場合は、Plug in をチェックしてみてはいかがでしょう?

appium-desktop の紹介

はじめに

Xcode 8 になり、UI Automation が廃止された関係で、アプリ版の appium (Ver.15.x) が使えなくなっていました。自動テスト自体はコマンドライン版の appium Ver.16 を使用すれば良いのですが、自動テストのスクリプトを書く際に UI パーツの ID などを特定するのに appium inspector が使えなくて困るという人は多いはず。

本家のトラブルシューティングには appium server を Ver.16 で起動し、appium inspector 使えば回避できるよ〜と記載してありますが、先日 appium-desktop なるものがリリースされました。

github.com

セットアップ方法

dmg 形式で配布されているので、通常通りインストールするだけです。

Release v1.0.0 · appium/appium-desktop · GitHub

appium-desktop でできること

  • appium server の起動
  • appium inspector

appium server の起動はコマンドライン版とほとんど差はなく、Host の IP Address と Port を指定してサーバーを起動します。

そして本命の appium inspector ですが、従来の機能はほとんど踏襲するだけでなく、使いやすくなっています。

appium inspector の起動方法

appium.app を起動すると、起動画面が表示されます。

f:id:munacky:20170413185314p:plain

「Start Server v.x.x」ボタンを選択すると appium server が起動します。

f:id:munacky:20170413185533p:plain

右上にある「Start New Session」を選択すると、Session の起動画面が表示されます

f:id:munacky:20170413185720p:plain

Session の起動画面で Desired Capabilities を入力して、「Start Session」 ボタンを選択して Session を開始します。automationName:‘XCUITest’ を設定しないと、従来の UI Automation を起動しようとしてエラーになります。

以下の通り、appium inspector が起動しました。

f:id:munacky:20170413185956p:plain

最後に

セットアップから appium inspector 起動までの手順を紹介させていただきました。Xcode8 で appium inspector が使えなくて困っていた方は ぜひ、導入してみてください。便利ですよ。

Circle CI で iOS アプリと Android アプリのビルド番号を更新する

Circle CI のビルド番号を AndroidiOS のアプリのビルド番号に反映させる方法を紹介します。

Circle CI のビルド番号とは

Circle CI ではビルドごとに一意の値が決まるようにビルド番号を環境変数で用意しています。

CIRCLE_BUILD_NUM

この環境変数は Circle CI 側でナンバリングされます。

AndroidiOS の共通ルール

iOS アプリも Android アプリもアプリケーションのバージョンとビルドを特定するためのプロパティを持ちます。自分の今まで手がけているプロジェクトではバージョン番号は手動で更新、ビルド番号は自動で更新するようにシステムを構築することが多かったです。

Android のバージョン番号

Android のバージョン番号はアプリケーションの build.gradle の中にある defaultConfig に記載します。 以下の例では versionCode をビルド番号として、versionNameにバージョン番号(この例では 1.0.0)を記載しています。 versionCode には環境変数 CIRCLE_BUILD_NUM があれば、その番号をビルド番号にして、なければ 1 を使用します。 なので、Circle CI 上ではなく手元でビルドすると常に1になります。

android {
    compileSdkVersion 2X
    buildToolsVersion "2X.X.X"

    defaultConfig {
        applicationId "com.myApplication"
        minSdkVersion XX
        targetSdkVersion XX
        versionCode System.getenv("CIRCLE_BUILD_NUM") ? System.getenv("CIRCLE_BUILD_NUM").toInteger() : 1
        versionName "1.0.0"

    }

iOS のバージョン番号

iOS のバージョン番号は info.plist に格納されています。以下の例では CFBundleShortVersionString に Version 番号を、CFBundleVersionにビルド番号が格納されています。この例ではバージョン番号は 1.0.0、ビルド番号は1です。

 <key>CFBundleShortVersionString</key>
    <string>1.0.0</string>
    <key>CFBundleVersion</key>
    <string>1</string>

plist ファイルを直接更新することもできますが、XMLの値更新などをゼロからテンプレート処理をしようしてやるのは結構大変なので、自分は fastlane の機能を使用してビルド番号を書き換えています。 以下のように fastlane でビルド番号を更新する lane を作成してビルドの前に呼び出します。

Fastfileの抜粋です。fastlane のアクション increment_build_number に対して番号を設定することによって、ビルド番号を指定できます。

  desc 'Set build number to CIRCLE_BUILD_NUM'
  lane :set_build_num do
    buildNum = ENV['CIRCLE_BUILD_NUM']
    if buildNum == nil then
      buildNum = "1"
    end
    increment_build_number build_number: buildNum
  end

この lane を呼び出すことによってinfo.plistファイルが更新されます。

更新したビルド番号を Git に反映すべきか否か

現状は更新したビルド番号を Git には反映させていないが、ポリシーによっては反映させることも必要になります。ただ、その場合は android のbuild.gradleに環境変数を埋め込むのではなく、テンプレート処理などを行って、ファイルを書き換える処理が必要になります。

また、CIで Git を更新する場合はこの更新によってCIが起動しないように、Gitのコミット時にコメントの頭に [ci skip] をつけるなどしないと、永遠に CI が回り続けるので注意しましょう。

もっと、良いやり方があれば、指摘していただけると幸いです。

iOSとAndroidの同時開発で Circle CI を使用する

はじめに

iOSAndroidのクロス開発を行なっているのですが、Circle CIを使用してインテグレーションすることになったので、概要をまとめてみました。

Circle CI の仮想環境は Mac OS を使用する

Circle CI では仮想環境をLinuxMac OS から選ぶことができます。(Mac OSは有料プランでしか選べませんが…)

iOSAndroid のクロス開発を行う場合は Circle CI は Mac OS仮想マシンを使う必要があります。これは、Mac OS仮想マシンでなければ iOS 版をビルドできないためです。

Circle CI で iOS 版と Android 版をビルドするときの大まかな流れ

1つのリポジトリiOS版とAndroid版を開発しているケースを想定しています。

Circle CI は関連づけられているリポジトリの更新を感知してインテグレーションを開始します。そのため、基本的には1つのインテグレーションのサイクルでiOS版とAndroid版をそれぞれビルドして、まとめてデプロイすることになります。

  • iOS のビルド環境を構築する
  • Android のビルド環境を構築する
  • iOS 版をビルドする
  • Android 版をビルドする
  • デプロイ

iOS のビルド環境を構築する

Circle CI の Mac OS の仮想環境には既に必要な XCode 等はインストールされているので、使用する XCode のバージョンを指定する。circle.yml の machine に xcode のバージョンを記載します。

machine:
  xcode:
    version: 8.2

ビルドツールとしてfastlaneを使用するのであれば、dependenciesでセットアップしておきます。また、2017年4月時点では Cocoa Pods や npm モジュールは Podfile や package.json などの設定ファイルの存在をチェックして自動でやってくれますが、念のため明示的にセットアップするように circle.yml に記載するほうが好きです。

dependencies:
  override:
    - gem install fastlane
    - pod install
    - npm install

Android のビルド環境を構築する

Circle CI の用意してくれる Mac OS の仮想環境には Android 向けの開発環境は入っていないため、自力で構築する必要があります。 おおよその場合、android-sdk と関連ライブラリをセットアップします。

android-sdk はつい最近まで、brewでインストールできたましたが、brew caskに移動したので、cask からインストールします。また、関連ライブラリは従来は android コマンドで実行していたが、つい最近廃止になり、sdkmanagerコマンドでセットアップすることになっています。

dependencies:
  pre:
    - brew install Caskroom/cask/android-sdk
    - sdkmanager "platforms;android-23" "build-tools;23.0.1"

sdkmanagerについては今度細かく書きたい。

自動テストを行う

自動テスト用のビルドを行い、自動テストを実施します。iOS版とAndroid版を両方ビルドして検証するべきですが、時間がかかるのが悩ましいところです。 iOS版とAndroid版のビルドについてはざっくりと紹介します。

iOS 版をビルドする

自分はiOS 版のビルドには fastlane を使っていますが、 XCodeコマンドラインツールを直接実行する方法もあります。fastlane はビルドから署名までいろいろと便利機能が充実しているので便利です。 更新されたブランチを特定してfastlaneのオプションを切り替えることによって署名を検証向けと製品向けを切り替えることができます。

Android 版をビルドする

Android 版のビルドは標準の Gradle を使用してビルドします。

デプロイ

変更されたブランチを特定してデプロイ先を変更することができます。Masterブランチの場合は App Store、Develop ブランチの場合は検証用のWebサーバーなどに配置します。 DeployGate などのツールを使用すれば、お金はかかりますが労力は軽減されそうです。

最後に

Circle CI を使用して iOS 版と Android 版を一緒にビルドする方法を中心にまとめてみました。Mac OS 上での コマンドラインベースのAndroidの環境構築を行なっている人が少ないので、参考になれば幸いです。