KINTO Tech Blog
CI/CD

[iOS][CI/CD] Xcode Cloudでプライベートリポジトリをライブラリとして取り込む

Cover Image for [iOS][CI/CD] Xcode Cloudでプライベートリポジトリをライブラリとして取り込む

はじめに

こんにちは。モバイル開発のヒロヤ(@___TRAsh)です。

弊社ではいくつもの内製開発プロダクトがありますが、多くのプロダクトでXcode Cloudを採用しています。
Xcode CloudはAppleが公式に提供しているCI/CDサービスで、iOSアプリのビルドやCD(TestFlightへのデプロイ)を自動化できます。

今回は、Xcode Cloudでプライベートリポジトリをライブラリとして取り込む方法についてあまり参考資料が無く、ビルドを通すのに苦労したので、調査した結果をこちらにまとめておきます。

対象

今回の内容はiOS環境のCI/CDの話になるので、ある程度iOS開発の知識がある方を対象としています。

環境

- Xcode 15.4
- SwiftPMでライブラリを管理してる
- 参照しているライブラリにプライベートリポジトリがある
- GitHub Actions + FastlaneでTestFlightへデプロイしてる

やりたいこと

TestFlightのデプロイをGitHub Actions + FastlaneからXcode Cloudに移行したいと考えています。
これをすることで、Fastlaneへの依存をなくすことができ、App申請までのフローに必要なツールを減らすことができます。
また、申請に必要な証明書の管理もXcode Cloud上で直接Apple Developerの証明書を参照してくれるので、証明書の管理も楽になります。

悩み

ここまで利点しかないXcode Cloudですが、ライブラリとしてプライベートリポジトリを参照する際には、ユーザー認証が必要になります。Xcode Cloudではそういった認証設定が考慮されていないため、プライベートリポジトリをライブラリとして参照するには一工夫する必要があります。
そこでXcode Cloudから提供されている ci_scripts/ci_post_clone.sh を活用して認証の設定することで、プライベートリポジトリを参照できるようになります。

.netrcの設定

Xcodeには12.5の頃から.netrcを参照する機能が追加されています。
.netrcはユーザー名とパスワードを記述したファイルで、~/.netrcに配置することで、git clone時に認証情報を自動で入力してくれます。
また、今回はライブラリをプライベートリポジトリのGitHub Releaseで管理する方法をとっているので、 api.github.com も追加しています。

ci_post_clone.sh
touch ~/.netrc
echo "machine github.com login $GITHUB_USER password $GITHUB_ACCESS_TOKEN" >> ~/.netrc
echo "machine api.github.com login $GITHUB_USER password $GITHUB_ACCESS_TOKEN" >> ~/.netrc

ユーザー名とアクセストークンをXcode Cloudの環境変数にSecretで設定しておき、ci_post_clone.sh で参照するようにしています。

追加のリポジトリにURL追加

App Store Connect内のXcode Cloud の設定にある 追加のリポジトリ にライブラリのリポジトリURLを追加します。

defaults delete で設定を削除する

上記の設定でプライベートリポジトリのライブラリを取得できる様になってもまだライブラリの依存関係を解決できず、以下の様なエラーに遭遇しました。

このエラーはXcode Cloud上でSwiftPMが、Package.resolvedを参照せず、自動でパッケージのバージョンを解決しようとしていることが原因です。
それぞれ、Xcodeのdefaultsを削除するとうまくビルドが通る様になります。

ci_post_clone.sh
defaults delete com.apple.dt.Xcode IDEPackageOnlyUseVersionsFromResolvedFile
defaults delete com.apple.dt.Xcode IDEDisableAutomaticPackageResolution

この2つの設定なんですが実は違いが明確にわからなかったです...
ローカルで xcodebuild のhelpコマンドを叩いて見てみると、似た様な設定があるのでそちらを参考にしても

$ xcodebuild -help
...

-disableAutomaticPackageResolution        prevents packages from automatically being resolved to versions other than those recorded in the `Package.resolved` file
-onlyUsePackageVersionsFromResolvedFile   prevents packages from automatically being resolved to versions other than those recorded in the `Package.resolved` file

Package.resolvedファイルに記録されているバージョン以外に、パッケージが自動的に解決されるのを防ぎます。

となっているので全く同じ内容しか書かれてないんですよね...
一応SwiftPMのIssueでも同じ様な内容の質問があって、この対応で解決しているので、現状はこれで問題ないと思います。
https://github.com/swiftlang/swift-package-manager/issues/6914

ひとまず、この2つの設定を削除することで、SwiftPMが参照するライブラリの依存関係をPackage.resolvedのみ参照し、依存関係を解決してくれる様になります。

結論

Xcode Cloudで起動前に参照される ci_scripts/ci_post_clone.sh.netrc を設定することで、プライベートリポジトリを参照できるようになり、ライブラリの依存関係を解決するために、defaults delete を設定することで、Xcode Cloud上でのビルドが通るようになりました。

ci_post_clone.sh
#!/bin/sh

defaults delete com.apple.dt.Xcode IDEPackageOnlyUseVersionsFromResolvedFile
defaults delete com.apple.dt.Xcode IDEDisableAutomaticPackageResolution

touch ~/.netrc
echo "machine github.com login $GITHUB_USER password $GITHUB_ACCESS_TOKEN" >> ~/.netrc
echo "machine api.github.com login $GITHUB_USER password $GITHUB_ACCESS_TOKEN" >> ~/.netrc

最後に

Fastlaneは古くからある偉大なツールですが、Xcode Cloudの利用により、App申請までのフローをシンプルにできました。
先にも述べましたが、Xcode Cloudを使うことによるメリットは多いので、ぜひ導入を検討してみてください。

Appendix

https://developer.apple.com/documentation/xcode/writing-custom-build-scripts
https://speakerdeck.com/ryunen344/swiftpm-with-kmmwoprivatenagithub-releasedeyun-yong-suru
https://qiita.com/tichise/items/87ff3f7c02d33d8c7370
https://github.com/swiftlang/swift-package-manager/issues/6914

Facebook

関連記事 | Related Posts

We are hiring!

【iOS/Androidエンジニア】モバイルアプリ開発G/東京

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。

【iOSエンジニア】モバイルアプリ開発G/大阪

モバイルアプリ開発GについてKINTOテクノロジーズにおける、モバイルアプリ開発のスペシャリストが集まっているグループです。KINTOやmy routeなどのサービスを開発・運用しているグループと協調しながら品質の高いモバイルアプリを開発し、サービスの発展に貢献する事を目標としています。