受託開発でもfastlaneとCircleCIでiOSアプリを継続的デリバリー

  • 43
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

この記事はCircleCI Advent Calendar 2015 - Qiitaの14日目の記事です。

13日目はnofrmmさんですが、まだ記事が無いようです。

余談

本当は、CircleCIでのiOSビルドの有料化に伴って、個人アプリのビルドをTravisCIに移したという話をしようと思いましたが、今でもCircleCIでビルド出来てる(金は払ってない)っぽいので、一旦移行はやめました。
なので、社内で実際に構築した別のことをまとめました。

背景

iOSアプリを受託で作る場合、いろいろ面倒ですよね。主に証明書周りが。

弊社でよくあるパターンとしては、

  • iTunes connectなどはお客さんしか入れない。
  • 証明書はもらえる。
  • UDIDが増えたら、お客さんに連絡→provisioningを発行してもらう。
  • ipaファイルを納品する。

という感じです。
そのため、証明書やprovisioning profileを動的に取得することは不可能で、Gitのリポジトリに含めることが多いです。

また、弊社ではすでにCircleCIのiOS Planに課金をしており、CircleCI上でiOSのビルドをシェルスクリプトベースで実行していました。

今回、fastlaneというものが、どうやら便利らしいという噂を聞きつけ、試してみました。

実現すること

CircleCI上で動作させるbetaというlaneを作成し、下記の手順を実行します。

  • 証明書のインストール
  • ipaファイルの作成
  • CircleCIのArtifactsとしてipaを登録
  • DeployGateにサブミット
  • 証明書のアンインストール

導入手順

一通り設定したものが、noboru-i/kyouen-ios at bcbca4f21be08d56242e23a44f04986496515bd3こちらになります。
こちらでは、SwiftLintの実行なども入っていますが、それはまた別の機会に。

Gemfileの作成・設定

source 'https://rubygems.org'

gem 'fastlane'

上記内容でGemfileを作成し、bundle install --path vendor/bundleでインストールを実行します。

fastlaneの初期化

bundle exec fastlane initというコマンドがありますが、実体としては後述のAppfileFastfileなどを質問に答えていくことで作ってくれる、というものなので、今回は実行せずに進みます。

手動で、下記のようなディレクトリ構成を作成します。
プロジェクトのルートディレクトリにfastlaneディレクトリを作成し、fastlaneで必要なものを配置していきます。

project_root
├── Gemfile
├── circle.yml
└── fastlane
    ├── Appfile
    ├── Fastfile
    ├── actions
    │   └── swiftlint.rb
    └── cert
        ├── 02b06950-dd9a-4a2f-8f74-097ed2dd2db7.mobileprovision
        ├── apple.cer
        └── dist.p12

certディレクトリ内のファイルについては、下記のように配置します。

  • ファイル名がUUIDとなっているprovisioning profileを配置(インストール済みのものは、~/Library/MobileDevice/Provisioning Profilesに格納されているはず。)
  • Apple Worldwide Developer Relations Certification Authorityと呼ばれるものを、apple.cerというファイル名で配置
  • p12ファイルはパスワードを設定の上、配置(キーチェーンアクセスより、パスワード付きで書き出せる。p12ファイルが最悪流出しても、パスワードがわからなければ利用できない。)

fastlane/Appfileの設定

こちらは、Bundle Identifierなどのツール全般の設定を行うファイルです。

詳しい説明は、こちらを確認ください。

今回は、プロジェクトの設定ファイルにある値をそのまま利用するので、ファイルだけ作成しておき、空にしています。

fastlane/Fastfileの設定

こちらは、laneと呼ばれる実行する処理を記述するファイルです。

詳しい説明はこちら、各アクションのパラメータなどはこちらに記載がありますが、こちらにあるコードを直接見たほうが早いかもしれません。

今回は、前述のbetaというlaneを定義するために、下記のように設定します。

各アプリ毎に変更が必要な箇所は、先頭にまとめてあります。(You need to set your project values.の部分)
環境に合わせて書き換えて頂ければと思います。

fastlane_version '1.47.0'

