手順の解説
GitHub
GitLabで利用する方法もあるのですが、GitLabだとmacOSのランナーが課金するかオンプレにしないと使えないので今回はGitHubを利用します。
今回は作業用のレポジトリをapple-appstore-connect-testとして作成しました。
このレポジトリはパブリックでもプライベートでも構いません。
よくわかっていない方は上のレポジトリをクローンすれば同じ環境が作れます。
プライベートレポジトリ
GitLabではSecure Filesという仕組みでデータをセキュアに保存できたのですが、GitHubにはないので証明書を保存するためのレポジトリを作成します。
今回は証明書保存用のレポジトリをapple-appstore-connect-test-matchとして作成しました。
こちらのレポジトリは機密情報を含むため、必ずプライベートレポジトリにしてください
ACCESS_TOKEN
多分、ローカルでテストしたい場合に必要になります。
New personal access token (classic)から作成します。
今回は無限に使う予定だったので有効期限をなしにしましたが、どうせApple Developer Programは一年で切れるので一年間でも良いと思います。
スコープはよくわかっていないのですがとりあえずrepoにさえチェックが入っていれば動きます。
作成したらトークンの内容を何処かにコピーしておきます。
App Store Connect
App Store Connect APIからAPIキーを生成します。
このAPIキーを利用することで証明書を取得してくるという仕組みです。
従来、2FAが有効になっていると証明書の取得は大変にめんどくさかったのですがAPIキーを利用することでそれらのしがらみから解放されました。
これを使うことのメリットは公式ドキュメントに書いてあるので目を通すと良いかもしれません。
- No 2FA needed
- Better performance
- Documented API
- Increased reliability
はい、いいことしかありませんね。
APIキーを発行したらIssuer ID、Key IDの二つをコピーします。
そして最後にAPIキー自体をダウンロードします。このキーは一度ダウンロードすると二度とダウンロードできないので大切に保管してください。
ダウンロードしたらそのファイルをBase64でエンコードします。ただし、このファイル自体が機密情報なので間違ってもオンラインサービスなどでエンコードしないようにしましょう。
cat AuthKey_XXXXXXXX.p8 | base64とコマンドを打てばエンコードされた文字列が表示されるので、この値をコピーします。
Base64自体は暗号化でもなんでもないのでやはりこのデータも機密情報となります。取り扱いには注意してください。
環境変数
ここまでできたら環境変数をGitHubのRepository secretsに登録します。
登録すべきデータは以下の五つです。ローカルで検証したい場合は.envに書き込んでしまっても良いでしょう。
| キー | 意味 |
|---|---|
| MATCH_PASSWORD | パスワード |
| ASC_KEY_ID | キーID |
| ASC_ISSUER_ID | 発行者ID |
| ASC_KEY_CONTENT | Base64した文字列 |
| PRIVATE_TOKEN | アクセストークン |
どうやらFastfileで利用する
matchはGitHub Actionsに渡されるGITHUB_TOKENではアクセスできないようでAccessible from repositories owned by the userの項目を変更してもアクセス権がないと表示されてしまったもっと強い権限をアクセストークンに与えれば解決するかもしれないが、とりあえず保留しておく
ローカルで作業する場合は上の五つ加えてGITHUB_ACTORが必要になります。
これらの値を.envに書き込んでください。GITHUB_ACTORはGitHubのユーザー名になるので私の場合はtkgstratorとなります。
MATCH_PASSWORDは適当に1Passwordとかで強めのパスフレーズを作成して設定しておけば良いです。
Fastlane
ここからあとの作業はローカルで行います。
init
fastlane initを実行します。
[✔] 🚀[✔] Looking for iOS and Android projects in current directory...[10:28:57]: Created new folder './fastlane'.[10:28:57]: Detected an iOS/macOS project in the current directory: 'TestApp.xcodeproj'[10:28:57]: -----------------------------[10:28:57]: --- Welcome to fastlane 🚀 ---[10:28:57]: -----------------------------[10:28:57]: fastlane can help you with all kinds of automation for your mobile app[10:28:57]: We recommend automating one task first, and then gradually automating more over time[10:28:57]: What would you like to use fastlane for?1. 📸 Automate screenshots2. 👩✈️ Automate beta distribution to TestFlight3. 🚀 Automate App Store distribution4. 🛠 Manual setup - manually setup your project to automate your tasks今回はTestFlightにベータ版をデプロイしたいので2を選択します。
いろいろ出てきますがとりあえずエンターキーを押します。
fastlane/├── Appfile└── Fastfileが作成されると思います。
match init
fastlane match initを実行します。
[✔] 🚀[16:51:49]: fastlane match supports multiple storage modes, please select the one you want to use:1. git2. google_cloud3. s34. gitlab_secure_files?今回はGitのプライベートレポジトリを利用するので1を選択します。
プライベートレポジトリを入力しろと言われるので今回の場合は**https://github.com/tkgstrator/apple-appstore-connect-test-match.git**と入力しました。
当たり前ですが、自身が管理するプライベートレポジトリを入力するように
途中でパスワードの入力を求められたらそれが先程設定したMATCH_PASSWORDになります。
これを入れないと対話式になってしまうのでGitHub Actionsでmatchができなくなります。
万が一間違った値を入力してしまってもfastlane match change_passwordでパスフレーズを初期化できるので安心してください。
match
とりあえず開発用と本番用の証明書があればよいと思うので、
fastlane match developmentfastlane match appstoreで証明書を発行します。
発行数が上限に達してるよと言われた場合にはfastlane match nuke developmentとかすれば大丈夫です。
ただし
appstoreの署名を取り消すにはfastlane match nuke distributionという罠があります。
Fastflie
fastlane/├── Appfile├── Fastfile└── Matchfileさて、上のようなファイルができていると思いますがここでFastfileだけ編集します。
その他はこのまま放置でオッケーです。
platform :ios do desc "Push a new beta build to TestFlight" lane :beta do setup_ci(provider: "travis") api_key = app_store_connect_api_key( key_id: ENV['ASC_KEY_ID'], issuer_id: ENV['ASC_ISSUER_ID'], key_content: ENV['ASC_KEY_CONTENT'], in_house: false, is_key_content_base64: true ) match( git_basic_authorization: Base64.strict_encode64("#{ENV['GITHUB_ACTOR']}:#{ENV['GITHUB_TOKEN']}"), api_key: api_key, app_identifier: 'work.tkgstrator.TestApp', type: "appstore", readonly: is_ci ) increment_build_number(xcodeproj: "TestApp.xcodeproj", build_number: latest_testflight_build_number + 1) build_app(scheme: "TestApp", export_method: "app-store", xcargs: "-allowProvisioningUpdates", output_directory: "build") upload_to_testflight(api_key: api_key, notify_external_testers: true, changelog: "Deploy by GitHub Actions.") endend自分でもよくわかっていないのですが、上のコードをそのまま書きます。
ただしapp_identifierやschemeやxcodeprojに関しては各自変更してください。
TestFlightであっても
appstoreを指定しなければいけないのは個人的な謎ポイントではある
GitHub Actions
最後にGitHub Actionsで上のコードが実行されるようにします。
mkdir -p .github/workflowsでディレクトリを作成して、その中に適当にtestflight.yamlを作成しました。
name: Upload TestFlight
on: workflow_dispatch: push: branches: - master
jobs: build: runs-on: [macos-14]
steps: - name: Checkout uses: actions/checkout@v4
- name: Select Xcode version run: sudo xcode-select -s '/Applications/Xcode_15.2.app/Contents/Developer'
- name: Show Xcode version run: xcodebuild -version
- name: Cache uses: actions/cache@v4 with: path: vendor/bundle key: ${{ runner.os }}-gems-${{ hashFiles('**/Gemfile.lock') }} restore-keys: | ${{ runner.os }}-gems-
- name: Bundle install run: | bundle config path vendor/bundle bundle install --jobs 4 --retry 3
- name: Upload a new build to App Store Connect env: ASC_KEY_ID: ${{ secrets.ASC_KEY_ID }} ASC_ISSUER_ID: ${{ secrets.ASC_ISSUER_ID }} ASC_KEY_CONTENT: ${{ secrets.ASC_KEY_CONTENT }} MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} GITHUB_TOKEN: ${{ secrets.PRIVATE_TOKEN }} GITHUB_ACTOR: ${{ secrets.GITHUB_ACTOR }} run: bundle exec fastlane betaGITHUB_ACTORについては環境変数に最初から入っているので設定が不要というわけです。
PRIVATE_TOKENも利用しないようなコードに変えたい…GitHub Actionsからプライベートレポジトリを読み込めないのが問題…
あと、キャッシュがあんまり効いている気がしない、コピペしたけど間違ってないこれ?
記事は以上。