CodemagicでiOSのネイティブアプリをCIに載せる

Flutter専門のCI/CDサービス Codemagicでネイティブアプリのビルドやテストが行えるそうなので試してみました。
2020.10.12

Flutter専門のCI/CDサービスとして開始されたCodemagicはアップデートによりネイティブアプリでも同等に扱えるようになりました。この記事ではcodemagic.yamlファイルを使ったネイティブアプリのCI/CD設定方法を説明します。

GUIで直接AndroidやiOSのネイティブアプリをビルドすることはできませんが、ネイティブアプリをビルドするためにcodemagic.yamlを使って簡単に設定することができます。

導入

  1. Codemagic - CI/CD for Flutter and mobile app projectsにログインします。
  2. Applications overviewで任意のプロジェクトを選択(ここではネイティブのiOSアプリのプロジェクトを選択してください)します。そしてSet up buildを選択してください。
  3. Select a starter workflowページに遷移するので、Continuous Integration workflowsのiOS Appを選択します。

  1. 初めてのネイティブのアプリでのCodemagicの利用になる場合には手短なガイドモーダルが表示されます。

デフォルトのYAMLファイルについて

ガイド用のモーダルは合計で4ステップあり、それに従い設定していくことでbuildのみを行える環境の構築が可能です。Download or copy the configuration file exampleではプロジェクトのルートディレクトリに設置する設定ファイルの雛形が提供されます。yamlファイルのコメントにある通り、iOSネイティブアプリ用のyamlファイルについての詳しい情報が知りたい場合はBuilding a native iOS app - Codemagic Docsを参照します。

また、テンプレートの{}部分を適した値に更新するように指示されています。ダウンロードが良いと思いますが、今回は説明も兼ねているのでクリップボードにコピーして新規ファイル作成します。以下のようなコードになります。

# Check out https://docs.codemagic.io/yaml/building-a-native-ios-app/ for more information
# Please review and update values in curly braces

workflows:
    ios-app:
        name: iOS App
        environment:
            vars:
                XCODE_WORKSPACE: "{{ ADD WORKSPACE NAME HERE }}"
                XCODE_SCHEME: "{{ ADD SCHEME NAME HERE }}"
            xcode: latest
            cocoapods: default
        scripts:
            - xcodebuild build -workspace "$XCODE_WORKSPACE.xcworkspace" -scheme "$XCODE_SCHEME" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
        artifacts:
            - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.app
            - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.dSYM

デフォルトのワークフローIDとワークフロー名が指定されていることがわかりますが、好きなように変更することができます。ワークフローはXcodeとCocoaPodsの両方の最新バージョンを使用しています。

プロジェクトに合った正しいフォルダ構造とツールのバージョンを選択してください。ファイルには、依存関係、変数、その他の関連データを必ず含めてください。

プロジェクトのニーズに合わせて codemagic.yaml を調整し終わったら、プロジェクトリポジトリのお好みのブランチにプッシュしてください。

値を更新したyamlファイルは以下になります。

# Check out https://docs.codemagic.io/yaml/building-a-native-ios-app/ for more information
# Please review and update values in curly braces

workflows:
  ios-app:
    name: iOS App
    environment:
      vars:
        XCODE_WORKSPACE: "CodeMagicTestSample"
        XCODE_SCHEME: "FileterHB"
      xcode: latest
      cocoapods: default
    scripts:
      - xcodebuild build -workspace "$XCODE_WORKSPACE.xcworkspace" -scheme "$XCODE_SCHEME" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
    artifacts:
      - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.app
      - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.dSYM

変更をブランチにプッシュして問題ないことを確認した後、モーダルの最後のステップ、Check for uploaded configuration fileで、Check for configuration fileをクリックします。

codemagic.yamlファイルが任意のブランチに存在することが確認できたらダイアログが表示されるのでStart your first buildをクリックします。

クリックするとbuild configurationの設定画面に遷移します。

ビルドが始まったら結果が反映されるまで待機します。

このプロジェクトでは数分後に成功したことを確認しました。

ここまでで.appファイルを生成するworkflowを通したネイティブアプリのビルドは終了です。

コードサイニング

.ipaファイルを生成するにはcode-signingが必要になります。

  • Certificate
  • Provisioning profile

個人利用のためにipaファイルをpublishする場合、iOS Development Ceritificateを使います。また、App Store ConnectにpublishするためにはiOS Distribution Certificateが必要になります。リリース用のiOS Distribution Certificateを作成したい場合にはこちらを参照してください。作成済みのProvisioning profileとCeritificateのダウンロードの取得方法を紹介します。

