Help us understand the problem. What is going on with this article?

GitLab CIでiOSプロジェクトのCI環境を構築する

More than 3 years have passed since last update.

すでに稼働しているGitLabへGitLab-CI Runnerを登録。
iOSプロジェクトの
・ユニットテスト
・Beta配信
・iTunesConnectへアップロード
をCI環境上で実現しました。

image.jpeg

実行環境

  • GitLab CE 9.3.9
  • GitLab-CI 9.3.9
  • Xcode 8.3.3
  • CocoaPods 1.2.0

この投稿で語らないこと

  • GitLabの環境構築手順
  • UnitTestの書き方

Runnerマシンの構築

利用したMacは10.12.6

CI Runnerのインストール

GitLab本家のドキュメント通りに実行しました。
https://docs.gitlab.com/runner/install/osx.html

RunnerをGitLabに登録

こちらも本家GitLabのドキュメント通りに実行し問題ありませんでした。
https://docs.gitlab.com/runner/register/index.html#macos

  • 補足

GitLab-CI Tokenの調べ方

  • GitLabのコーンソール上でRunnerを登録したいプロジェクトを選択
  • [Settings] -> [Pipelines] を選択
  • 下の図の位置にTokenが表示されます。 名称未設定.002.jpeg

Runner executorは何を選ぶ?

Please enter the executor: ssh, docker+machine, docker-ssh+machine, kubernetes, docker, parallels, virtualbox, docker-ssh, shell:
docker

Runner executorはShellを選択しました。

Xcodeのインストール

RunnerマシンにもXcodeをインストールする必要があります。

証明書の取り込み

Runnerマシンにコードサイン証明書を取り込みます。
Apple Developerサイトから新たにダウンロードしてもビルドできません。
開発マシンからエスクポートする必要があります。
詳細はこちら
http://qiita.com/moc-yuto/items/bcecf6a1050115093c5a

Cocoapodsのインストール

プロジェクトの依存関係を解決するためにCocoapodsを導入している場合は、RunnerマシンにもCocoapodsをインストールする必要があります。
今回はRunnerを複数プロジェクトで共有する予定があったため、Bundlerを用いてインストールしました

$ sudo gem install bundler

Xcodeプロジェクトの設定

本家GitLabのドキュメントが非常に役立ちました。
https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects

image.png

重要なポイントはスキームにShareのチェックを入れること

Bundlerの設定とCocoaPodsの入れ直し

既存のXocdeプロジェクトにもBundlerを導入し、CocoaPodsを入れ直しました。

Gemfileという名前のファイルをxcodeprojと同じ階層に作成します。
内容は以下、

# frozen_string_literal: true
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'cocoapods', '1.2.0'

Bundler経由でCocoapodsをインストールします。

$ bundle install --path vendor/bundle

以下の2つのファイルをバージョン管理に含めます。
- Gemfile
- Gemfile.lock

ジョブの記述

.gitlab-ci.ymlという名前のファイルにRunnerに実行させたい処理(job)を記述します。
このファイルはxcodeprojと同じ階層に配置する必要があります。

本家のドキュメントを参考に、以下のようなjobを記述しました。

stages:
  - build

before_script:
  - bundle install
  - bundle exec pod install

build_project:
  stage: build
  script:
    - xcodebuild clean -project ProjectName.xcodeproj -scheme SchemeName | xcpretty
    - xcodebuild test -project ProjectName.xcodeproj -scheme SchemeName -destination 'platform=iOS Simulator,name=iPhone 7,OS=latest' | xcpretty -s
  tags:
    - ci-for-ios

tags に記載するタグはRunner登録時に入力したタグと同一のものにします。
また、GitLabのコンソールからタグを編集することも可能です。
※ [Settings] -> [Pipelines] を選択

ここで、xcprettyというモジュールが必要になりましたので
新たにxcprettyをBundler経由でインストールします。

