麦芽を支える技術

麦芽(ばくが、英語:malt)とは、麦、特に大麦の種子を発芽させたもので、ビール、ウイスキー、水飴の原料となる。(Wikipediaより)

BitriseでSwift Package Manager(SPM)のパッケージをキャッシュする

はじめに

自分のXcodeプロジェクトではライブラリ管理を徐々にCocoaPodsからSwift Package Manager(以下 SPM)へ移行してるんですが、CocoaPodsの時にやっていたのと同じようなBitriseのキャッシュ設定をSPM用にも入れたいなと思いちょっと調べてみました。

で、先に言ってしまうと、調べた結果こちらの記事がとても参考になった(と言うかCIサービスがCircleCIなところ以外はほとんどこれと一緒)ので、気になる方はこちらと併せてご確認ください。

uptech.team

今回やりたいこと

CocoaPodsでは、落としてきたライブラリたちが全て./Podsに入るので、BitriseのCache:Pushでは以下のようにCache Pathsに設定することで./Podfile.lockに変更が入った際に./Podsをキャッシュする、という形にしていました。

./Pods -> ./Podfile.lock

今回やりたいこととしては基本的にはこれと同じ方針で、SPM依存パッケージの変更があったら、SPM依存パッケージファイル群をキャッシュする設定をBitriseに入れようと思います。

SPMパッケージ変更の検知

SPMではパッケージをXcodeGUI上で操作して追加するので、CocoaPodsで言うところのPodfile.lockのように依存パッケージが記載されているファイルはどこにあるんだろ、と思い調べたところここに入っていました。

$ ls -l XXXX.xcworkspace/xcshareddata/swiftpm/
total 8
-rw-r--r--  1 asmz  staff  1396 10 19 17:14 Package.resolved
$

このPackage.resolvedを今回SPMパッケージ変更の検知に用います。

なお、このファイルはBitrise側で使用するため、リポジトリ管理されている必要があります。

SPMパッケージ保存場所の変更

上記のパッケージ変更検知を受け、実際にキャッシュしたいディレクトリを指定する必要があるのですが、デフォルト設定だとSPMパッケージはDerivedDataに保存されるようです。

これだと動的にディレクトリ名が変わってしまいBitriseのキャッシュStepに組み込めないため、以下のように明示的にパッケージ保存場所を指定します。

[xcodebuildの場合]

xcodebuild build ... -clonedSourcePackagesDirPath {packages_path}

[fastlane scanの場合]

run_tests(
  ...
  cloned_source_packages_path: "{packages_path}"
)

[fastlane gymの場合]

build_app(
  ...
  cloned_source_packages_path: "{packages_path}"
)

これで {packages_path} で指定した場所にSPMのパッケージ群が落ちてくる形になるので、ここをCI側でキャッシュ指定させれば良いですね。

なお余談として、自分はこれ以外にfastlane actionのxcovを使ってコードカバレッジを取得しているのですが、xcovも内部でxcodebuildコマンドを叩いているようでその際にSPM依存関係がチェックされ、存在しなければパッケージ導入処理が走ります。

今回、↑でパッケージのパスを変えた関係で、このxcovでパッケージ導入処理が走ってしまったので調べたら、xcovにも同様のcloned_source_packages_pathオプションが追加されていました。

github.com

ご利用のfastlaneコマンドで、同じようにSPMパッケージ導入が始まってしまうActionありましたら、このオプション追加されていないか確認してみると良いかもです。

Bitriseキャッシュ設定

あとはBitriseのCache:Push Stepで以下のようにCache Pathsを指定します。

{packages_path} -> ./{project_name}.xcworkspace/xcshareddata/swiftpm/Package.resolved

{packages_path}とPackage.resolvedのパスはそれぞれ先に確認した内容を入れてください。

Bitrise実行結果

以上の設定でBitriseを回してうまくCacheがPushできれば、以降Cache:PullでSPMパッケージがキャッシュから取得できるかと思います。

[キャッシュ設定前]

