potatotips #77で「RenovateによるiOSライブラリーの自動更新」という発表をしました&いくつかの補足

発表資料はこちらです 👇

発表の中では時間の都合で触れられなかったところをいくつか補足しようと思います。

CocoaPodsでRealmを使っているとPodfile.lockが更新できない件

github.com

Renovateの実行環境はDockerコンテナの中のUbuntuです。Renovateは Podfile を書き換えた後に pod install を実行してPodfile.lock を更新します。CocoaPodsはLinuxで動くのか?と思われるかもしれませんが、pod gem(や依存の xcodeproj)もLinuxで問題なく動作するようです。ほとんどのライブラリーは問題ないのですが、Realmのpodspecには少し特殊なところがあります。

https://github.com/realm/realm-swift/blob/8c1cbf124c5025da9220595e7764059963065891/Realm.podspec#L110

  s.prepare_command         = 'sh scripts/setup-cocoapods.sh'

podspecのprepare_commandスクリプトを実行しています。そのスクリプトではrealm-coreをダウンロードしているのですが、その先頭でset -euo pipefailとしており、bashではなくshだとエラーになってしまいます。

build.sh: 13: set: Illegal option -o pipefail

さらにここをbashで実行したとしてもスクリプト内部でxcode-selectコマンドが使われているので、どちらにせよ詰んでしまいます。

ここで我々ができることは、発表のSwiftPMの箇所で「Package.resolvedの更新に非対応」でも触れた

  • PRに自分で追いコミットをするか、もしくは
  • CIで(例えばGitHub Actionsで)更新するワークフローを用意する

という感じになりそうです。

on:
  pull_request:
    paths:
      - Podfile
jobs:
  precheck:
    runs-on: ubuntu-latest
    outputs:
      files_changed: ${{ steps.file_changes.outputs.files }}
    steps:
      - uses: actions/checkout@v3
      - id: file_changes
        uses: trilom/file-changes-action@v1.2.4
  update:
    runs-on: macos-latest
    needs: precheck
    if: contains(fromJson(needs.precheck.outputs.files_changed), 'Podfile.lock') == false
    steps:
      - ...

CocoaPodsとXcodeGenを併用しているとPodfile.lockが更新できない件

XcodeGenを使用していると、通常生成したXcodeプロジェクトはリポジトリにコミットせず、CIでも初期セットアップでコマンドを実行してXcodeプロジェクトを生成すると思います。これがRenovateでは困ったことになります。Renovateの実行時に任意の初期化処理を挟むことができず(そもそも実行環境にXcodeGenも存在しない)、Xcodeプロジェクトが存在しないままだとpod installの実行に失敗してしまうためです。

これを回避する手段として、CocoaPodsの:integrate_targetsという設定を使用できます。Podfileのルートではinstall!というオプションを設定することができます。その引数に :integrate_targets => false と指定することで、Xcodeプロジェクトへの組み込みステップが省略されるため、Xcodeプロジェクトがなくても pod install を実行できるようになります。

この設定をRenovateでの実行時だけ有効にするように、以下のようにハックします。

is_running_in_renovate = ENV['HOME'] == '/home/ubuntu'
if is_running_in_renovate
  # Renovateでの実行時にはXcodeGenが実行できず、xcodeprojが存在しないので `:integrate_targets => false` とする。
  install! 'cocoapods', :integrate_targets => false
else
  install! 'cocoapods'
end

ただこの設定には注意点があり、:integrate_targets => falseだとアプリが使用しているSwiftのバージョンをXcodeプロジェクトから抜き出すことできなくなり、ライブラリー側のビルド設定のSWIFT_VERSIONが設定されなくなってしまいます。

これも回避策があり、Podfile内の各ターゲットで current_target_definition.swift_version = '5.0' と明示的に定義してしまえば問題ありません。

target 'FooBarApp' do
  # https://github.com/CocoaPods/CocoaPods/issues/8653#issuecomment-488767262
  current_target_definition.swift_version = '5.0'
  ...
end

おわりに

モバイルアプリ開発でもどんどんRenovateを活用していきたいですね!!

その他の方の発表資料はconnpassのページからどうぞ!