見出し画像

CircleCI と fastlane で iOS アプリのビルドを自動化する - fastlane match を使わない方法 -

Build サービスチームで Solution Architect をしている t_maru です。

前回は fastlane match を使って証明書をプライベートリポジトリから自動的に取得してビルドに使用する方法を紹介しましたが、今回はリポジトリへの証明書アップロードが実施できない場合などに取れる手段として手動で証明書を取得してビルドする方法をご紹介します。

前回の記事で説明したように、fastlane match を使って自動で取得する方法の場合、証明書をプライベートリポジトリにアップロードする前に暗号化処理が入りますので、今回紹介する方法より安全で設定する項目も少ないので極力 fastlane match を使用することをおすすめします。

準備するものと手順

準備するものは前回の fastlane match を使用する方法と同じく、

  • 証明書 (.cer)、秘密鍵 (.p12)、Provisioning Profile (.mobileprovision)、秘密鍵のパスワード

となります。これらは Apple の Developer Console 等から入手してください。

設定手順としては、リポジトリに各種証明書、Provisioning Profile をアップロードしていた部分 (fastlane match の cli が自動で行っていた処理) を手動の処理でアップロード先も CircleCI の環境変数に置き換わる形となります。

  1. 証明書 (.cer)、秘密鍵 (.p12)、Provisioning Profile (.mobileprovision) を準備する

  2. CircleCI の環境変数に .cer, .p12, .mobileprovision のファイルを設定する

  3. Fastfile に証明書等を取得するための処理を記載する

  4. CircleCI の設定ファイルに設定を追加する

    1. 環境変数から証明書等を取得してファイルとして復元する処理を追加

    2. fastlane の処理を呼び出す設定を追加

以降で、それぞれの手順と設定方法について説明します。

CircleCI の環境変数に .cer, .p12, .mobileprovision のファイル等を設定する

Apple の Developer Console から取得した証明書等を CircleCI の環境変数に設定していきますが、それぞれを base64 エンコードして文字列に変換しておきます。

今回環境変数として設定するものは下記になります。

  • 証明書 (.cer)

  • 秘密鍵を含む証明書 (.p12)

  • Provisioning Profile

  • 秘密鍵のパスワード

  • Keychain name

  • Keychain password

最後の Keychain 関連の 2 項目については CI コンテナ内だけに影響するものですので Fastfile に直接書いても支障はありませんが、今回は環境変数にもたせています。

必要な設定を行うと CircleCI の環境変数は下記のようになります。
※ 環境変数名は Fastfile, CircleCI の設定ファイルと対応していれば良いため適宜変えていただいて構いません。

CircleCI 環境変数例

Fastfile に証明書等を取得するための処理を記載する

以下、 Fastfile のサンプルです。

lane :setup_appstore_provisioning_profiles do |options|
  create_keychain(
    name: ENV["KEYCHAIN_NAME"],
    password: ENV["KEYCHAIN_PASSWORD"],
    unlock: true,
    timeout: 3600)

  `curl -OL https://developer.apple.com/certificationauthority/AppleWWDRCA.cer`
  `echo #{ENV['DISTRIBUTION_CER']} | base64 -D > distribution.cer`
  `echo #{ENV['DISTRIBUTION_P12']} | base64 -D > distribution.p12`

  import_certificate(
    certificate_path: ".fastlane/distribution.cer",
    keychain_password: ENV["KEYCHAIN_PASSWORD"])
  import_certificate(
    certificate_path: ".fastlane/distribution.p12",
    certificate_password: ENV["CERTIFICATE_PASS"],
    keychain_password: ENV["KEYCHAIN_PASSWORD"])
  import_certificate(
    certificate_path: ".fastlane/AppleWWDRCA.cer",
    keychain_password: ENV["KEYCHAIN_PASSWORD"])

  Dir.glob("../SampleApp_AppStore_Provision.mobileprovision").each {|filename|
    puts filename
    FastlaneCore::ProvisioningProfile.install(filename)
  }
end

lane :build_prod_appstore do
  gym(
    workspace: "sample-app.xcworkspace",
    scheme: "sample-app",
    clean: true,
    output_directory: "Archives",
    output_name: "sample-app-prod-appstore-#{ENV['CIRCLE_BUILD_NUM']}.ipa",
    export_method: "app-store",
    export_options: {
      provisioningProfiles: {
        "com.example.sampleapp" => "SampleApp_AppStore_Provision"
      }
    }
  )
end

`setup_appstore_provisioning_profiles` という処理が証明書や Provisioning Profile の設定などを行う処理です。fastlane match を使う場合はこのあたりの処理が裏側で自動的に行われますので明示的に設定を書く必要はありませんでした。

`build_prod_appstore` という処理が iOS アプリをビルドするための処理です。私が動作確認をした環境では `export_options` の部分で Provisioning Profile と Bundle ID のマッピングを書かないとエラーとなりましたので、上記のような設定となっていますが iOS project の設定次第では `export_options` の項目を設定しなくてもビルドできる可能性はありますので、ご自身の環境に合った設定をお使いください。

今回は App Store 向けのビルドを行う例を記載しますが、App Store 以外にも Ad Hoc ビルドを行いたい場合は上記と同様の設定を Ad Hoc 用に追加してください。

CircleCI の設定ファイルに設定を追加する

.cer と .p12 については Fastfile 側で環境変数から取得してデコードする処理を記載しておりましたが、Provisioning Profile はファイル名指定で読み取っているので事前に CircleCI の処理として環境変数からファイルとして復元する形を取ります。(動作未確認ですが、.cer などと同様に Fastfile で Provisioning Profile をデコードしても問題ないかもしれません。)

以下、CircleCI の設定サンプルです。

version: 2.1

commands:
  prepare-build:
    steps:
      - run:
          name: Set Timezone
          command: |
            sudo systemsetup -settimezone "Asia/Tokyo"
      - restore_cache:
          keys:
            - v1-gems-{{ checksum "Gemfile.lock" }}
      - 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

jobs:
  build-appstore:
    macos:
      xcode: "14.3.0"
    resource_class: macos.x86.medium.gen2
    shell: /bin/bash --login -eo pipefail
    environment:
      BUNDLE_PATH: vendor/bundle
      LC_ALL: en_US.UTF-8
      LANG: en_US.UTF-8
    steps:
      - checkout
      - prepare-build
      - run:
          name: Decode AppStore Provisioning Profiles
          command: base64 -D -o ./SampleApp_AppStore_Provision.mobileprovision \<<< $APPSTORE_PP
      - run:
          name: Set up AppStore Provisioning Profiles
          command: bundle exec fastlane setup_appstore_provisioning_profiles --verbose
      - run:
          name: Run tests
          command: make test-all
      - store_artifacts:
          path: test_output
          destination: test_output
      - run:
          name: Build ipa
          command: bundle exec fastlane build_prod_appstore --verbose
      - store_artifacts:
          path: Archives
          destination: archives

workflows:
  build-appstore-wf:
    jobs:
      - build-appstore:
          filters:
            branches:
              only: main

まとめ

今回は fastlane match を使用せず CircleCI で iOS アプリのビルドを行う手段について紹介しました。

各種処理がブラックボックス化されていないため、どのような値をどう使って処理をするのか、という点に置いては今回紹介した方法がわかりやすいかもしれませんが、冒頭でもお伝えしたように基本的には fastlane match を使う方法が簡単で安全ですので前回紹介した方法を検討して頂けると良いかと思います。

みんなにも読んでほしいですか?

オススメした記事はフォロワーのタイムラインに表示されます!