【iOS】match使わずにCircleCI2.0+FastlaneでFabric配布

## 概要
CircleCI2.0+Fastlaneでは証明書は`match`を使うのがデフォルトになっているらしい。
`match`だとプライベートなリポジトリに証明書など一式アップしないといけないので、
今回は`match`は使わずにデプロイできるようにする。

### :wrench: セットアップ

- CircleCIでプロジェクトの設定をする

    「ADD PROJECTS」>「対象のプロジェクト」>「Set Up Project」を選択 
![image.png (126.1 kB)](https://img.esa.io/uploads/production/attachments/5529/2018/04/02/18452/1cb8217a-af02-4eff-a233-d35f3cd92913.png)
    Operating Systemは`macOS`でLaunguageは`Swift`を選択し、`Start building`で設定します。

- ローカルでFastlane初期化

    ```ruby:Gemfile
    source "https://rubygems.org"
    
    gem 'fastlane'
    gem 'cocoapods'
    ```

    ```sh
    $ bundle exec fastlane init
    ```

## :pencil: 設定ファイル
最終的な設定ファイルを載せて解説しています。

```yml:circleci/config.yml
references:
  defaults: &defaults
    macos:
      xcode: "X.X.X"
    working_directory: ~/project
    shell: /bin/bash --login -eo pipefail
    environment:
      TZ: "/usr/share/zoneinfo/Asia/Tokyo"
      LANG: en_US.UTF-8
      LC_ALL: en_US.UTF-8

version: 2
jobs:
  build:
    <<: *defaults
    branches:
      only:
        - develop
    environment:
      FASTLANE_LANE: crashlytics_stg
    steps:
      - checkout:
          path: ~/project
      - run:
          name: Set Ruby Version
          command:  echo "ruby-2.4" > ~/.ruby-version
      - run:
          name: Decode certificates
          command: base64 -D -o XXXXXX.p12 <<< $CERTIFICATES
      - run:
          name: Make Provisioning Profiles directory
          command: mkdir -pv ~/Library/MobileDevice/Provisioning\ Profiles/
      - run:
          name: Decode Provisioning Profiles
          command: base64 -D -o ~/Library/MobileDevice/Provisioning\ Profiles/xxxxxx.mobileprovision <<< $PROVISIONING_PROFILES


      - restore_cache:
          key: v1-gems-{{ checksum "Gemfile.lock" }}
          path: vendor/bundle
      - run:
          name: Bundle install
          command: bundle check || bundle install
          environment:
            BUNDLE_JOBS: 4
            BUNDLE_RETRY: 3
      - save_cache:
          key: v1-gems-{{ checksum "Gemfile.lock" }}
          paths: vendor/bundle
      - restore_cache:
          keys:
          - v1-pods-{{ checksum "Podfile.lock" }}
          path: Pods
      - run:
          name: Running pod install
          command: bundle exec fastlane ios setup
          environment:
            BUNDLE_JOBS: 4
            BUNDLE_RETRY: 3
      - save_cache:
          key: v1-pods-{{ checksum "Podfile.lock" }}
          paths: Pods
      - run:
          name: Fastlane
          command: bundle exec fastlane ios $FASTLANE_LANE
```

事前にcircleci側に以下環境変数を設定しておきます。

```
$CERTIFICATES
$PROVISIONING_PROFILES
```

設定値としては証明書とProvisioning Profileを一旦base64にエンコードした物を設定します。

```sh
$ base64 -i xxxxx.p12 | pbcopy # CERTIFICATES に設定
$ base64 -i xxxxx. mobileprovision | pbcopy # PROVISIONING_PROFILES に設定
```

```ruby:Fastfile
default_platform(:ios)

platform :ios do

  before_all do
    ENV["SLACK_URL"] = 'xxxxxxxxx'
    ENV["CRASHLYTICS_API_TOKEN"] = xxxxxxxx'
    ENV["CRASHLYTICS_BUILD_SECRET"] = 'xxxxxxxxxx'
    ENV["FL_OUTPUT_DIR"] = 'temp'
    
    clear_derived_data(derived_data_path: "./DerivedData")
    sh "rm -rf ../build"
  end
  
  desc "Runs setup"
  lane :setup do
    unless File.exist?("../Pods/Manifest.lock") && FileUtils.cmp("../Podfile.lock", "../Pods/Manifest.lock") then
      cocoapods(verbose: true)
    end
  end
  
  desc "Runs build to stging"
  lane :build_stg do

    if is_ci?
      setup_circle_ci
      import_certificate(
        keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
        keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
        certificate_path: 'xxxxxxx.p12',
        certificate_password: ''
      )
    end

    update_app_identifier(
      xcodeproj: "Xxxxx.xcodeproj",
      plist_path: "Xxxxx/Info.plist",
      app_identifier: 'com.xxxx.xxxx'
    )

    gym(
      scheme: 'Xxxx',
      configuration: 'Debug',
      derived_data_path: "./DerivedData",
      export_method: "ad-hoc",
      clean: true,
      verbose: true,
      output_directory: "build",
      output_name: 'Xxxxx,
      xcargs: "OTHER_SWIFT_FLAGS='$(inherited) -DSTGING' PROVISIONING_PROFILE_SPECIFIER='com.xxxx.xxxx AdHoc' CODE_SIGN_IDENTITY='iPhone Distribution'",
      export_xcargs: "-allowProvisioningUpdates",
      export_options: {
        provisioningProfiles: {
          'com.xxxx.xxxx' => 'com.xxxx.xxxx AdHoc'
        }
      }
    )
  end

  lane :crashlytics_stg do
    build_stg
    crashlytics(
      crashlytics_path: './Pods/Crashlytics/iOS/Crashlytics.framework',
      api_token: ENV['CRASHLYTICS_API_TOKEN'],
      build_secret: ENV['CRASHLYTICS_BUILD_SECRET'],
      ipa_path: './build/Xxxxx.ipa',
      notifications: true,
      notes: "Fastlaneによる自動デプロイ",
      groups: "xxxxx"
    )
    if is_ci?
      slack(
        message: "Successfully deployed new App Update."
      )
    end
  end

  error do |lane, exception|
    if is_ci?
      slack(
        message: exception.message,
        success: false
      )
    else
      puts exception.message
    end
  end
end
```

ポイントとしては、`build_stg`の先頭で

```ruby
    setup_circle_ci
    import_certificate(
      keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
      keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
      certificate_path: 'xxxxxxx.p12',
      certificate_password: ''
    )
```
と書く事で一時的なKeychainをCircleCI上に作成し、デコードした
証明書とプロビジョニングプロファイルを設定しています。

## :bomb: バッドノウハウ

- **Error computing cache key**

```
Error computing cache key: template: cacheKey:1:19: executing "cacheKey"
```
チェックサム化するファイルのパスが間違っていた為...
https://qiita.com/sawadashota/items/d1382b1e1b9a5595ab66

- **The key you are authenticating with has been marked as read only**

    ```
    ERROR: The key you are authenticating with has been marked as read only.
    ```
    今回の実装とは関係ないですが、Githubにpushしようとしたら↑のエラーに遭遇
    
    CircleCIのProjectの設定で`Checkout SSH Keys`から設定する
    `Authrization Github`というのがあったので追加して見たらうまく行きました:+1:

- **OSをMacでCI実行した時のrubyバージョンエラー**

    ```
    Jwt-2.1.0 requires ruby version >= 2.1, which is incompatible with the current version, ruby 2.0.0p648
    ```

    参考URL
    https://discuss.circleci.com/t/jwt-2-1-0-requires-ruby-version-2-1-which-is-incompatible-with-the-current-version-ruby-2-0-0p648/18318
    https://circleci.com/docs/2.0/testing-ios/#custom-ruby-versions
    
    ```
          - run:
              name: Set Ruby Version
              command:  echo "ruby-2.4" > ~/.ruby-version
    ```
    上を追加

- **CocoaPodsで設定したライブラリが provisioning profilesでエラーになる**

```
[19:03:54]: ▸ === BUILD TARGET FAPanels OF PROJECT Pods WITH CONFIGURATION Release ===
[19:03:54]: ▸ Check dependencies
[19:03:54]: ▸ Code Signing Error: FAPanels does not support provisioning profiles. FAPanels does not support provisioning profiles, but provisioning profile NO_SIGNING/ has been manually specified. Set the provisioning profile value to "Automatic" in the build settings editor.
[19:03:54]: 
[19:03:54]: ⬆️  Check out the few lines of raw `xcodebuild` output above for potential hints on how to solve this error
[19:03:54]: 📋  For the complete and more detailed error log, check the full log at:
[19:03:54]: 📋  /Users/distiller/Library/Logs/gym/XXXX.log
[19:03:54]: 
[19:03:54]: Looks like fastlane ran into a build/archive error with your project
[19:03:54]: It's hard to tell what's causing the error, so we wrote some guides on how
[19:03:54]: to troubleshoot build and signing issues: https://docs.fastlane.tools/codesigning/getting-started/
[19:03:54]: Before submitting an issue on GitHub, please follow the guide above and make
[19:03:54]: sure your project is set up correctly.
[19:03:54]: fastlane uses `xcodebuild` commands to generate your binary, you can see the
[19:03:54]: the full commands printed out in yellow in the above log.
[19:03:54]: Make sure to inspect the output above, as usually you'll find more error information there
```

`PROVISIONING_PROFILE_SPECIFIER `を設定してやる事でうまく行きました
[参考URL](http://techblog.lclco.com/entry/2017/10/31/121516)

## :link: 関連リンク

- [CircleCI 2.0 and the iOS Code Signing](https://medium.com/@m4rr/circleci-2-0-and-the-ios-code-signing-df434d0086e2)
- [CircleCI 2.0 に移行しました](http://tech.smarthr.jp/entry/2017/07/12/073000)
- [CircleCI Public](https://github.com/CircleCI-Public)
- [CircleCI Docker Hub](https://hub.docker.com/u/circleci/)
- [CircleCI2.0でアプリケーションをビルドする](https://qiita.com/sawadashota/items/d1382b1e1b9a5595ab66)
- [Circle CI 2.0 移行時のキャッシュ設定メモ](https://qiita.com/gaaamii/items/b2e8178d7d6e1423374f)
- [iOS アプリの CI 環境を CircleCI 2.0 に移行したら結構便利](https://tech.recruit-mp.co.jp/mobile/circleci-2-for-ios/)
- [fastlane matchを使ったiOS証明書、Provisioning Profileの導入管理](http://tech.hyakuren.org/entry/ios-provisioning-use-fastlane-match)