Xcodeを開いて設定画面に遷移した後、Accountsタブで任意のアカウントを選択して右カラムのDownload Manual Profileを選択します。ここでダウンロードしたものは~/Library/MobileDevice/Provisioning Profiles/に配置してあります。

Ceritificateの取得方法も説明します。Download Manual Profilesの右側のManage Certificates...をクリックします。

Certificateの管理画面ダイアログが表示されるのでエクスポートしたいCertficateにマウスオーバーしてコンテキストメニューを開きます。するとExport Certificateをクリックします。

.p12ファイルの名前やパスワードを設定して任意のディレクトリに保存することができます。

以上で設定済みの証明書とprovisioning profileの取得の説明は終わりです。

codemagic.yamlファイルへの反映

テンプレートとして予め提供されているcodemagic.yamlファイルは.appファイルを生成するwolkflowを通してビルドを行うだけなので、.ipaファイルを生成したい場合はこのファイルにいくつかの環境変数をcode-signing用に追加しなければいけません。

証明書とプロビジョニングプロファイルを暗号化された形で、codemagic.yamlファイルの環境変数セクションにキーと値のペアとして追加する必要があります。

  • CM_CERTIFICATE (encrypted version of the certificate)
  • CM_CERTIFICATE_PASSWORD (encrypted version of the certificate password)
  • CM_PROVISIONING_PROFILE (encrypted version of the provisioning profile)

プロジェクトの設定画面に移動します。遷移先のEncrypt environment variablesというリンクをクリックします。

リンクをクリックするとEncrypt environment variables for YAML configurationというモーダルが表示されます。Choose a file or drag it hereにファイルをドラッグすると暗号化された文字列が表示されるのでコピーしておきましょう。codemagic.yamlファイルに環境変数を設定する時に使用します。もう一つ、証明書のパスワードについても環境変数に設定しておきたいので、ダイアログのPaste the value of the variable hereというプレースホルダーが書いてあるテキストエリアにパスワードを押してEncryptボタンをクリックします。ファイルをドラッグした時と同じように暗号化された文字列が出力されるのでコピーしておきましょう。

codemagic.yamlに証明書とprovisioning profile、および.p12ファイルに設定したパスワードを環境変数として設定します。YAMLファイルは以下のようになりました。

# Check out https://docs.codemagic.io/yaml/building-a-native-ios-app/ for more information
# Please review and update values in curly braces

workflows:
  ios-app:
    name: iOS App
    environment:
      vars:
        XCODE_WORKSPACE: "CodeMagicTestSample"
        XCODE_SCHEME: "CodeMagicTestSample"
      xcode: latest
      cocoapods: default
      CM_CERTIFICATE: Encrypted(...) # 証明書ファイルの暗号化文字列
      CM_PROVISIONING_PROFILE: Encrypted(...) # provisioning profileの暗号化文字列
      CM_CERTIFICATE_PASSWORD: Encrypted(...) # .p12ファイルに指定したパスワードを暗号化した文字列
# ... 以下略

scriptの更新

ipaファイルの生成やテストの実行を行うために環境変数の設定以外に、scriptの変更が必要になります。

まずは keychain を初期化します。

- keychain initialize

Provisioning profileをデコードし、code-signingのプロセス間でアクセスできるようにフォルダに配置します。

PROFILES_HOME="$HOME/Library/MobileDevice/Provisioning Profiles"
mkdir -p "$PROFILES_HOME"
PROFILE_PATH="$(mktemp "$PROFILES_HOME"/$(uuidgen).mobileprovision)"
echo ${CM_PROVISIONING_PROFILE} | base64 --decode > $PROFILE_PATH
echo "Saved provisioning profile $PROFILE_PATH"

Certificateをデコードしてキーチェーンに追加します。

echo $CM_CERTIFICATE | base64 --decode > /tmp/certificate.p12
keychain add-certificates --certificate /tmp/certificate.p12 --certificate-password $CM_CERTIFICATE_PASSWORD

テスト実行用のコマンドも追加します。

      - |
        # run tests
        xcodebuild \
        -workspace "CodeMagicTestSample.xcodeproj" \
        -scheme "CodeMagicTestSample" \
        -sdk iphonesimulator \
        -destination 'platform=iOS Simulator,name=iPhone 11 Pro Max,OS=13.4' \
        clean build test CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO

