開発中のXcodeプロジェクトは、署名がAutomatic(自動署名)で、Apple Developmentの証明書を使うのが一般的である。配布用のアプリは、Developer ID Applicationの証明書で署名する。ローカルの設定は変えずに、CIでだけDeveloper IDの署名に切り替えたい。

切り替えの方法には、project.pbxprojをsedで書き換える方法と、xcodebuildの引数で上書きする方法がある。引数で上書きする方法であれば、ファイルを書き換えずに済む。

- name: Archive
  env:
    TEAM_ID: ${{ secrets.TEAM_ID }}
  run: |
    xcodebuild -project [プロジェクト名].xcodeproj -scheme [スキーム名] -configuration Release \
      -archivePath build/[アプリ名].xcarchive \
      CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY="Developer ID Application" DEVELOPMENT_TEAM="$TEAM_ID" archive

実行例は、Xcode 27.0のランナー(runs-on: xcode-27)のもの。証明書は、事前にキーチェーンへインポートしておく。

ビルド設定を上書きする方法

xcodebuildは、コマンドラインの末尾に[ビルド設定]=[値]と書くと、ビルド設定を上書きする。project.pbxprojの内容よりも、コマンドラインの指定が優先される。

ビルド設定値意味
CODE_SIGN_STYLEManual署名の方法を手動にする
CODE_SIGN_IDENTITYDeveloper ID Application署名に使う証明書
DEVELOPMENT_TEAMTeam ID署名するTeam

この方法でアーカイブを作ると、アーカイブの中のアプリが、Developer IDの証明書で署名される。ワークフローのログには、実行されたxcodebuildのコマンドが表示される。Secretsの値は、***に置き換えられる。

/Applications/Xcode_27.app/Contents/Developer/usr/bin/xcodebuild -project [プロジェクト名].xcodeproj -scheme [スキーム名] -configuration Release -archivePath build/[アプリ名].xcarchive CODE_SIGN_STYLE=Manual "CODE_SIGN_IDENTITY=Developer ID Application" DEVELOPMENT_TEAM=*** archive

アーカイブの署名を確認する。

- name: Show signature
  run: codesign -dv --verbose=2 build/[アプリ名].xcarchive/Products/Applications/[アプリ名].app 2>&1 | grep -E 'Authority|flags|Timestamp'
CodeDirectory v=20500 size=488 flags=0x10000(runtime) hashes=4+7 location=embedded
Authority=Developer ID Application: [名前] (***)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=[署名した日時]

flags=0x10000(runtime)は、Hardened Runtimeが有効であることを表す。Hardened Runtimeは、Xcodeのプロジェクトで有効にしておく。

引数で上書きしても、ワーキングツリーは変更されない。git status --shortの結果は、空である。

$ git status --short
$

ローカルでは、Automatic署名のまま、通常どおりビルドできる。

pbxprojをsedで書き換える方法

project.pbxprojを、sedとperlで書き換える方法もある。署名の方法をManualに変え、CODE_SIGN_IDENTITYを追加する。

- name: Rewrite project.pbxproj
  run: |
    sed -i '' 's/CODE_SIGN_STYLE = Automatic;/CODE_SIGN_STYLE = Manual;/' [プロジェクト名].xcodeproj/project.pbxproj
    perl -i -pe 's/(CODE_SIGN_STYLE = Manual;)/$1\n\t\t\t\tCODE_SIGN_IDENTITY = "Developer ID Application";/' [プロジェクト名].xcodeproj/project.pbxproj
    git diff --stat

macOSのsedは、BSD版である。-iには、バックアップの拡張子を渡す必要があり、バックアップを作らないときは''を渡す。GNU版のsedとは、書き方が異なる。

参考: 【Mac】GNUのsedを使う

この方法でも、アーカイブはDeveloper IDの署名になる。署名の結果は、引数で上書きした場合と同じである。

CodeDirectory v=20500 size=488 flags=0x10000(runtime) hashes=4+7 location=embedded
Authority=Developer ID Application: [名前] ([Team ID])
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=[署名した日時]

sedは意図しない箇所も書き換える

git diff --statで、書き換えの範囲を確認すると、project.pbxprojが12行追加、6行削除になる。

 [プロジェクト名].xcodeproj/project.pbxproj | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

CODE_SIGN_STYLE = Automatic;は、アプリのターゲットだけでなく、テストのターゲットを含む6か所に存在する。sedは、すべての箇所を書き換える。アプリのターゲットだけに絞りたい場合は、sedの指定が複雑になる。

sedは一致しなくても成功する

sedは、置換する文字列が見つからなくても、終了コードが0である。プロジェクトの設定が変わり、置換のパターンに一致しなくなっても、ワークフローは成功する。

$ sed -i '' 's/DOES_NOT_EXIST/x/' file
$ echo $?
0

署名の設定が書き換わらないまま、ビルドが進む。署名の失敗は、後の段階で見つかる。引数で上書きする方法であれば、置換のパターンに依存しない。

ExportOptions.plistのTeam IDを埋め込む

エクスポートに使うExportOptions.plistも、Team IDなどをSecretsから渡す。リポジトリには、プレースホルダー(YOUR_TEAM_ID)で保存しておく。

<key>teamID</key>
<string>YOUR_TEAM_ID</string>

ワークフローで、プレースホルダーを置き換える。

- name: Export archive
  env:
    TEAM_ID: ${{ secrets.TEAM_ID }}
  run: |
    sed -i '' "s/YOUR_TEAM_ID/$TEAM_ID/" ExportOptions.plist
    xcodebuild -exportArchive -archivePath build/[アプリ名].xcarchive -exportPath build -exportOptionsPlist ExportOptions.plist

plistの値は、PlistBuddyでも書き換えられる。PlistBuddyは、キーが存在しない場合に、終了コード1で失敗する。

$ /usr/libexec/PlistBuddy -c "Set :teamID $TEAM_ID" ExportOptions.plist
$ /usr/libexec/PlistBuddy -c "Print :teamID" ExportOptions.plist
[Team ID]
$ /usr/libexec/PlistBuddy -c "Set :noSuchKey x" ExportOptions.plist
Set: Entry, ":noSuchKey", Does Not Exist
$ echo $?
1

置換の失敗を検出したい場合は、sedよりもPlistBuddyが適している。

どちらの方法を選ぶか

引数で上書きする方法は、ファイルを書き換えないため、ローカルの設定と、CIの設定が分離される。sedによる書き換えと違い、置換のパターンの変更や、意図しない箇所の書き換えを心配しなくてよい。

xcodebuildの引数が長くなるときは、ビルド設定を.xcconfigのファイルにまとめて、-xcconfigで渡す方法もある。