[00:11:01]: Resolving Swift Package Manager dependencies...
[00:11:01]: $ xcodebuild -resolvePackageDependencies -workspace ./XXXX.xcworkspace -scheme XXXX
[00:11:04]: ▸ Command line invocation:
[00:11:04]: ▸     /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -resolvePackageDependencies -workspace ./XXXX.xcworkspace -scheme XXXX
[00:11:08]: ▸ Resolve Package Graph
[00:11:08]: ▸ Fetching https://github.com/kishikawakatsumi/KeychainAccess
[00:11:10]: ▸ Fetching https://github.com/onevcat/Kingfisher
[00:11:13]: ▸ Fetching https://github.com/ishkawa/APIKit
[00:11:14]: ▸ Fetching https://github.com/facebook/facebook-ios-sdk
[00:11:19]: ▸ Fetching https://github.com/bannzai/Gedatsu
[00:11:25]: ▸ Cloning https://github.com/bannzai/Gedatsu
[00:11:25]: ▸ Checking out https://github.com/bannzai/Gedatsu at 1.2.0
[00:11:26]: ▸ Cloning https://github.com/ishkawa/APIKit
[00:11:26]: ▸ Checking out https://github.com/ishkawa/APIKit at 5.1.0
[00:11:26]: ▸ Cloning https://github.com/kishikawakatsumi/KeychainAccess
[00:11:27]: ▸ Checking out https://github.com/kishikawakatsumi/KeychainAccess at 4.2.1
[00:11:27]: ▸ Cloning https://github.com/onevcat/Kingfisher
[00:11:28]: ▸ Checking out https://github.com/onevcat/Kingfisher at 5.15.6
[00:11:28]: ▸ Cloning https://github.com/facebook/facebook-ios-sdk
[00:11:29]: ▸ Checking out https://github.com/facebook/facebook-ios-sdk at 7.1.1
[00:11:36]: ▸ Resolved source packages:
[00:11:36]: ▸   APIKit: https://github.com/ishkawa/APIKit @ 5.1.0
[00:11:36]: ▸   KeychainAccess: https://github.com/kishikawakatsumi/KeychainAccess @ 4.2.1
[00:11:36]: ▸   Kingfisher: https://github.com/onevcat/Kingfisher @ 5.15.6
[00:11:36]: ▸   Gedatsu: https://github.com/bannzai/Gedatsu @ 1.2.0
[00:11:36]: ▸   Facebook: https://github.com/facebook/facebook-ios-sdk @ 7.1.1
[00:11:36]: ▸ resolved source packages: APIKit, KeychainAccess, Kingfisher, Gedatsu, Facebook

[キャッシュ設定後]

[12:16:13]: Resolving Swift Package Manager dependencies...
[12:16:13]: $ xcodebuild -resolvePackageDependencies -workspace ./XXXX.xcworkspace -scheme XXXX -clonedSourcePackagesDirPath ./swift_packages
[12:16:17]: ▸ Command line invocation:
[12:16:17]: ▸     /Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild -resolvePackageDependencies -workspace ./XXXX.xcworkspace -scheme XXXX -clonedSourcePackagesDirPath ./swift_packages
[12:16:17]: ▸ User defaults from command line:
[12:16:17]: ▸     IDEClonedSourcePackagesDirPathOverride = /Users/vagrant/git/swift_packages
[12:16:21]: ▸ Resolve Package Graph
[12:16:25]: ▸ Resolved source packages:
[12:16:25]: ▸   Facebook: https://github.com/facebook/facebook-ios-sdk @ 7.1.1
[12:16:25]: ▸   KeychainAccess: https://github.com/kishikawakatsumi/KeychainAccess @ 4.2.1
[12:16:25]: ▸   APIKit: https://github.com/ishkawa/APIKit @ 5.1.0
[12:16:25]: ▸   Kingfisher: https://github.com/onevcat/Kingfisher @ 5.15.6
[12:16:25]: ▸   Gedatsu: https://github.com/bannzai/Gedatsu @ 1.2.0
[12:16:25]: ▸ resolved source packages: Facebook, KeychainAccess, APIKit, Kingfisher, Gedatsu

各パッケージ取得のためのGit Cloneがなくなっていることが分かります。

(しかし↑だと、もともと約35sしか処理に時間がかかっていないこともあり、約半分の約12sまで削減されても体感としてはそんなに効果を感じられませんが。。。)

おわりに

以上がBitriseでのSPMパッケージキャッシュ手順でした。

なお、参考にした元の記事ではこれをCircleCIで行っているように、このキャッシュ対象の設定自体はBitriseに寄らずどのCIサービスでも有効です。

是非お試しいただければ。