default_platform :ios

# You need to set your project values.
workspace_name = 'Sample.xcworkspace'
scheme_name = 'Sample_adhoc'
deploygate_user_name = 'sample-user'
project_name = 'Sample'

# And you need to set environment variables.
# DEPLOY_GATE_KEY : DeployGate API key, using when upload to DeployGate.
# GITHUB_ACCESS_TOKEN : GitHub access token, using when comment to Pull Request.
# KEY_PASSWORD : p12 password, using when import *.p12 files.

platform :ios do
  before_all do
    add_keychain if is_ci?
  end

  after_all do
    remove_keychain if is_ci?
  end

  desc 'Submit a new Beta Build to DeployGate'
  lane :beta do
    gym(
      workspace: workspace_name,
      scheme: scheme_name,
      output_name: "#{project_name}.ipa"
    )

    Helper.log.info 'Save Artifacts'
    sh "cp #{lane_context[SharedValues::IPA_OUTPUT_PATH]} $CIRCLE_ARTIFACTS"

    message = "Commit: #{ENV['CIRCLE_SHA1']} / #{last_git_commit[:message]}, Build: #{ENV['CIRCLE_BUILD_NUM']}"
    deploygate(
      api_token: ENV['DEPLOY_GATE_KEY'],
      user: deploygate_user_name,
      ipa: lane_context[SharedValues::IPA_OUTPUT_PATH],
      message: message
    )
  end

  private_lane :add_keychain do
    create_keychain(
      name: 'ios-build.keychain',
      password: SecureRandom.uuid,
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      lock_when_sleeps: true
    )
    import_certificate keychain_name: 'ios-build.keychain', certificate_path: 'fastlane/cert/apple.cer'
    Dir.glob('cert/*.p12').each do |cert_file|
      import_certificate keychain_name: 'ios-build.keychain', certificate_path: 'fastlane/' + cert_file, certificate_password: ENV['KEY_PASSWORD']
    end

    Dir.glob('cert/*.mobileprovision').each do |provisioning_file|
      FastlaneCore::ProvisioningProfile.install(provisioning_file)
    end
  end

  private_lane :remove_keychain do
    delete_keychain(
      name: 'ios-build.keychain'
    )
    sh "rm -f \"#{FastlaneCore::ProvisioningProfile.profiles_path}*\""
  end
end

外部公開用のlaneとしてbetaがあり、それ以外はprivate_laneとしてメソッドを切り出しているイメージです。

CircleCIの設定

上記までで、bundle exec fastlane betaを実行することで当初の目的を果たせるようになりました。

あとは、これをCircleCI上で実行するように、circle.ymlを下記のようにします。

machine:
  timezone: Asia/Tokyo
  xcode:
    version: "7.1"
test:
  override:
    - echo 'no test'
deployment:
  master:
    branch: master
    commands:
      - bundle exec fastlane beta

ここでは、masterブランチの変更があった場合に、betaを実行するようにしています。

最後に、CircleCI上でenvironment variablesの設定を行います。設定が必要な項目はFastfileに記載してあります。(And you need to set environment variables.の部分)

あ、CircleCIのProject setting -> Experimental SettingsでBuild iOS projectをOnにすることもお忘れなく。(CircleCIへの課金が必要)

完成

この設定を行うことで、「XXさんが手が離せないのでアプリの配布が出来ません!」だったり、「XXさんが作ったipaとYYさんがつくったipaで動作が違うんだけど」とか、「ipaの作成作業が面倒なので、DeployGateでの配布は1日1回までとさせて頂きます」といったことから開放されます。

今回は、fastlaneの中でもgymdeploygateといったアクションぐらいしか使っていないですが、provisioning profileの更新や、iTunes connectとの連携など、様々なものが定義されています。

実際やってみて、パス周りだったり、パラメータの意味を調べるのだったりに時間がかかってしまいましたが、今まで慣れないシェルスクリプトで頑張っていたことを考えると、Rubyのコードで拡張していけるようになっただけでも、良かったと思います。

この投稿は CircleCI Advent Calendar 201514日目の記事です。