CI, публикация и сборка под обе платформы

KMP-проект собирается под несколько ОС, и CI это диктует: что можно на Linux, а что только на macOS.

CI для KMP — конвейер, который собирает и тестирует общий модуль под все цели; сборка и тесты iOS требуют раннера на macOS.

Главное ограничение CI

Kotlin/Native под iOS компилируется только на macOS — нужны Xcode и Apple-тулчейн. Поэтому CI обычно делят: Android-сборку и общие JVM-тесты гоняют на дешёвых Linux-раннерах, а iOS-сборку и iOS-тесты — на macOS-раннерах. Это влияет на стоимость и время пайплайна.

jobs:
  android:
    runs-on: ubuntu-latest
    steps:
      - run: ./gradlew :shared:testDebugUnitTest assembleDebug
  ios:
    runs-on: macos-latest
    steps:
      - run: ./gradlew :shared:iosSimulatorArm64Test

Кэширование Gradle и Konan

Сборки KMP небыстрые: Kotlin/Native тянет зависимости тулчейна (Konan). На CI обязательно кэшируйте ~/.gradle и ~/.konan, иначе каждый запуск будет качать тулчейн заново. Это одна из самых частых причин медленных пайплайнов.

Публикация общего модуля

Если iOS-команда работает в отдельном репозитории, общий модуль публикуют как артефакт: для Android — в Maven-репозиторий (обычный AAR/JAR с метаданными KMP), для iOS — как XCFramework через Swift Package Manager или CocoaPods. Тогда iOS-разработчику не нужен Kotlin-тулчейн локально — он тянет готовый бинарник.

Как работает под капотом

При публикации Gradle с плагином Kotlin Multiplatform выкладывает несколько артефактов под общими координатами: корневой модуль с метаданными плюс по варианту на каждую цель. Когда потребитель добавляет зависимость, Gradle по метаданным выбирает нужный вариант под его платформу. Для iOS-стороны XCFramework публикуется отдельно, так как Swift Package Manager/CocoaPods не понимают Gradle-метаданные — им нужен готовый фреймворк. Поэтому iOS-публикация — это отдельный шаг пайплайна, собирающий и выкладывающий XCFramework.

Частые ошибки

Пытаться собрать iOS на Linux-раннере — не выйдет, нужен macOS. Вторая ошибка — не кэшировать Konan и тулчейн, получая многоминутные сборки. Третья — публиковать только Android-артефакт и забыть XCFramework, после чего iOS-команда не может подключить обновление. Четвёртая — не закрепить версии Xcode/Kotlin на CI, ловя «работает локально, падает на CI».

Итоги

  • iOS-сборка и тесты требуют macOS-раннеров; Android можно на Linux.
  • Кэшируйте Gradle и Konan, иначе пайплайн будет долгим.
  • Общий модуль публикуют как Maven-артефакт (Android) и XCFramework (iOS).
  • Gradle-метаданные выбирают вариант под платформу; iOS-фреймворк публикуется отдельно.
Проверьте себя
1. Почему iOS-сборку KMP нельзя гонять на Linux-раннере?
ALinux медленнее
BKotlin/Native под iOS требует macOS с Xcode и Apple-тулчейном
CLinux не поддерживает Kotlin
DЭто запрещено лицензией
2. Что критично кэшировать на CI для KMP?
AТолько исходники
BGradle и Konan-тулчейн, иначе каждая сборка качает их заново
CЛоги
DСкриншоты UI