top of page
Matt

iOSアプリをTestFlightにデプロイするためのGithub Actionsパイプラインを作成する方法

更新日:2023年10月12日

前回のブログ記事では、テストと統合の自動化の重要性について書きました。これは完全に自動化されたCICDパイプラインの一部として行われます。Github Actionsワークフローのymlファイルのコードスニペットを共有しました。

この記事では、これを正確にどのように達成するかを実証します。コードを表示するには、こちらをクリックしてください

このワークフローは以下のことを行います。

パイプライン環境変数

再利用される変数をグローバルパイプライン変数としてキャプチャするのは常に良い習慣です

コード:

env:
  ARCHIVE_SCHEME: "Your Project Name (e.g. Falling Sky)"
  APP_NAME: "Your App Name (e.g. FallingSky)"
  BUNDLE_ID: "Your Bundle Id (e.g. com.ka.fallingsky)"

ワークスペースのクリーンアップ(自己ホストランナーに適しています)

コード:

- name: Clean Workspace
        run: |
          rm -rf $GITHUB_WORKSPACE/* 
          rm -rf $RUNNER_TEMP/*

独自のビルドエージェントを実行している場合、各ビルドが互いに完全に分離して実行されることを確認したいと思うでしょう。ここで行っていることは、ビルドエージェントがビルドの一部として使用する2つのフォルダ内のファイルを単に削除するだけです。


コードをチェックアウトする

コード:

- name: Checkout
        uses: actions/checkout@v3

これにはGitHubアクションのチェックアウトステップを使用し、コードの最新バージョンをチェックアウトします。


デフォルトのスキームを設定する

コード:

- name: Set Default Scheme
        run: |
          scheme_list=$(xcodebuild -list -json | tr -d "\n")
          default=$(echo $scheme_list | ruby -e "require 'json'; puts JSON.parse(STDIN.gets)['project']['targets'][0]")
          echo $default | cat >default
          echo Using default scheme: $default

このステップは、Xcodeプロジェクトのスキームのリストを取得し、最初のターゲット(デフォルトのスキームと想定されている)を抽出し、デフォルトのスキームの名前を「default」という名前のファイルに保存し、デフォルトのスキームの使用を示すログメッセージも提供します。これは、将来のパイプラインステップで特定のXcodeスキームがデフォルトとして設定されていることに依存する場合に役立ちます。


自動化されたテストのビルドと実行

コード:

- name: Build for test
        env:
          scheme: ${{ 'default' }}
          platform: ${{ 'iOS Simulator' }}
        run: |
          # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
          device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
          if [ $scheme = default ]; then scheme=$(cat default); fi
          if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
          file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`

          xcodebuild clean build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device"

      - name: Test
        timeout-minutes: 30
        env:
          scheme: ${{ 'default' }}
          platform: ${{ 'iOS Simulator' }}
        run: |
          # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
          device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
          if [ $scheme = default ]; then scheme=$(cat default); fi
          if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
          file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
          xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device" -test-iterations 3 -retry-tests-on-failure

これは、パイプラインがテストに失敗した場合に停止することを保証する重要なステップです。これは、本番環境でのリグレッションを防ぐのに理想的です。


証明書とプロビジョニングプロファイルをインストールする

コード:

- name: Install the Apple certificate and provisioning profile
        env:
          BUILD_CERTIFICATE_BASE64: ${{ secrets.DIST_CERT_BASE64 }}
          P12_PASSWORD: ${{ secrets.DIST_CERT_P12_PASSWORD }}
          BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          # create variables
          CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
          PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

          # import certificate and provisioning profile from secrets
          echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
          echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH

          # create temporary keychain
          security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

          # import certificate to keychain
          security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          # apply provisioning profile
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

これは、アプリをアーカイブしてTestFlightに配布し、その後App Storeに送信する準備ができるようにするために必要です。このステップには、多くのGitHubリポジトリレベルのシークレットが必要です。これらの設定方法について以下で詳しく説明します。詳細については、https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development をご覧ください。


DIST_CERT_BASE64 & DIST_CERT_P12_PASSWORD

Base64として格納されたp12ビルド証明書とその署名に使用されるパスワードです。次の手順に従ってp12証明書を作成したら、次のコマンドを実行してBase64テキストを取得する必要があります:

base64 -i BUILD_CERTIFICATE.p12 | pbcopy

そして、この情報をリポジトリのシークレットとして保存します。


PROVISIONING_PROFILE_BASE64

これは配布に必要なプロビジョニングプロファイルです。プロファイリングプロファイルを作成したら(プロビジョニングプロファイルを作成する必要がありますhttps://developer.apple.com)、次のコマンドを実行してBase64テキストを取得します:

base64 -i PROVISIONING_PROFILE.mobileprovision | pbcopy

そして、リポジトリシークレットとして保存します。


KEYCHAIN_PASSWORD

各実行で一時キーチェーンが作成されるため、これはランダムに生成された文字列であっても問題ありません。


バージョン番号を増やす

コード:

- uses: yanamura/ios-bump-version@v1
        with:
          version: 1.1.0
          build-number: ${{ github.run_number }}

GitHubの実行番号をバージョンの「ユニーク」部分として使用します。これにより、関連するファイルが更新され、App Store Connectで新しいビルドとして利用可能になります。


アプリのビルドとアーカイブ

コード:

- name: Build and archive app
        run: |
          if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
          file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
          xcodebuild archive -"$filetype_parameter" "$file_to_build" -scheme "$ARCHIVE_SCHEME" -archivePath "$APP_NAME" -configuration Release

Creating an IPA file

コード:

- name: Create IPA file
        env:
          EXPORT_OPTIONS_PLIST: ${{ secrets.EXPORT_OPTIONS_PLIST_BASE64 }}
        run: |
          EXPORT_OPTIONS_PLIST_PATH=$RUNNER_TEMP/ExportOptions.plist
          echo -n "$EXPORT_OPTIONS_PLIST" | base64 -d -o $EXPORT_OPTIONS_PLIST_PATH
          xcodebuild -exportArchive -archivePath $GITHUB_WORKSPACE/$APP_NAME.xcarchive -exportPath $RUNNER_TEMP/build -exportOptionsPlist $EXPORT_OPTIONS_PLIST_PATH
          mv "$RUNNER_TEMP/build/$ARCHIVE_SCHEME.ipa" $RUNNER_TEMP/build/$APP_NAME.ipa

これにより、TestFlightにアップロードする準備が整ったIPAファイルが作成されます

EXPORT_OPTIONS_PLIST_BASE64シークレットを使用します。これはExportOptions.plistのBase64です。これを多くのオンラインの例と共に手動で作成できます。次の手順を実行してExportOptions.plistファイルのBase64をキャプチャし、リポジトリシークレットとして保存します:

base64 -i ExportOptions.plist | pbcopy

TestFlightにデプロイする

コード:

- name: Upload to TestFlight
        env:
          API_KEY_BASE64: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
        run: |
          mkdir -p ./private_keys
          echo -n "$API_KEY_BASE64" | base64 --decode -o "./private_keys/AuthKey_${{ secrets.APPSTORE_API_KEY_ID }}.p8"
          xcrun altool --validate-app -f ${{ runner.temp }}/build/${{ env.APP_NAME }}.ipa -t ios --apiKey ${{ secrets.APPSTORE_API_KEY_ID }} --apiIssuer ${{ secrets.APPSTORE_ISSUER_ID }}
          xcrun altool --upload-app -f ${{ runner.temp }}/build/${{ env.APP_NAME }}.ipa -t ios --apiKey ${{ secrets.APPSTORE_API_KEY_ID }} --apiIssuer ${{ secrets.APPSTORE_ISSUER_ID }}

これにより、生成されたIPAファイルがTestFlightにアップロードされます。完了すると、Appleが処理するのに数分かかりますが、デバイスでテスト用に利用可能になります。

このステップにはAPPSTORE_API_PRIVATE_KEY、APPSTORE_API_KEY_ID、APPSTORE_ISSUER_IDの3つのシークレットが必要です。これはApp Store Connectで作成されたAPIキーから生成されます。キーをダウンロードし、.p8ファイルのBase64をキャプチャして、APPSTORE_API_PRIVATE_KEYリポジトリシークレットとして保存します。キーIDと発行者IDはApp Store Connectで見つかります。


サポート方法


このコンテンツは常に無料で提供されます。価値があると感じた場合、他の人と共有を検討してください. さらに、私たちのゲームをダウンロードして正直なレビューを残すことは、私たちを大いにサポートします. 質問やフィードバックがある場合は、お気軽にお問い合わせください。できる限りお答えします.


今すぐApple App StoreからFalling Skyをダウンロード: https://apps.apple.com/app/id6446787964


以下はymlファイル全体のコードです:

name: My Automated CI/CD Pipeline

on:
  push:
    branches: [ "main" ]
  workflow_dispatch:

env:
  ARCHIVE_SCHEME: "Your Project Name (e.g. Falling Sky)"
  APP_NAME: "Your App Name (e.g. FallingSky)"
  BUNDLE_ID: "Your Bundle Id (e.g. com.ka.fallingsky)"

jobs:
  build:
    name: Build and Test default scheme using any available iPhone simulator
    runs-on: self-hosted

    steps:
      - name: Clean Workspace
        run: |
          rm -rf $GITHUB_WORKSPACE/* 
          rm -rf $RUNNER_TEMP/*

      - name: Checkout
        uses: actions/checkout@v3

      - name: Set Default Scheme
        run: |
          scheme_list=$(xcodebuild -list -json | tr -d "\n")
          default=$(echo $scheme_list | ruby -e "require 'json'; puts JSON.parse(STDIN.gets)['project']['targets'][0]")
          echo $default | cat >default
          echo Using default scheme: $default

      - name: Build for test
        env:
          scheme: ${{ 'default' }}
          platform: ${{ 'iOS Simulator' }}
        run: |
          # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
          device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
          if [ $scheme = default ]; then scheme=$(cat default); fi
          if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
          file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`

          xcodebuild clean build-for-testing -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device"

      - name: Test
        timeout-minutes: 30
        env:
          scheme: ${{ 'default' }}
          platform: ${{ 'iOS Simulator' }}
        run: |
          # xcrun xctrace returns via stderr, not the expected stdout (see https://developer.apple.com/forums/thread/663959)
          device=`xcrun xctrace list devices 2>&1 | grep -oE 'iPhone.*?[^\(]+' | head -1 | awk '{$1=$1;print}' | sed -e "s/ Simulator$//"`
          if [ $scheme = default ]; then scheme=$(cat default); fi
          if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
          file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
          xcodebuild test-without-building -scheme "$scheme" -"$filetype_parameter" "$file_to_build" -destination "platform=$platform,name=$device" -test-iterations 3 -retry-tests-on-failure

      - name: Install the Apple certificate and provisioning profile
        env:
          BUILD_CERTIFICATE_BASE64: ${{ secrets.DIST_CERT_BASE64 }}
          P12_PASSWORD: ${{ secrets.DIST_CERT_P12_PASSWORD }}
          BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
          KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
        run: |
          # create variables
          CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
          PP_PATH=$RUNNER_TEMP/build_pp.mobileprovision
          KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db

          # import certificate and provisioning profile from secrets
          echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
          echo -n "$BUILD_PROVISION_PROFILE_BASE64" | base64 --decode -o $PP_PATH

          # create temporary keychain
          security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
          security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH

          # import certificate to keychain
          security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
          security list-keychain -d user -s $KEYCHAIN_PATH

          # apply provisioning profile
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

      - uses: yanamura/ios-bump-version@v1
        with:
          version: 1.1.0
          build-number: ${{ github.run_number }}

      - name: Build and archive app
        run: |
          if [ "`ls -A | grep -i \\.xcworkspace\$`" ]; then filetype_parameter="workspace" && file_to_build="`ls -A | grep -i \\.xcworkspace\$`"; else filetype_parameter="project" && file_to_build="`ls -A | grep -i \\.xcodeproj\$`"; fi
          file_to_build=`echo $file_to_build | awk '{$1=$1;print}'`
          xcodebuild archive -"$filetype_parameter" "$file_to_build" -scheme "$ARCHIVE_SCHEME" -archivePath "$APP_NAME" -configuration Release

      - name: Create IPA file
        env:
          EXPORT_OPTIONS_PLIST: ${{ secrets.EXPORT_OPTIONS_PLIST_BASE64 }}
        run: |
          EXPORT_OPTIONS_PLIST_PATH=$RUNNER_TEMP/ExportOptions.plist
          echo -n "$EXPORT_OPTIONS_PLIST" | base64 -d -o $EXPORT_OPTIONS_PLIST_PATH
          xcodebuild -exportArchive -archivePath $GITHUB_WORKSPACE/$APP_NAME.xcarchive -exportPath $RUNNER_TEMP/build -exportOptionsPlist $EXPORT_OPTIONS_PLIST_PATH
          mv "$RUNNER_TEMP/build/$ARCHIVE_SCHEME.ipa" $RUNNER_TEMP/build/$APP_NAME.ipa

      - name: Upload to TestFlight
        env:
          API_KEY_BASE64: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
        run: |
          mkdir -p ./private_keys

          echo -n "$API_KEY_BASE64" | base64 --decode -o "./private_keys/AuthKey_${{ secrets.APPSTORE_API_KEY_ID }}.p8"

          xcrun altool --validate-app -f ${{ runner.temp }}/build/${{ env.APP_NAME }}.ipa -t ios --apiKey ${{ secrets.APPSTORE_API_KEY_ID }} --apiIssuer ${{ secrets.APPSTORE_ISSUER_ID }}

          xcrun altool --upload-app -f ${{ runner.temp }}/build/${{ env.APP_NAME }}.ipa -t ios --apiKey ${{ secrets.APPSTORE_API_KEY_ID }} --apiIssuer ${{ secrets.APPSTORE_ISSUER_ID }}




閲覧数:273回
bottom of page