概要
業務内でWatchアプリ込みのiOSアプリについてGithubActionsを使用し、
App Store ConnectにアップロードするようなActionを作成したので備忘録として
※今回はxcode build + Xcode自動署名方式を使用しています
構成
アプリ:iOS + WatchOS + Widget
xcodeproj形式のプロジェクトファイル
Swift-6
XCode-16
完成したコード
name: Deploy To App Store Connect
on:
push:
branches:
- main
jobs:
deploy:
runs-on: macos-15
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.1.0"
- name: Archive Scheme
run: |
xcodebuild -project ProjectName.xcodeproj \
-scheme SchemeName \
-configuration Release \
archive -archivePath SchemeName.xcarchive \
CODE_SIGNING_ALLOWED=NO
- name: Create ExportOptions.plist
run: |
echo '${{ secrets.EXPORT_OPTIONS }}' > ExportOptions.plist
- name: Save App Store Connect Private Key to File
run: |
mkdir private_keys
echo "${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }}" > ./private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8
- name: Export .ipa
run: |
xcodebuild -exportArchive \
-archivePath SchemeName.xcarchive \
-exportOptionsPlist ExportOptions.plist \
-exportPath export \
-allowProvisioningUpdates \
-authenticationKeyPath $PWD/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8 \
-authenticationKeyID ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} \
-authenticationKeyIssuerID ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
- name: Upload ipa to TestFlight
run: |
xcrun altool --upload-app -f export/SchemeName.ipa \
--type ios \
--apiKey ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} \
--apiIssuer ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} \
--apiPrivateKey $pwd/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8
- name: Cleanup
run: rm ExportOptions.plist `pwd`/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8
アプリ固有の環境値
下記については各々のProjectに合わせて変更してください
ProjectName.xcodeproj:プロジェクト名
SchemeName:スキーム名
各種設定
Github
Secrets And Variablesに必要な値を取得します
App Store Connect API Keyの作成
API Keyを発行してください
※App Store Connect -> ユーザーとアクセス -> 統合 -> チームキーの作成
発行したら、下記三点をコピーしておきましょう
・Issue ID
・KeyID
・API Key ID
Export Options
Xcodeからipaファイルをexportするとexport先のフォルダにExportOptions.plistが作成されるのでそちらを使用します
Xcode -> Archive -> Distribute App -> Custom -> Export
※ TestFlightのみアップするように設定もできる
stgブランチ -> Test Flight
mainブランチ -> AppStoreConnect
のようにブランチごとに分けると良いかも
Github Secrets And Variables設定
上記で取得した値を
Github Secrets And Variables -> Repository secretsに設定
APP_STORE_CONNECT_PRIVATE_KEY: App Store Connectにて取得したPrivate Key
↑ VSCodeなどで証明書を開いてコピペ
APP_STORE_CONNECT_API_KEY_ID: App Store Connectにて取得したAPI Key
APP_STORE_CONNECT_ISSUER_ID: App Store Connectにて取得したIssuer ID
EXPORT_OPTIONS: XcodeでExportしたExportOption.plistの値
↑ VSCodeなどでplistを開いてコピペ
ここまで設定すれば動くようになります
あとは指定したブランチへマージし、Giahub Actionが動くことを確認してください
解説
仮想環境のセットアップ
仮想環境のセットアップしxcodeをインストール
jobs:
deploy:
runs-on: macos-15
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.1.0"
アーカイブの作成
configuration
Archive時のBuild Configration
CODE_SIGNING_ALLOWED=NO
アーカイブ時は署名が不要なのでoptionをNOで設定
- name: Archive Scheme
run: |
xcodebuild -project ProjectName.xcodeproj \
-scheme SchemeName \
-configuration Release \
archive -archivePath SchemeName.xcarchive \
CODE_SIGNING_ALLOWED=NO
各種設定ファイルの作成
ExportOptions.plistとApp Storeアップロード用の証明書を作成します
※ 後述しますが、p8形式の秘密鍵を書き出す際に、ファイル名はApp Store Connectで取得した名称をそのまま使用してください。
- name: Create ExportOptions.plist
run: |
echo '${{ secrets.EXPORT_OPTIONS }}' > ExportOptions.plist
- name: Save App Store Connect Private Key to File
run: |
mkdir private_keys
echo "${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }}" > ./private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8
ipaファイルの書き出し
App Store ConnectのAPIKey情報をもとに署名を行い、ipaファイルを作成します
- name: Export .ipa
run: |
xcodebuild -exportArchive \
-archivePath SchemeName.xcarchive \
-exportOptionsPlist ExportOptions.plist \
-exportPath export \
-allowProvisioningUpdates \
-authenticationKeyPath $PWD/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8 \
-authenticationKeyID ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} \
-authenticationKeyIssuerID ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
ipaファイルのアップロード
--type ios
アップロードする際にはtypeをiOSを指定
App Store API Keyを使用して、App Store Connectへアップロードします
- name: Upload ipa to TestFlight
run: |
xcrun altool --upload-app -f export/SchemeName.ipa \
--type ios \
--apiKey ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }} \
--apiIssuer ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }} \
--apiPrivateKey $pwd/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8
事後処理
不要ファイルを削除します
- name: Cleanup
run: rm ExportOptions.plist `pwd`/private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8
詰まったところ
MacOS 仮想環境のバージョンについて
mac-osの仮想環境ではXCodeが1バージョンしか指定できなくなっていた。
(作成した当時はmac-osのlatestを選択するとsonoma(MacOS14 - XCode15)が指定され、Xcode16が見つからんぞ〜って怒られる。
※2024年11月中旬
ログ
Switching Xcode to version '16.1.0'...
Available versions:
┌─────────┬──────────┬─────────────┬─────────────┬────────┬──────────────────────────────────┐
│ (index) │ version │ buildNumber │ releaseType │ stable │ path │
├─────────┼──────────┼─────────────┼─────────────┼────────┼──────────────────────────────────┤
│ 0 │ '15.4.0' │ '15F31d' │ 'GM' │ true │ '/Applications/Xcode_15.4.app' │
│ 1 │ '15.3.0' │ '15E204a' │ 'GM' │ true │ '/Applications/Xcode_15.3.app' │
│ 2 │ '15.2.0' │ '15C500b' │ 'GM' │ true │ '/Applications/Xcode_15.2.app' │
│ 3 │ '15.1.0' │ '15C65' │ 'GM' │ true │ '/Applications/Xcode_15.1.app' │
│ 4 │ '15.0.1' │ '15A507' │ 'GM' │ true │ '/Applications/Xcode_15.0.1.app' │
└─────────┴──────────┴─────────────┴─────────────┴────────┴──────────────────────────────────┘
Error: Could not find Xcode version that satisfied version spec: '16.1.0'
そのため、macos-15(sequoia)とXcodeのバージョンを直接指定
jobs:
deploy:
# MacOS 15(sequoia)を指定
runs-on: macos-15
steps:
- name: Checkout source code
uses: actions/checkout@v4
- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
# Xcodeのバージョンを直接指定
xcode-version: "16.1.0"
Archive commandについて
WatchOSを含む場合は -sdk optionを設定したらビルドでこける
(...Watch系のPackageを取得後にこけていた。いまいち理解できなくて時間がたくさん溶けました笑
note: Disabling previews because SWIFT_VERSION is set and SWIFT_OPTIMIZATION_LEVEL=-Owholemodule, expected -Onone (in target 'SwiftUI Apple Watch Decimal Pad' from project 'SwiftUI Apple Watch Decimal Pad')
note: Disabling previews because SWIFT_VERSION is set and SWIFT_OPTIMIZATION_LEVEL=-Owholemodule, expected -Onone (in target 'Supabase' from project 'Supabase')
** ARCHIVE FAILED **
古い記事ですが、ここを参照
コメントの直後の行を削除すると動くようになりました。
- name: Archive Scheme
run: |
xcodebuild -project ProjectName.xcodeproj \
-scheme SchemeName \
-configuration Release \
# ↓ sdkを指定するとbuildエラーになる
-sdk iphonesimulator
archive -archivePath SchemeName.xcarchive \
CODE_SIGNING_ALLOWED=NO
証明書の名前について
証明書の命名には規約があるようです。
(自分はapp_store_connect_key.p8のような命名にして8時間ぐらい時間を溶かしました...)
"Error Domain=ITunesConnectionAuthenticationErrorDomain Code=-26000 \"Failed to generate JWT token: Error Domain=NSCocoaErrorDomain Code=-43 \"Failed to load AuthKey file.\" UserInfo={NSLocalizedDescription=Failed to load AuthKey file., NSLocalizedFailureReason=The file \U2018AuthKey_***.p8\U2019 could not be found in any of these locations: '~/path/to/root/private_keys', '~/private_keys', '~/.private_keys', '~/.appstoreconnect/private_keys'.}\" UserInfo={NSLocalizedRecoverySuggestion=Failed to generate JWT token: Error Domain=NSCocoaErrorDomain Code=-43 \"Failed to load AuthKey file.\" UserInfo={NSLocalizedDescription=Failed to load AuthKey file., NSLocalizedFailureReason=The file \U2018AuthKey_***.p8\U2019 could not be found in any of these locations: '~/path/to/root/private_keys', '~/private_keys', '~/.private_keys', '~/.appstoreconnect/private_keys'.}, NSLocalizedDescription=Failed to generate JWT token: Error Domain=NSCocoaErrorDomain Code=-43 \"Failed to load AuthKey file.\" UserInfo={NSLocalizedDescription=Failed to load AuthKey file., NSLocalizedFailureReason=The file \U2018AuthKey_***.p8\U2019 could not be found in any of these locations: '~/path/to/root/private_keys', '~/private_keys', '~/.private_keys', '~/.appstoreconnect/private_keys'.}, NSLocalizedFailureReason=App Store operation failed.}"
AuthKey_{API_KEY_ID}.p8という形式で指定しましょう
- name: Save App Store Connect Private Key to File
run: |
mkdir private_keys
echo "${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY }}" > ./private_keys/AuthKey_${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}.p8
と、まぁ色々引っかかりましたがなんとかデプロイするGithub Actionが完成しました。
iOSのみのアプリを書き出す際のxcodebuildコマンドは割とネットに転がっていましたが、AppleWatchアプリを含む場合のxcodebuildコマンドはなかなか見つからなかったので苦労しました〜