ここで指定している-destinationの値と、project.pbxprojeのIPHONEOS_DEPLOYMENT_TARGET`とのバージョンが一致していることを確認してください。

.ipaファイル生成コマンドもこのタイミングで追加します。

      - |
        # build ipa
        xcode-project use-profiles
        xcode-project build-ipa --workspace "CodeMagicTestSample.xcodeproj" --scheme "CodeMagicTestSample"

私の環境に合わせて設定後のcodemagic.yamlファイルを掲載しておきます。

# Check out https://docs.codemagic.io/yaml/building-a-native-ios-app/ for more information
# Please review and update values in curly braces

workflows:
  ios-app:
    name: iOS App
    environment:
      vars:
        XCODE_WORKSPACE: "CodeMagicTestSample"
        XCODE_SCHEME: "CodeMagicTestSample"
      xcode: latest
      cocoapods: default
      CM_CERTIFICATE: Encrypted()
      CM_CERTIFICATE_PASSWORD: Encrypted(...)
      CM_PROVISIONING_PROFILE: Encrypted(...)
      CM_CERTIFICATE_PASSWORD: Encrypted(...)
    scripts:
      - xcodebuild build -workspace "$XCODE_WORKSPACE.xcworkspace" -scheme "$XCODE_SCHEME" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
    artifacts:
      - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.app
      - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.dSYM

# You can get more information regarding the YAML file here:
# https://docs.codemagic.io/building/yaml

# Workflow setup for building Native iOS project
workflows:
  # The following workflow is for generating a debug build (.app)
  ios-project-debug: # workflow ID
    name: CodeMagicTestSampleDebug # workflow name
    environment:
      xcode: latest
      cocoapods: default
    scripts:
      - |
        # run tests
        xcodebuild \
        -workspace "CodeMagicTestSample.xcworkspace" \
        -scheme "CodeMagicTestSample" \
        -sdk iphonesimulator \
        -destination 'platform=iOS Simulator,name=iPhone 11 Pro Max,OS=13.4' \
        clean build test CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
      - |
        # build .app
        xcodebuild build -workspace "CodeMagicTestSample.xcworkspace" -scheme "CodeMagicTestSample" CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO
    artifacts:
      - $HOME/Library/Developer/Xcode/DerivedData/**/Build/**/*.app
    publishing:
      email:
        recipients:
          - xxx@gmail.com # enter your email id here

  # The following workflow is for generating a release build (.ipa)
  ios-project-release: # workflow ID
    name: CodeMagicTestSample # workflow name
    environment:
      vars:
        CM_CERTIFICATE: Encrypted(...) # enter the encrypted version of your certificate
        CM_PROVISIONING_PROFILE: Encrypted(...) # enter the encrypted version of your provisioning profile
      xcode: latest
      cocoapods: default
    scripts:
      - keychain initialize
      - |
        # set up provisioning profiles
        PROFILES_HOME="$HOME/Library/MobileDevice/Provisioning Profiles"
        mkdir -p "$PROFILES_HOME"
        PROFILE_PATH="$(mktemp "$PROFILES_HOME"/$(uuidgen).mobileprovision)"
        echo ${CM_PROVISIONING_PROFILE} | base64 --decode > $PROFILE_PATH
        echo "Saved provisioning profile $PROFILE_PATH"
      - |
        # set up signing certificate
        echo $CM_CERTIFICATE | base64 --decode > /tmp/certificate.p12
        keychain add-certificates --certificate /tmp/certificate.p12  --certificate-password $CM_CERTIFICATE_PASSWORD
      - |
        # run tests
        xcodebuild \
        -workspace "CodeMagicTestSample.xcodeproj" \
        -scheme "CodeMagicTestSample" \
        -sdk iphonesimulator \
        -destination 'platform=iOS Simulator,name=iPhone 11 Pro Max,OS=13.4' \
        clean build test CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
      - |
        # build ipa
        xcode-project use-profiles
        xcode-project build-ipa --workspace "CodeMagicTestSample.xcodeproj" --scheme "CodeMagicTestSample"
    artifacts:
      - build/ios/ipa/*.ipa
      - /tmp/xcodebuild_logs/*.log
    publishing:
      email:
        recipients:
          - xxx@gmail.com # enter your email id here

codemagic.yamlの変更を行ったcommitをpushした後、管理画面からStart new buildをクリックしてビルドを開始します。

記載されたテストの実行やPublishingが成功したことを確認しました。

GitHubを見るとリポジトリにもテストの結果が反映されています。

まとめ

CodemagicはFlutter向けのCI/CDサービスとして開始されましたが、現在は各種ネイティブアプリのサポートも進められているとのことでしたが、確かに簡単に設定が出来ました。ただ、現状Codemagic以前から存在するサービスの方が古参なだけあって情報も豊富なので基本的にはFlutter用のCI/CDサービスとして使用しそうだなという感想を持っています。 FlutterでのCI/CDに載せる設定手順は公式ブログの以下の記事がわかりやすいです。

個人アプリで使用しているのでもう少し知見が溜まったら記事にしてみようかなと思っています。