./gradlew на удалённом Mac через dependencyResolutionManagement в Kotlin DSL, Gradle properties, --parallel и удалённый build cache. Ниже — матрица зеркал, шаблон settings.gradle.kts, пороги workerов, ключи кеша и приёмка lockfile. См. также главная, блог, CocoaPods, SPM, Gradle/Maven.
Три узких места трансграничного pull на общем Apple Silicon:
- Репозитории без иерархии. Смешение прямого Maven Central, корпоративного Nexus и
plugins.gradle.orgв разных модулях даёт недетерминированный порядок разрешения и «плавающие» версии при частичной деградации зеркала. - Параллель × память. Сочетание
org.gradle.parallel=true, высокогоorg.gradle.workers.maxи тяжёлого Kotlin compile на одном удалённом Mac упирается в пик Metaspace и своп, что маскируется под сетевые таймауты. - Кеш без пространства имён. Общий удалённый build cache без префикса по JDK, Gradle и архитектуре воспроизводит редкие промахи ABI и отравляет артефакты соседних команд iOS и Android на том же хосте.
Трансграничный pull: портрет узкого места
Общий удалённый Mac часто совмещает Gradle, CocoaPods и SwiftPM. Сведите число хостов реестра к одному Nexus и замеряйте p95 фазы конфигурации отдельно от компиляции: рост только первой — репозитории и lockfile, обеих — сначала workers.max, затем сеть.
Матрица решений: цепочка зеркал и риски
| Режим | Когда оправдан | Контроль и параметры |
|---|---|---|
| Только публичный Central через ближайший регион | Открытые артефакты, нет политики хранения секретов в URL | systemProp.org.gradle.internal.http.connectionTimeout=120000, socketTimeout=180000, org.gradle.internal.repository.max.tentatives=5 |
| Nexus как единая точка входа с прокси на Central и Plugin Portal | Трансграничные команды, единый ACL и аудит | Явный порядок maven { url = uri(...) } в Kotlin DSL, запрет анонимного allowInsecureProtocol в prod |
| Раздельные репозитории: приватный maven первым, зеркало Central вторым | Смешанные координаты com.company.* и публичные библиотеки |
Документируйте exclusiveContent или фильтры group, чтобы не затенять публичные артефакты |
| Локальный кеш артефактов на томе NVMe + удалённый build cache | Повторяющиеся агрегаты и мульти-модульные деревья | GRADLE_USER_HOME на job, org.gradle.caching=true, префикс ключа с aarch64 и версией Gradle из wrapper |
Сверьтесь с Gradle/Maven и CocoaPods, чтобы не перегрузить диск.
Шаблон settings.gradle.kts: dependencyResolutionManagement и цепочка зеркал
Централизуйте репозитории в settings.gradle.kts. Креды — из переменных окружения CI, не из VCS.
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
repositories {
maven { url = uri("https://nexus.example/repository/maven-public/") }
maven { url = uri("https://nexus.example/repository/gradle-plugins/") }
mavenCentral()
}
}
В gradle.properties: org.gradle.caching=true, org.gradle.parallel=true, systemProp.org.gradle.internal.http.connectionTimeout=120000, socketTimeout=180000, org.gradle.internal.repository.max.tentatives=5. Backoff job-уровня 2/4/8 для 5xx оборачивайте вокруг ./gradlew, не дублируя внутренние ретраи без лимитов реестра.
Параллель, workerы и память демона на M-серии
--parallel и org.gradle.parallel=true распараллеливают подпроекты; Kotlin ограничивайте org.gradle.workers.max. На shared удалённом Mac снижайте workerы и смотрите пик RSS.
| Ядра хоста (физ.) | Стартовый org.gradle.workers.max | Ориентир org.gradle.jvmargs |
|---|---|---|
| 8 (M2/M3 база) | 3–4 при двух активных Gradle job | -Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError |
| 10–12 (M4 Pro) | 5–6 при одном тяжёлом job | -Xmx6g -XX:MaxMetaspaceSize=768m и отдельный GRADLE_USER_HOME на NVMe |
| Общий пул без гарантии эксклюзива | 2–3 и явный лимит одновременных ./gradlew на хост |
Снижайте kotlin.daemon.jvmargs или отключайте параллель модулей в ночных ветках |
Для демона: org.gradle.daemon.idletimeout=10800000 и post-step очистка; полная изоляция — ./gradlew --no-daemon с холодным стартом.
Удалённый build cache: префикс ключа и инвалидация
Задайте namespace удалённого build cache с версией wrapper, мажором JDK и aarch64; при смене Kotlin или ABI поднимите префикс или очистите сегмент по runbook. Локально держите GRADLE_USER_HOME/caches в workspace; URL удалённого кеша и заголовки — только из переменных окружения.
Операционные шаги: от каталогов до приёмки lockfile
Шаг 1. GRADLE_USER_HOME="${CI_WORKSPACE}/.gradle-${CI_PIPELINE_ID}" до checkout.
Шаг 2. org.gradle.caching=true, таймауты из блока выше; проверьте PREFER_SETTINGS или FAIL_ON_PROJECT_REPOS.
Шаг 3. Холодный ./gradlew --build-cache --parallel tasks, затем целевой assemble; --scan — только ночью.
Шаг 4. На Could not resolve / timeout — повтор CI с backoff, но не маскируйте битый lockfile.
Шаг 5. --write-verification-metadata или gradle.lockfile в отдельном MR; в main сравнивайте checksum с merge-base.
Шаг 6. Артефактируйте dependencyInsight и начало лога конфигурации для спорных модулей.
Шаг 7. Зафиксируйте wrapper и checksum дистрибуции в README раннера; при откате верните distributionUrl и очистите сегмент кеша.
FAQ: 401, certificate pinning и смесь Nexus с Central
Креды не в /.gradle/gradle.properties сервисной учётки: используйте env и providers.environmentVariable в Kotlin DSL; проверьте отсутствие устаревшего buildscript.repositories.
Truststore в образе раннера или JAVA_TOOL_OPTIONS=-Djavax.net.ssl.trustStore=...; не отключайте проверку цепочки глобально.
Следующие шаги без входа: узел, SSH и помощь
Тарифы узла, помощь по SSH, реле SSH, главная, аренда, блог.
Удалённый Mac под Gradle и мультиплатформенный CI
Выберите узел ближе к Nexus и закрепите политику кеша: ниже только открытые страницы MacPull без обязательного входа.