日時 2025年10月 の話
先行(1ヶ月早い)して公開した Android編 へのリンクを載せておきます
最初の構想では 1 記事にするつもりだったので
Android 編といいながら、こちらの iOS 編の説明が書いてあったりするので、、
今回の目標
- Unity iOS プラットフォーム 対象
-
Github Actions で fastlane にて ipa 作成 + TestFlight アップロード
(この時点で Github / TestFlight 使用確定) - fastlane は match / gym / upload_to_testflight 等を使用して
全てのビルド処理をまかなう - Github Actions は Mac ランナーのセットアップをまかなう
iOS ビルドは コード署名 が鬼門
その仕組みやハマった点を説明すると
とんでもなく長い解説になるので iOS 編は答えから
Github Actions 用 YAML
name: Build Unity for iOS
on:
workflow_dispatch:
jobs:
build_ios:
name: Build and Upload iOS
runs-on: macos-latest
env:
# unity-builder がデフォルトで build/{プラットフォーム} に出力 + Unity が iOS に出力する
BUILD_LOCAL_PATH: 'build/iOS/iOS'
steps:
# LFS 使用
- name: Checkout repository
uses: actions/checkout@v4
with:
lfs: true
# キャッシュを設定(ビルド時間短縮のため)
- name: Cache Unity Library
uses: actions/cache@v4
with:
path: Library
key: Library-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }}
restore-keys: Library-
# XCode は最新にする
- name: Select Xcode version
id: select_xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
# Unity のバージョンは ProjectVersion.txt をみてもらう
# 指定したければ unityVersion: で指定する
- name: Build Xcode project with Unity
uses: game-ci/unity-builder@v4
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
targetPlatform: iOS
# Xcode プロジェクト出力先に fastlane 用のファイルをコピーする
- name: Copy fastlane configuration
env:
BUILD_FULL_PATH: ${{ format('{0}/{1}', github.workspace, env.BUILD_LOCAL_PATH) }}
run: |
mkdir -p ${{ env.BUILD_FULL_PATH }}/fastlane
cp ConfigFastlane/Gemfile ${{ env.BUILD_FULL_PATH }}
cp ConfigFastlane/Gemfile.lock ${{ env.BUILD_FULL_PATH }}
cp ConfigFastlane/Fastfile ${{ env.BUILD_FULL_PATH }}/fastlane/
cp ConfigFastlane/Appfile ${{ env.BUILD_FULL_PATH }}/fastlane/
cp ConfigFastlane/Matchfile ${{ env.BUILD_FULL_PATH }}/fastlane/
# match 用の SSH 秘密鍵を設定する
- name: Setup SSH key for match
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.MATCH_GIT_SSH_KEY }}
# fastlane 用の ruby セットアップ
# 3.3 系に留めた
- name: Set up Ruby and Bundler
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3.9'
bundler-cache: true
working-directory: ${{ env.BUILD_LOCAL_PATH }}
# fastlane に全てやってもらう
- name: Run fastlane to build and upload
uses: maierj/fastlane-action@v3.1.0
env:
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64 }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_KEYCHAIN_PASSWORD: ${{ secrets.MATCH_KEYCHAIN_PASSWORD }}
MATCH_KEYCHAIN_NAME: ${{ secrets.MATCH_KEYCHAIN_NAME }}
APPLE_IDENTIFIER: ${{ secrets.APPLE_IDENTIFIER }}
APPLE_TEAM_NAME: ${{ secrets.APPLE_TEAM_NAME }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
lane: 'build_and_upload'
subdirectory: ${{ env.BUILD_LOCAL_PATH }}
Fastfile
default_platform(:ios)
platform :ios do
desc "Run Xcode to archive the ipa and upload to TestFlight"
lane :build_and_upload do |options|
# 基本的にビルド番号は日時より自動で作成だが(必ず番号が大きくなる)
# 引数指定も用意しておく
build_number = options[:build_number] || Time.now.strftime("%Y%m%d%H%M")
# Mac ランナーではデフォルトのキーチェーンではパスワードを聞かれるなど止まってしまうので
# 自身で新しくキーチェーンを作成して使用する
create_keychain(
name: ENV["MATCH_KEYCHAIN_NAME"],
password: ENV["MATCH_KEYCHAIN_PASSWORD"],
default_keychain: true,
unlock: true,
timeout: 1200
)
# キーチェーンの検索リストを再設定する
# create_keychain だけで上手く行かない
sh "security list-keychains -s #{ENV['MATCH_KEYCHAIN_NAME']} login.keychain"
# 自動署名だとトラブルが多いのでマニュアル署名
match(
type: "appstore",
app_identifier: ENV["APPLE_IDENTIFIER"],
team_name: ENV["APPLE_TEAM_NAME"],
team_id: ENV["APPLE_TEAM_ID"],
keychain_name: ENV["MATCH_KEYCHAIN_NAME"],
keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
readonly: true
)
# キーチェーンの秘密鍵にアクセス権を付与
sh "security set-key-partition-list -S apple-tool:,apple: -k '#{ENV['MATCH_KEYCHAIN_PASSWORD']}' #{ENV['MATCH_KEYCHAIN_NAME']}"
# Unity の設定がどうなっていても Xcode をマニュアル署名になる様にする
update_code_signing_settings(
use_automatic_signing: false,
path: "Unity-iPhone.xcodeproj",
targets: ["Unity-iPhone"],
team_id: ENV["APPLE_TEAM_ID"],
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore #{ENV['APPLE_IDENTIFIER']}",
bundle_identifier: ENV["APPLE_IDENTIFIER"]
)
increment_build_number(build_number: build_number)
gym(
export_method: "app-store",
clean: true,
derived_data_path: "derived_data",
export_options: {
provisioningProfiles: { ENV["APPLE_IDENTIFIER"] => "match AppStore #{ENV['APPLE_IDENTIFIER']}" }
}
)
# TestFlight アップロード様に API Key(p8)を設定
app_store_connect_api_key(
key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
issuer_id: ENV["APP_STORE_CONNECT_API_ISSUER_ID"],
key_content: ENV["APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64"],
is_key_content_base64: true
)
# TestFlight iへ ipa をアップロード
upload_to_testflight(skip_waiting_for_build_processing: true)
end
# ローカルビルド用
# ローカル Mac は 個人設定がされているはずなので fastlane もシンプル
desc "(LOCAL)Run Xcode to archive the ipa and upload to TestFlight"
lane :local_build_and_upload do |options|
# 基本的にビルド番号は日時より自動で作成だが(必ず番号が大きくなる)
# 引数指定も用意しておく
build_number = options[:build_number] || Time.now.strftime("%Y%m%d%H%M")
match(
type: "appstore",
readonly: true
)
increment_build_number(build_number: build_number)
gym(
export_method: "app-store",
clean: true
)
upload_to_testflight(skip_waiting_for_build_processing: true)
end
# 念の為 処理の最後とエラー時に作成したキーチェーンを削除
after_all do |lane|
if is_ci?
delete_keychain(name: ENV["MATCH_KEYCHAIN_NAME"])
end
end
error do |lane|
if is_ci?
delete_keychain(name: ENV["MATCH_KEYCHAIN_NAME"])
end
end
end
Github Actions Secrets
| 名前 | 内容 | 説明 |
|---|---|---|
| UNITY_EMAIL | Unity アカウント のメールアドレス |
game-ci/unity-builder で必要 |
| UNITY_PASSWORD | Unity アカウント のパスワード |
game-ci/unity-builder で必要 |
| UNITY_LICENSE | Unity ライセンス ファイル | game-ci/unity-builder で必要 *Personal は注釈あり |
| APPLE_IDENTIFIER | アプリの Bundle Identifier | 最初に決めないと 動けないやつ |
| APPLE_TEAM_NAME | Apple Developer でのチーム名 |
個人の場合は 名前になっているはず |
| APPLE_TEAM_ID | Apple Developer のチーム ID |
Apple Developer のチーム ID |
| APP_STORE_CONNECT_API_KEY_ID | API Key ID | TestFlight アップロード用 |
| APP_STORE_CONNECT_API_ISSUER_ID | Issuer Key ID | TestFlight アップロード用 |
| APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64 | p8 を base64 エンコードしたもの | TestFlight アップロード用 |
| MATCH_GIT_SSH_KEY | match リポジトリへの SSH 秘密鍵 |
リポジトリ専用 SSH(Deploy keys) がお勧め |
| MATCH_PASSWORD | match リポジトリ のパスワード |
match 構築時に設定 |
| MATCH_KEYCHAIN_NAME | 一時的に作成する キーチェーンの名前 |
好きな名前で OK |
| MATCH_KEYCHAIN_PASSWORD | 一時的に作成する キーチェーンのパスワード |
好きなパスワードで OK |
名前は
- UNITY_EMAIL
- UNITY_PASSWORD
- UNITY_LICENSE
以外は 好きな名前で良いはずです
(上記 3 つは 固定名 で要求される)
ローカルビルド用シェル(一応)
実はローカルビルドだと Development 署名も出来る様にしていますが
Fastfile からは lane を消してます(混乱させない様に)
#!/bin/bash
# 自身の使用している Unity へのパス
UNITY_EDITOR_PATH="/Applications/Unity/Hub/Editor/6000.2.7f2/Unity.app/Contents/MacOS/Unity"
UNITY_PROJECT_PATH=$(pwd)
FASTLANE_CONFIG_PATH="${UNITY_PROJECT_PATH}/ConfigFastlane"
LOG_FILE="${UNITY_PROJECT_PATH}/unity_build_ios.log"
# Script でパスを指定してる場合は合わせるなりセットするなりする
# (Github Actions と合ってないのは気にしない)
BUILD_PATH="${UNITY_PROJECT_PATH}/Build/iOS"
# シェルの使い方
# 第一引数で dev or app を選び、第二引数にビルド番号を指定できる(オプション)
if [ $# -eq 0 ]; then
echo "Error: No arguments specified."
echo "Example:"
echo " build-ios.sh dev"
echo " build-ios.sh app [Build Number]"
exit 1
fi
# 第1引数をモードとして取得 (dev or app)
MODE=$1
# スクリプトが失敗した時点で処理を中断する設定
set -e
echo "----- Starting Unity Build iOS -----"
# 既存のビルドフォルダを削除してクリーンな状態にする
rm -rf $BUILD_PATH
# ログファイルをクリア
rm -f $LOG_FILE
# Unityをバッチモードで実行
# BatchModeBuilder.BuildIos が私のビルドメソッド
$UNITY_EDITOR_PATH -quit -batchmode -projectPath $UNITY_PROJECT_PATH -executeMethod BatchModeBuilder.BuildIos -logFile $LOG_FILE
if [ $? = 0 ]; then
echo "Unity Build iOS Succeeded."
else
echo "Unity Build iOS Failed. Check log file: $LOG_FILE"
exit 1
fi
echo "----- fastlane configuration -----"
# fastlane用のフォルダを作成し、設定ファイルをコピーする
mkdir -p "${BUILD_PATH}/fastlane"
cp "${FASTLANE_CONFIG_PATH}/Gemfile" "${BUILD_PATH}"
cp "${FASTLANE_CONFIG_PATH}/Gemfile.lock" "${BUILD_PATH}"
cp "${FASTLANE_CONFIG_PATH}/Fastfile" "${BUILD_PATH}/fastlane/"
cp "${FASTLANE_CONFIG_PATH}/Appfile" "${BUILD_PATH}/fastlane/"
cp "${FASTLANE_CONFIG_PATH}/Matchfile" "${BUILD_PATH}/fastlane/"
echo "Copied configuration files."
echo "----- Starting fastlane Build -----"
# Xcodeプロジェクトのディレクトリに移動
cd $BUILD_PATH
if [ "$MODE" == "dev" ]; then
echo "Building for Development..."
fastlane build_dev
elif [ "$MODE" == "app" ]; then
# betaモードの場合、第2引数(ビルド番号)が必須
if [ -z "$2" ]; then
echo "Uploading to TestFlight without a specified build number."
fastlane local_build_and_upload
else
BUILD_NUMBER=$2
echo "Uploading to TestFlight with build number: $BUILD_NUMBER"
fastlane local_build_and_upload build_number:$BUILD_NUMBER
fi
else
echo "Error: An invalid mode was specified: $MODE"
echo "Available modes: dev, app"
exit 1
fi
# 元のディレクトリに戻る
cd $UNITY_PROJECT_PATH
echo "All build processes completed successfully!"
一度ローカルビルドを成功させる必要がある
Fastfile や Gemfile.lock などが存在前提なので
お分かりだと思いますが、一度ローカルでビルドを成功させて
それらを用意する必要があります
Homebrew
Mac なら とりあえず入れとけなんじゃないでしょうか
rbenv や fastlane は Homebrew でインストールするのがお勧めです
この辺りの環境構築は、今回は割愛してさらっといきます
(0 から始めるととんでもなく長くなるので、、)
rbenv
Mac で Unity ビルドする時に
OS に初期インストールされている Ruby で上手く行った覚えはないんですよね
今回は rbenv で 3.3.9 (3.3 系)を指定しています
因みに Local 指定で自分の Unity プロジェクト だけにしています
3.3.9 にした理由は
3.4 系だと Github Actions の環境でエラーが出たからです
下記は今も出ている警告
vendor/bundle/ruby/3.3.0/gems/highline-2.0.3/lib/highline/import.rb:10: warning: abbrev was loaded from the standard library, but will no longer be part of the default gems starting from Ruby 3.4.0.
You can add abbrev to your Gemfile or gemspec to silence this warning.
まあ Gemfile と Gemfile.lock に abbrev の対応をすれば良いみたいなんですが
Gemfile と Gemfile.lock を作った後だったので 3.3.9 にすることで回避した感じです
CocoaPods
残念ながら、現状の私のプロジェクトの状態はゲーム部分しかできておらず
ライブラリなど入っていないので CocoaPods に関してはスルーです
Xcode プロジェクトも Unity-iPhone.xcodeproj です
Unity-iPhone.xcworkspace になる方は CocoaPods を使用していると思いますが
その辺りの処理は、私の記述からご自身で改変して頑張って下さい
CocoaPods のインストール設定や Fastfile の指定など
で行けると思われます
このあたりより コード署名に係る部分が嵌り所だと思いますので
私の情報は そちらを重視して頂ければと考えています
Gemfile & Gemfile.lock
事前に ローカル Mac 環境の Xcode プロジェクトがあるディレクトリに
Gemfile を配置
bundle install
を行い Gemfile.lock を作成しておきます
因みに Gemfile は本当に Fastlane のためだけです
source "https://rubygems.org"
gem "fastlane"
Fastlane
最初は Github Actions の方で、
コード署名や Xcode での ipa アーカイブなど行おうと思っていたのですが
ローカル Mac で Fastlane を使っていたので
ビルド動作を別々にする事はないと思い統一して使用する事にしました
一番最初のローカルビルド時は、
Xcode プロジェクトが出力されたディレクトリで
fastlane init
として、対話形式で Fastfile や Appfile などを作成します
これで fastlane ディレクトリが作成されて
Fastfile など fastlane 用のファイルが作成されます
これを特定のディレクトリに保存しておいて
毎回 コピーして使う様にします
今回は Fastfile は提示しているので
適当に作成しても 私の Fastfile をコピーして使って頂けば良いかと
Match
Fastlane を使おうと思った一番の理由です
iOS ビルドにおけるコード署名と、
それらの p12 ファイルとプロビジョニング プロファイルの管理を
Match に任せてしまう感じです
つまり ローカル Mac で Match を使い
p12 ファイルとプロビジョニング プロファイル用の Github リポジトリの作成
(途中のリポジトリのパスワード設定)まで作成しておく必要があります
この辺りもさらっと流します
(ご自身で調べて下さい、Free の AI でも十分教えてくれます)
fastlane match init
で Matchfile を作成して、
自身の Github の署名用のデータのリポジトリを設定します
(因みに私は、development の署名用データも追加しています)
Match リポジトリの SSH Key
私も初めて知ったんですが
自身の Github アカウントに SSH を設定するのは普通だと思いますが
リポジトリ専用の SSH があったんですね
- Deploy keys
リポジトリの Settings から Deploy keys という項目です
Github Actions では、いくら Secrets への設定といっても
アカウント全体で有効な SSH の秘密鍵を登録するのは避けた方が良いですしね
.zprofile(ローカルビルド用)
これは あくまで ローカルビルドを成功させるための
各種ツールやライブラリの設定で記述が必要になるってだけです
rbenv や match をセットアップしていけば
自ずと記入する必要が出てくるはずです
Apple Developer Program
Github Actions の Fastlane で
TestFlight に ipa をアップロードするのに必要な
p8 (App Store Connect への Auth API Key)ファイルを作成する必要があります
この辺りも作成方法は割愛します
(ご自身で調べて下さい、こちらも Free の AI で十分教えてくれるはずです)
p8 ファイルは一度しか DL 出来ないみたいなので少し注意が必要です
具体的な使い方は
p8 ファイルは SSH の秘密鍵の様に 複数行で構成されるテキスト内容なので
base64 エンコードをかけて Github Secrets に登録します
Mac なら簡単です
base64 -i <入力p8ファイル> -o <出力base64化ファイル>
とすれば良いだけです
後は出力したファイルをテキストエディターなどで開いて中身をコピーすれば OK
(一行のテキスト情報になっているはず)
Github Actions 解説
はじめに何故ローカルビルドの話をしたかというと
Gemfile.lock や Fastfile や Matchfile を作る目的もあったのですが
ビルドを併用する目的もあります
Android 編でも説明していますが
Github Actions の Free 枠の Mac ランナーの使用時間は 200 分 (/月)だけなんですね
これ 例えば 現状の私のプロジェクト レベルで言うと
タイトルだけ、しかも適当にしか出来てない様な、ほぼ空の Unity プロジェクトでも
Github Actions の実行時間は 20 分 ほどかかっています
つまり 月に 10 回(200 / 20)ほどしか実行出来ない算段です、、
(Unity プロジェクトが作り込まれれば 順次 ビルド時間は増大するでしょう)
なので、私のプランとしては
Mac でビルドが許容できない時だけ Github Actions を実行して
後は Mac でビルドを行う、としています
(まあ、Github Actions で CI / CD の入門を、という側面も強いですが)
*自分の Mac を Github Actions 用の
セルフ ホスト ランナー にする気はありませんでした
(ローカル ビルドで十分かなと)
YAML
Github Actions の YAML を少しづつ解説していきます
まずは先頭部分
name: Build Unity for iOS
on:
workflow_dispatch:
jobs:
build_ios:
name: Build and Upload iOS
runs-on: macos-latest
env:
# unity-builder がデフォルトで build/{プラットフォーム} に出力 + Unity が iOS に出力する
BUILD_LOCAL_PATH: 'build/iOS/iOS'
name: で YAML 全体の名前をつけて
on: で動作を設定します
今回は、Actions タブ内から 手動で動作させたかったので
workflow_dispatch: としています
ここを色々と変えれば、
コミットをトリガーにとか、オプション項目を選んでとか出来ます
次に、ワークフローのジョブとして
jobs: で build_ios: と ID を設定します
(これは任意の名前で良い)
name: で色々な箇所に名前をつけられるの
細かく name: は設定した方が良いです
理由は、ワークフローが実行されると
この name: でログのタイトルが出るので追いやすくなります
runs-on: で最新(Actions)の MacOS を指定しています
ここは、特定の MacOS がいいなら、それらを設定できます
(例によって調べてね)
env: で環境変数を設定します
env: はステップの各処理毎に設定できるので
ここでは全体で使う BUILD_LOCAL_PATH だけ設定しています
BUILD_LOCAL_PATH
ここが最初に大嵌りした内容です
まず前提として GameCI の
Github Actions 用のカスタムアクションである unity-builder を使います
更に ビルド用の static メソッドは用意せずに
通常の Unity のビルドを行う様にしました
つまり ビルドの出力パスを指定しませんでした
(これが嵌った最大の理由かも、、)
unity-builder のドキュメント
buildsPath の説明
buildsPath
Path where the builds should be stored.
In this folder a folder will be created for every targetPlatform.
required: false default: build
つまり
build/iOS
に Xcode のプロジェクトが出力されると思っていました
でも 何故か上手く行かない
エラーが Xcode プロジェクトが見つからないって言うんですね、、
で、エラーログを追っていて気が付いたんですが
build/iOS/iOS
に出力してるんです
つまり unity-builder のデフォルト出力パスの後に
Unity が iOS ってディレクトリを作ってたんです
これ気が付くまで、Mac ランナーの時間をかなり消費しましたw
ビルド用の static メソッド用意して
ビルドパスを ./ (ルート)にしても良かったんですが
今回は同じ様に嵌る人もいるかと考え、あえてこのままで対応しました
リポジトリの checkout ~ Xcode バージョン指定
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
lfs: true
# キャッシュを設定(ビルド時間短縮のため)
- name: Cache Unity Library
uses: actions/cache@v4
with:
path: Library
key: Library-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }}
restore-keys: Library-
- name: Select Xcode version
id: select_xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest-stable
ここから steps: の記述になっていきます
actions/checkou
まずは Mac ランナーにリポジトリを checkout させるのに
actions/checkout を使います
v4 などのバージョン指定は
私が YAML を作成した際に ドキュメントサイト に載っていた値になります
(以降 全て)
LFS を使用しているので、そちらも指定します
actions/cache
次に、ここが良く分かってなく 申し訳ないのですが
ビルド時間を短縮するためのキャッシュ設定となるんですが、、
これ WEB とかで検索して、ほぼコピペです
使用する理由としては、ビルド時間を短くして
Mac ランナーの使用時間を少しでも短くする らしいのですが、、
個人的は Unity ビルドでのキャッシュはいい思い出がないので
無い方がいい感じなんですよね、、
(Reimport 覚悟で Library ディレクトリを消すって一度はやってません?w)
maxim-lobanov/setup-xcode
最後は Xcode のバージョン指定です
実は、この環境を構築しているときに
自分の MacOS を Tahoe にして Xcode も 26 に上げたんですね
なので MacOS も Xcode も latest ってしてるだけなんです
もし ご自身の環境で バージョン指定が必要な方は 適宜 変えて下さい
Unity ビルド
- name: Build Xcode project with Unity
uses: game-ci/unity-builder@v4
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
targetPlatform: iOS
game-ci/unity-builder
GameCI の unity-builder を使って iOS ビルドを行います
重要なのは Unity の ライセンス認証 です
重要というより 罠 に近いですw
Unity 歴が長い方は ご存じだと思いますが
以前は Unity のライセンス形態を問わず(Personal でも)シリアルキー と言うのが発行されて
1 シリアルキーで 2 環境(PC) まで認証可能だったんです
ライセンス ファイル
通常は ネットワークでシリアルキーを送る事でアクティベート出来るんですが
それが上手く行かない状況用に マニュアル アクティベート と言うのかあって
Unity のサイトで シリアルキーを使用して、ライセンス ファイル なる物を作成し
その ライセンス ファイルをローカルで使用して アクティベートを行う、というものです
Unity Personal はシリアルキーが無くなった
GameCI の unity-builder は、その ライセンス ファイル を利用して
Unity のアクティベートを行う仕組みになっているのですが、、
なんと!
2025年09月 の時点で Unity は Personal アカウントでは
シリアルキーを廃止して
Unity Hub 起動時のアクティベートに切り替えているみたいなんです
つまり
GameCI の unity-builder の Unity ライセンス ファイルが手に入らない!
より詳細に
具体的には、マニュアル アクティベート フローにおいて
最初に ライセンス リクエスト ファイル(拡張子 .alf)を作成するんですが
これは 作れます(Unity Hub で作れます)
次に、この ライセンス リクエスト ファイル(拡張子 .alf)を使用して
Unity の サイトで ライセンス ファイル(拡張子 .ulf)を作成するのですが
サイトの UI で シリアルキー の入力を求められるんです
ここに 以前の Personal アカウント時のシリアルキーを入力しても
エラーになって弾かるんです
*因みに Pro より上の契約では、いままで通りシリアルキーは発行されてます
GameCI 公式が対応
ここで困って考えたんですが
- Unity の正式(公式)の挙動である
- GameCI だって有名なコミュニティー&機能
これ 放置されてる状況のわけないだろ! って事で
GameCI 側や Stack Overflow とかで議論ぐらいされていると踏んで調べたら
GameCI のサイトに説明がありました
Unity no longer supports manual activation of Personal licenses
詳しくは、上記の GameCI サイトの注意書きを読んで欲しいのですが
Unity のサイトで ライセンス ファイル(拡張子 .ulf)を使い
ライセンス ファイル(拡張子 .ulf)を作る時に
シリアルキーの入力を求められる UI になったら
ブラウザの開発者モードで HTML を表示して 一部を書き換えると
シリアルキーの入力なしで ライセンス ファイル(拡張子 .ulf) 作成のボタンが押せる
って感じです
これ 良いんかいな、、と思わなくもない手法ですが、、
GameCI が公式サイトに載せてますし、、と 心を Void にして進みます
ライセンス認証に必要な情報
| 環境変数名 | 情報 |
|---|---|
| UNITY_EMAIL | Unity アカウント の メールアドレス |
| UNITY_PASSWORD | Unity アカウント の パスワード |
| UNITY_LICENSE | Unity ライセンス ファイル(拡張子 .ulf) |
これらを Github Actions の Mac ランナーに
特定の名前の環境変数(env:)としてセットします
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
で、ここで出てくるのは、secrets. って記述です
これは Github リポジトリの機能で
ここに Actions 用の情報を予めセットしておけるやつです
${{ secrets.xxxxx }} は
YAML で secrets. の値を使用する時の定番の書き方見たいです
Secrets
Github のアカウントではなく、リポジトリの Settings 内に
Secrets and variables のプルダウン項目があり
その中に Actions があるので選択します
New repository secret ボタンを押して登録出来ます
今回は 環境変数名と合わせて(同じ名前)登録しています
使用する Unity のバージョン
今回は 指定していませんので
Unity プロジェクト内にある
ProjectSettings/ProjectVersion.txt
のバージョンが使用されます
もし指定したい場合は unityVersion: で指定します
# Unity バージョン指定の例
uses: game-ci/unity-builder@v4
with:
targetPlatform: iOS
unityVersion: 6000.2.7f2
fastlane 用の準備
- name: Copy fastlane configuration
env:
BUILD_FULL_PATH: ${{ format('{0}/{1}', github.workspace, env.BUILD_LOCAL_PATH) }}
run: |
mkdir -p ${{ env.BUILD_FULL_PATH }}/fastlane
cp ConfigFastlane/Gemfile ${{ env.BUILD_FULL_PATH }}
cp ConfigFastlane/Gemfile.lock ${{ env.BUILD_FULL_PATH }}
cp ConfigFastlane/Fastfile ${{ env.BUILD_FULL_PATH }}/fastlane/
cp ConfigFastlane/Appfile ${{ env.BUILD_FULL_PATH }}/fastlane/
cp ConfigFastlane/Matchfile ${{ env.BUILD_FULL_PATH }}/fastlane/
- name: Setup SSH key for match
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.MATCH_GIT_SSH_KEY }}
- name: Set up Ruby and Bundler
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.3.9'
bundler-cache: true
working-directory: ${{ env.BUILD_LOCAL_PATH }}
fastlane 用のファイル群をコピーする
| ファイル名 | コピー場所 | 作成方法 |
|---|---|---|
| Gemfile | Xcode プロジェクトと同じディレクトリ | 手動で作成 |
| Gemfile.lock | Xcode プロジェクトと同じディレクトリ | 事前に bundle install |
| Fastfile | Xcode プロジェクトのディレクトリに fastlane ディレクトリを作成 |
事前に fastlane init |
| Appfile | Xcode プロジェクトのディレクトリに fastlane ディレクトリを作成 |
事前に fastlane init |
| Matchfile | Xcode プロジェクトのディレクトリに fastlane ディレクトリを作成 |
事前に fastlane match init |
私の場合は、事前にリポジトリのルートに
ConfigFastlane ディレクトリを作成して
その中に 上記 5 ファイルを置いてあります
それを 絶対パス 指定で処理していきます
(github.workspace で取得できます)
まず Gemfile と Gemfile.lock をコピー
次に mkdir -p コマンドで fastlane ディレクトリを作成して
Fastfile / Appfile / Matchfile をコピーします
webfactory/ssh-agent
これは match リポジトリ専用の SSH 設定 Deploy keys で登録した
SSH 公開鍵と対になる 秘密鍵 を Secrets に登録して指定します
(この 秘密鍵 は そのまま登録で OK)
*基本的に base64 エンコードしている情報は
Secrets の名前に _BASE64 と付けているので参考にして下さい
fastlane で match を実行した時にエラーが出たので設定しています
ruby/setup-ruby
Ruby (fastlane用)のバージョンを指定します
基本は、自身のローカルビルドを行った環境と
同じバージョンにするとトラブルは少ないはずです
with:
bundler-cache: true
Gemfile と Gemfile.lock を用意しているので
bundler-cache: true で bundle install コマンドを省略できます
(毎回 インストールしないで済む)
with:
working-directory: ${{ env.BUILD_LOCAL_PATH }}
working-directory: は Gemfile.lock が置かれている場所を指定します
fastlane 実行
- name: Run fastlane to build and upload
uses: maierj/fastlane-action@v3.1.0
env:
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64: ${{ secrets.APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64 }}
MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
MATCH_KEYCHAIN_PASSWORD: ${{ secrets.MATCH_KEYCHAIN_PASSWORD }}
TMP_KEYCHAIN_NAME: ${{ secrets.TMP_KEYCHAIN_NAME }}
APPLE_IDENTIFIER: ${{ secrets.APPLE_IDENTIFIER }}
APPLE_TEAM_NAME: ${{ secrets.APPLE_TEAM_NAME }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
with:
lane: 'build_and_upload'
subdirectory: ${{ env.BUILD_LOCAL_PATH }}
maierj/fastlane-action
実際に fastlane を実行させます
subdirectory
Optional The relative path from the project root to the subdirectory where the fastlane folder is located.
後は Fastfile の中の説明に入っていきます
env: で設定している環境変数は、全て Fastfile で使うためのものです
なので Fastfile の解説の方で説明します
Fastfile
ここが最も手こずった箇所です
エラー解決まで トライ & エラー しまくって
月 Free 枠の 200 分 を超えてしまい
Github に課金したのは、まあ勉強代ってことでw
(この情報を公開することで、皆は Free の CI / CD 環境ライフを!)
個人的には
情報が 少ない & 古い情報しか無い事象は、
AI はハルシネーションを起こしやすいと痛感させられた
(AI の賢い使い方も大分覚えた)
default_platform(:ios)
platform :ios do
desc "Run Xcode to archive the ipa and upload to TestFlight"
lane :build_and_upload do |options|
# 基本的にビルド番号は日時より自動で作成だが(必ず番号が大きくなる)
# 引数指定も用意しておく
build_number = options[:build_number] || Time.now.strftime("%Y%m%d%H%M")
Xcode で ipa を archive して、それを TestFlight へアップロードする
lane の最初の部分です
build_number は後述する
increment_build_number(ビルド番号の指定)で使うのですが
最初は 引数( |options| )で指定したのですが、途中から 面倒になってw
省略すると 日時 を使い 絶対に数値が戻らない値を自動でセットする様にしました
create_keychain
create_keychain(
name: ENV["TMP_KEYCHAIN_NAME"],
password: ENV["MATCH_KEYCHAIN_PASSWORD"],
default_keychain: true,
unlock: true,
timeout: 1200
)
# キーチェーンの検索リストを再設定する
sh "security list-keychains -s #{ENV['TMP_KEYCHAIN_NAME']} login.keychain"
Mac で言う キーチェーン を新たに作成して、そこに match で取得した
署名用の p12 や プロビジョニングプロファイルを設定できる様にします
なんで、その様な事をするかというと
Mac ランナーに初めからあるキーチェーンのパスワードが分からなかったからです
(match が使おうとすると パスワード を聞かれて lane が止まってしまう)
AI は、仮想ランナーのデフォルトのキーチェインのパスワードは "" (空)です
なんて大嘘を平気で言いますw
ここで大いに参考になったのは
Github Actions のドキュメントです
Xcode 開発用の macOS ランナーに Apple 証明書をインストールする
基本は fastlane で出来る事は置き換えて
出来ない事は、ドキュメントのコードをパクる
方針で上手く行きました
ENV[ ] で使用している環境変数は
全て env: 経由で指定している Secrets に設定しているものです
(以降全て)
TMP_KEYCHAIN_NAME
MATCH_KEYCHAIN_PASSWORD
この 2 つは、
好きな キーチェーン名 と キーチェーンのパスワード を設定しているだけです
# キーチェーンの検索リストを再設定する
sh "security list-keychains -s #{ENV['TMP_KEYCHAIN_NAME']} login.keychain"
これは、キーチェーンを作るだけでは上手く行かず
新しく作ったキーチェーンを Mac が検索するときに最初に見つける様にする
おまじないの様なものです
(これ Github のドキュメントが無かったら一生解決していなかったw)
sh は Fastfile の中でシェルを使うコマンド?です
match
match(
type: "appstore",
app_identifier: ENV["APPLE_IDENTIFIER"],
team_name: ENV["APPLE_TEAM_NAME"],
team_id: ENV["APPLE_TEAM_ID"],
keychain_name: ENV["TMP_KEYCHAIN_NAME"],
keychain_password: ENV["MATCH_KEYCHAIN_PASSWORD"],
readonly: true
)
# キーチェーンの秘密鍵にアクセス権を付与
sh "security set-key-partition-list -S apple-tool:,apple: -k '#{ENV['MATCH_KEYCHAIN_PASSWORD']}' #{ENV['TMP_KEYCHAIN_NAME']}"
いよいよ match の解説です
fastlane を使う最大の理由でした
そして 一番 大嵌りした箇所ですw
、、と言うか match 自体に解説なんていらない簡単な物のはずです
(だからこそ使いたかった)
解説するのは match 自体を成功させるまでの設定ですね
Manual 署名
初め Automatically でなんとか動作させようと頑張ったのですが諦めました
(ローカル Mac では Automatically で署名は動作したので)
結局 マニュアル署名 で実装しています
つまり match も全て指定して動作させています
ipa の目的は、
開発中のアプリを TestFlight から配布(DL)したい
(App Store Connect に接続する)なので
プロビジョニングプロファイルの種類は Ad Hoc ではなく
App Store にする 必要があります
後は
- アプリの Bunble Identifier
- Apple Developer の チーム名(*)
- Apple Developer の チーム ID
- 先程作成した キーチェーンの名前
- 先程作成した キーチェーンのパスワード
を指定します
(それぞれ Secrets 経由で設定)
Apple Developer の チーム名
これだけ注意が必要です
現状で 個人の Apple Developer Program 契約では
ダッシュボード上を探してもチーム名という表示ありません
(法人契約だとチーム名は表示されている?)
しかし チーム名は存在するんですw
私の場合は
ローカル Mac で falstlane を動作させた時のログから見つけました
AI も 個人の Developer 契約だと、個人名になると間違わなかったです
readonly: true
match は便利過ぎて
プロビジョニングプロファイル の更新が必要な場合には
(例えば テスト端末の登録を追加した等)
自動で更新をしてくれます
しかし そこまでノー管理だとトラブルが起きやすいのかな?と思い
readonly: true を指定する事で読み取り専用にしています
# キーチェーンの秘密鍵にアクセス権を付与
sh "security set-key-partition-list -S apple-tool:,apple: -k '#{ENV['MATCH_KEYCHAIN_PASSWORD']}' #{ENV['TMP_KEYCHAIN_NAME']}"
これも Github のドキュメント様様です
コメントの通りです
これが無いと
エラー(パスワード聞かれて lane が止まってしまう)が起きて
上手く動作しないんです
update_code_signing_settings
update_code_signing_settings(
use_automatic_signing: false,
path: "Unity-iPhone.xcodeproj",
targets: ["Unity-iPhone"],
team_id: ENV["APPLE_TEAM_ID"],
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore #{ENV['APPLE_IDENTIFIER']}",
bundle_identifier: ENV["APPLE_IDENTIFIER"]
)
increment_build_number(build_number: build_number)
update_code_signing_settings で
Xcode の署名設定をマニュアル設定にします
リポジトリの Unity プロジェクト自体の設定は
ローカルビルド用に Automatically を設定しているので
ビルド後の Xcode 設定は Automatically なので 変更します
(ローカル ビルドの Automatically は変えたくなかったんですw)
ここで解説すべきは 2 つですかね
update_code_signing_settings を使う理由は
正しい(確立した)操作で Xcode プロジェクトの操作を行わないと
全て(内部の構造も)をマニュアル署名設定にしてしまい、エラーが出たからです
要は Unity-iPhone.xcodeproj だけマニュアル設定にしたい
って感じです
残りの 1 つは、profile_name: です
これも、
ローカル Mac で falstlane を動作させた時のログから引っ張ってきました
increment_build_number
これは前述したビルド番号(build_number)を指定しているだけで
特に説明はいらないはずです
gym
gym(
export_method: "app-store",
clean: true,
derived_data_path: "derived_data",
export_options: {
provisioningProfiles: { ENV["APPLE_IDENTIFIER"] => "match AppStore #{ENV['APPLE_IDENTIFIER']}" }
}
)
gym で ipa をアーカイブします
ここで真に必要なのは export_options: です
これで マニュアル署名で、どのをプロファイルを使うかを指定しないと
上手く動作しませんでした
clean: true は
Xcode の Clean Build Folder と同義です
derived_data_path: "derived_data"
は AI に教えてもらったテクニックで、Xcode が実行した際の中間ファイルなどを
"derived_data" というディレクトリに出力してね、って指定です
で、今の処理って Github Actions の Mac ランナー上で行われているフローで
一度きりの環境なはずで毎回消えるので "derived_data" も絶対に消えるので
変なキャッシュに 100% 悩まされる事はないって話(AI談)です
app_store_connect_api_key
app_store_connect_api_key(
key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
issuer_id: ENV["APP_STORE_CONNECT_API_ISSUER_ID"],
key_content: ENV["APP_STORE_CONNECT_API_PRIVATE_KEY_BASE64"],
is_key_content_base64: true
)
upload_to_testflight(skip_waiting_for_build_processing: true)
app_store_connect_api_key で
TestFlight にアクセス用の p8 ファイル を設定します
(p8 ファイルの作り方は割愛します)
通常 base64 エンコードした環境変数は
デコードして元に戻すして使用するのですが
is_key_content_base64: true というオプションがあり
base64 エンコードのまま使用できました
key_id: と issuer_id: は
p8 ファイルを作成したら App Store Connect で表示されているはずです
upload_to_testflight
実際に ipa を TestFlight へアップロードします
skip_waiting_for_build_processing: true は
非同期処理化的な指定です
つまり 実際のアップロード成功を待たずに
upload_to_testflight が成功したら終わりにします
これは Mac ランナーの使用時間も軽減出来ますし
そもそも最後の工程なんで、実際のアップロードの成否を知りたければ
Actions のログでも、
実際の TestFlight のダッシュボードでも調べようは沢山あるはずです
気になる話
それより
私が WEB で見かけて気になっているのテクニックがあり
ipa のアーカイブまでは Mac でしか出来ないですが
TestFlight へのアップロードは Mac でなくても良いって話です
つまり ipa を作成したら
一旦 保存しておき、次は Linux なりの 非 Mac ランナーで
TestFlight へアップロードするというテクニックです
(Mac ランナーの使用時間を減らすテクニック)
結論は
コスパが悪そうと判断して見送りましたw
行う作業や処理に対して
劇的に Mac ランナーの使用時間が減るか? と考えたら
エラーやトラブルの可能性と比較してコスパ悪そうと思ってしまいました
(でも気になるw)
作成したキーチェーンの削除
# 念の為 処理の最後とエラー時に作成したキーチェーンを削除
after_all do |lane|
if is_ci?
delete_keychain(name: ENV["MATCH_KEYCHAIN_NAME"])
end
end
error do |lane|
if is_ci?
delete_keychain(name: ENV["MATCH_KEYCHAIN_NAME"])
end
end
一応、念のため程度ですが Github Actions で fastlane が実行された場合のみ
(とエラー終了した場合)
キーチェーン名を指定で削除する処理を入れています
(Mac ランナーに残るとは言い切れませんが、なんちゃってセキュリティ風w)
ただ注意があり
私の場合は ローカル Mac とのビルドで併用しているので
is_ci を見て Github Actions で実行された場合のみ、という条件を入れています
いくら 名前が違うとはいえ
自身の Mac でキーチェーン削除コマンドを呼ぶのは 行うべきではありませんね
Test Flight
後は、App Store Connect 側で
Test Flight にアップした ipa の承認がされるまで 暫し待てば DL 可能になるはずです
最後に
これで Unity の iOS ビルド(ipa 作成)と TestFlight へのアップロードを
Free(無料)の環境で行う話は、一通り終わりです
思っている事は
Github Actions で Unity iOS ビルドに手を出すレベルの方なら
コピペして終わりなどという場合は少ないと思っており
それぞれが 手こずっている挙動 や エラーを解決するための 理屈 として
参考にして頂けたらと思っています
(Android 編もそうですが、結構 冗長に説明しているつもりです)
最後の最後に、どうしても Github Actions の Mac ランナーの
Free で使える 月の時間 200 分 がネックな気がします
私は前述した通り Github に課金してしまったので
会社のつもりでバンバン実行ボタンを押しそうで怖いですw
最後に書き終わって気が付いたのですが
同じ従量制課金をするなら Unity Automation Build にすれば
もっと簡単に iOS ビルド環境が手に入ったはずだなぁとw
- Unity Automation Build の iOS ビルドは初めから有料
- 専用の仕組みなので、基本の設定は ポチポチ指定で完了する(はず)