# frozen_string_literal: true
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'cocoapods', '1.2.0'
gem 'xcpretty', '0.2.8'

Bundler経由でCocoapodsをインストールします。

$ bundle install

ジョブの実行

.gitlab-ci.ymlファイルがあり、
かつ.gitlab-ci.yml内のtagsがRunnerに設定したタグに含まれていたら
ジョブが実行されます。
.gitlab-ci.ymlファイルを追加したコミットをpushすると、おそらくジョブが実行されるでしょう。

xcprettyの文字エンコード

私の環境では、上記の.gitlab-ci.ymlではすぐにジョブがエラーになってしまいました。理由はxcprettyの文字エンコードを指定していなかったことが原因のようでした。
深追いせず、文字コードを設定したところ動作しました。

stages:
  - build

before_script:
  - export LANG=en_US.UTF-8
  - export LANGUAGE=en_US.UTF-8
  - export LC_ALL=en_US.UTF-8
  - bundle install
  - bundle exec pod install

build_project:
  stage: build
  script:
    - xcodebuild clean -project ProjectName.xcodeproj -scheme SchemeName | xcpretty
    - xcodebuild test -project ProjectName.xcodeproj -scheme SchemeName -destination 'platform=iOS Simulator,name=iPhone 7,OS=latest' | xcpretty -s
  tags:
    - ci-for-ios

シミュレーターの権限問題

Runnerマシン上でユニットテストを実行する際、iOSシミュレータを起動します。
シミュレーターの起動時はMacの管理ユーザーで認証を求められます。
対策法は現在調査中...

fastlaneでの実行

続いて、ユニットテストの実行に加えてBeta配信とiTunesStoreへのアップロードを目指します。
これらの操作を.gitlab-ci.ymlからfastlaneを呼び出して実現することにします。
まず、Gemfileを編集して

# frozen_string_literal: true
source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'cocoapods', '1.2.0'
gem 'fastlane', '2.50.1'

Bundle経由でfastlaneをインストールします。

$ bundle install

続いて、fastlaneの初期設定を行います。
以下のコマンドを実行します。

$ bundle exec fastlane init

詳細はfastlaneの設定について親切な記事を参照してください。
http://qiita.com/econa77/items/d81a392d6b195cfb7316

laneは次のようにしました。

  • ユニットテストの実行
  desc "Runs all the tests"
  lane :test do
    scan(
      scheme: "{アプリのスキーム}",
      clean: true,
      devices: ["iPhone 7"]
    )
  end
  • CrashlyticsによるBeta配信
  lane :fabric do
    sigh(
      adhoc: true,
      force: true,
      app_identifier: "{アプリのバンドルID}"
    )
    gym(
      scheme: "{Xcodeプロジェクトのスキーム}", 
      workspace: "{xcworkspaceファイル名}",
      configuration: "Release",
      clean: true,
      output_name: "AdHoc.ipa",
      export_method: "ad-hoc"
    )
    crashlytics(
      crashlytics_path: "./Pods/Crashlytics/",
      api_token: "{fabricのapi token}",
      build_secret: "{fabricのbuild secret}",
      ipa_path: "./AdHoc.ipa"
    )
  end
  • iTunesConnectへアップロード
    gym(
      scheme: "{Xcodeプロジェクトのスキーム}", 
      workspace: "{xcworkspaceファイル名}",
      configuration: "Release",
      clean: true,
      output_name: "AppStore.ipa",
      export_method: "app-store",
      include_symbols: true,
      include_bitcode: true
    )
    deliver(force: true)

上記のlaneにてRelease用のipaを作成する場合、コード署名が発生します。
コード署名は最初の一回のみ、管理者権限を求められます。
GUIで「常に許可」を選択することで、処理がストップせずに進みます。

img.png

deliverでエラー

さらに、iTunesConnectへipaをアップロードするdeliverでエラーが発生しました。
Mac組み込みのOpensslのバージョンが古いために起こるエラーです。

