Интент запроса: ускорить ./gradlew на удалённом Mac через dependencyResolutionManagement в Kotlin DSL, Gradle properties, --parallel и удалённый build cache. Ниже — матрица зеркал, шаблон settings.gradle.kts, пороги workerов, ключи кеша и приёмка lockfile. См. также главная, блог, CocoaPods, SPM, Gradle/Maven.

Три узких места трансграничного pull на общем Apple Silicon:

  1. Репозитории без иерархии. Смешение прямого Maven Central, корпоративного Nexus и plugins.gradle.org в разных модулях даёт недетерминированный порядок разрешения и «плавающие» версии при частичной деградации зеркала.
  2. Параллель × память. Сочетание org.gradle.parallel=true, высокого org.gradle.workers.max и тяжёлого Kotlin compile на одном удалённом Mac упирается в пик Metaspace и своп, что маскируется под сетевые таймауты.
  3. Кеш без пространства имён. Общий удалённый 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 и очистите сегмент кеша.

Три цифры для отчёта: p95 конфигурации; доля попаданий удалённого build cache; пик RSS демона на самом тяжёлом модуле Kotlin.

FAQ: 401, certificate pinning и смесь Nexus с Central

Почему после включения зеркала Gradle возвращает 401 только в CI?

Креды не в /.gradle/gradle.properties сервисной учётки: используйте env и providers.environmentVariable в Kotlin DSL; проверьте отсутствие устаревшего buildscript.repositories.

Как сочетать корпоративный pinning TLS и публичный Central?

Truststore в образе раннера или JAVA_TOOL_OPTIONS=-Djavax.net.ssl.trustStore=...; не отключайте проверку цепочки глобально.

Можно ли параллелить Gradle с тяжёлым CocoaPods на одном хосте?

Да, но лимитируйте одновременные резолверы; см. CocoaPods и SPM.

Следующие шаги без входа: узел, SSH и помощь

Тарифы узла, помощь по SSH, реле SSH, главная, аренда, блог.

Удалённый Mac под Gradle и мультиплатформенный CI

Выберите узел ближе к Nexus и закрепите политику кеша: ниже только открытые страницы MacPull без обязательного входа.