PrivateリポジトリのSwiftPMをCIから使う

最近はモバイルアプリのリポジトリのプロジェクト整理として、これまではPrivateなPodSpecsやgit submoduleを使って解決していた依存を置き換えたり、単体での取り回しが効く程度にリポジトリを分けたりなどをしている。

iOSアプリならPackage.swift を用意して、Swift Package Managerで解決
Androidアプリなら GitHub PackagesMavenとして利用して、Gradleで解決
という方針で進めている。

CI as a Service (以下、CI) にて、PrivateリポジトリをSwift Package Managerで依存解決するのに、ちょっとてこずったので記しておく。
AndroidアプリでのGitHub Packages利用についても、あとでまとめたい。

本題

本題の回答としては、Appleドキュメントに記載があって、Xcode 12現在はこれで解決ができるようになっている。
Apple Developer Documentation

If you’re using the xcodebuild command directly, use SSH–based Git URLs for your packages and configure your SSH credentials. Set up your known_hosts file in the ~/.ssh directory of the macOS user that runs your CI tasks. xcodebuild honors your SSH configuration — there’s no additional setup required.

  • https ではなく、SSH形式のGit URLを使う
    • 例えば git@github.com:niw/KeyboardGuide.git
  • ~/.ssh/known_hostsSSHの設定を置く

この設定をしていない場合、 xcodebuild で依存解決ができずに、ビルドができない。

...
Resolving Swift Package Manager dependencies...
Command line invocation:
    /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -resolvePackageDependencies
...
Resolve Package Graph
Fetching git@github.com:user/repository.git
xcodebuild: error: Could not resolve package dependencies:
  The server SSH fingerprint failed to verify.
...

具体的な策

BitriseのWorkflowで、 Activate SSH key (RSA private key) 後に、Script stepとして、以下を実行しておくことで解決した。

for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts || true

前述の‪Apple‬ドキュメントにある通り、 ~/.ssh/known_hostsgithub.comへの接続を追加した形だ。

ここからは、この対応をする上で半日を潰した罠を書き残しておく。

1. 情報が少ない

上記のドキュメントに書いてある通りにやればいいだけ。
それだけなのだが、それにしても話題にしている人が少ない。

取り組み始めた段階で、すぐにほぼ答えの回答にたどり着いたが、なぜか設定後のビルドが失敗したので、諦めて別の方法を探していた。

discuss.bitrise.io

Circle CIのヘルプにも、同じ話題が上がっていた。

support.circleci.com

しかし、 IDEPackageSupportUseBuiltinSCM について調べても、情報が少ない。
ヘルプの記載と名前から推測すると、 xcodebuild でシステムのGitを参照させるためのオプションだろうか。

xcodebuild does not conform to the system ssh config and does not access the keys CircleCI stores in ssh-agent. We can work around this by requesting Xcode uses the main system version of ssh which will behave as expected.

ちなみに、Xcode 12では解決しているのか、 IDEPackageSupportUseBuiltinSCM YES にせずに、通るようになった。

2. SSH公開鍵/秘密鍵の用意

普段GitHubと連携する前提だと、多くのことがGitHubPersonal Access Token で解決できるのだけど、SwiftPMの場合にはGitそれ自体のサポートのため、SSHによる認証が採用されている。
幸いに、今回の環境ではBitriseでCocoaPodsのPrivate PodSpecsを使うために、他リポジトリにもアクセス権があるSSH鍵を設定していたので、この構成を調整する必要はなかった。
BitriseにAppを追加するフローで設定できるSSH鍵のままでは、リポジトリへの権限問題で、認証を通過できないかもしれない。

また、GitHub Actionsなどの他CIでは、解決方法が異なるかもしれない。

3. Bitrise Workflowの同時編集

これはSwiftPMと全く関係ない話題 だが、BitriseのWorkflowを複数人で編集すると、上書きしあう可能性がある。
自分が本題で詰まっていたため、途中からチームメンバーにも検証を手伝ってもらっていたが、どうにも自分が書いた設定が巻き戻っていることがあった。
それぞれ別々にWorkflowを用意して作業していたつもりであったが、おそらく他の人がWorkflowエディタを開いた段階の設定が、自分の変更を上書きして、巻き戻りが発生していたのだと思う。
やってみれば、 bitrise.yml は1枚なので、そうなると理解できるが、できればdiffだけを保存してもらいたい...

まとめ

以上、紆余曲折あって半日溶かしたものの、結局はAppleドキュメントにある通りに、パッケージをSSH形式のURL指定し、 ~/.ssh/known_hostsSSH設定を追加する

for ip in $(dig @8.8.8.8 github.com +short); do ssh-keyscan github.com,$ip; ssh-keyscan $ip; done 2>/dev/null >> ~/.ssh/known_hosts || true

で解決できた。
1日お疲れ様でした。