[14:23:36]: fastlane finished with errors
[14:23:36]: -----------------------------------------------------------------------
[14:23:36]: Connection reset by peer - SSL_connect
[14:23:36]:
[14:23:36]: SSL errors can be caused by various components on your local machine.
[14:23:36]: Apple has recently changed their servers to require TLS 1.2, which may
[14:23:36]: not be available to your system installed Ruby (2.0.0)
[14:23:36]:
[14:23:36]: The best solution is to use the self-contained fastlane version.
[14:23:36]: Which ships with a bundled OpenSSL,ruby and all gems - so you don't depend on system libraries
[14:23:36]: - Use Homebrew
[14:23:36]: - update brew with brew update
[14:23:36]: - install fastlane using:
[14:23:36]: - brew cask install fastlane
[14:23:36]: - Use One-Click-Installer:
[14:23:36]: - download fastlane at https://download.fastlane.tools
[14:23:36]: - extract the archive and double click the install
[14:23:36]: -----------------------------------------------------------
[14:23:36]: for more details on ways to install fastlane please refer the documentation:
[14:23:36]: -----------------------------------------------------------
[14:23:36]: 🚀 https://docs.fastlane.tools 🚀

[14:23:36]: -----------------------------------------------------------
[14:23:36]:
[14:23:36]: You can also install a new version of Ruby
[14:23:36]:
[14:23:36]: - Make sure OpenSSL is installed with Homebrew: brew update && brew upgrade openssl
[14:23:36]: - If you use system Ruby:
[14:23:36]: - Run brew update && brew install ruby
[14:23:36]: - If you use rbenv with ruby-build:
[14:23:36]: - Run brew update && brew upgrade ruby-build && rbenv install 2.3.1
[14:23:36]: - Run rbenv global 2.3.1 to make it the new global default Ruby version
[14:23:36]: - If you use rvm:
[14:23:36]: - First run rvm osx-ssl-certs update all
[14:23:36]: - Then run rvm reinstall ruby-2.3.1 --with-openssl-dir=/usr/local
[14:23:36]:
[14:23:36]: If that doesn't fix your issue, please google for the following error message:
[14:23:36]: 'Connection reset by peer - SSL_connect'
[14:23:36]: -----------------------------------------------------------------------

今回はHome brew経由でOpensslとRubyのバージョンをアップデートすることで対処しました。

Home brewのインストール

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

opensslとrubyの更新

$ brew install ruby
$ brew install openssl

そのままではopensslの参照先が古いバージョンのままです。
.bash_profileを編集してパスを通します。

# bash_profile
export PATH=/usr/local/Cellar/openssl/1.0.2l/bin:$PATH

これで、deliverで発生していたエラーがなくなります。

さらにdeliverでエラー

さらにdeliverでエラーが発生しました。

Unexpected Error

deliverはデフォルトでスクリーンショットやメタデータのアップロードをする仕様になっているようです。
今回はipaのアップロードのみを予定していたため、画像ファイル等を用意していませんでした。

    gym(
      scheme: "{Xcodeプロジェクトのスキーム}", 
      workspace: "{xcworkspaceファイル名}",
      configuration: "Release",
      clean: true,
      output_name: "AppStore.ipa",
      export_method: "app-store",
      include_symbols: true,
      include_bitcode: true
    )
    deliver(
      app_identifier: "{アプリのバンドルID}",
      skip_metadata: true,
      skip_screenshots: true,
      force: true
    )

以上のようにすることで、アップロードに成功しました。

yumemi
みんなが知ってるあのサービス、実はゆめみが作ってます。スマホアプリ/Webサービスの企画・UX/UI設計、開発運用。Swift, Kotlin, PHP, Vue.js, React.js, Node.js, AWS等エンジニア・クリエイターの会社です。Twitterで情報配信中https://twitter.com/yumemiinc
http://www.yumemi.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away