Что может быть проще? Пишем команду сборкиgradle clean buildи все готово. На первый взгяд все действительно так, и займет это немного времени. Но со временем кодовая база и, соответственно, количество тестов (ну я очень на это надесь) будет расти, вы не успеете опомниться как сборка будет у вас занимать уже 10 или больше минут

Давайте подумаем что нам может в этом помочь? А для этого проанализируем основные шаги этого процесса:

  1. Разрешение зависимостей на внешние библиотеки

  2. Компиляция Java кода

  3. Прогон тестов

Бьем проект на модули

А так же вспомним (или узнаем) про такую фичу gradle как build cache. Как она может нам помочь? Ведь по факту, если мы внесем в java код какое либо измение, это вызовет перекомпиляцию кода и прогон всех тестов. В этом случае нам может помочь модульность проекта. Если у нас код довольно сильно сегментирован (например по доменным моделям), и зачастую в рамках выполнения задач мы вносим изменения только в какую либо часть проекта - то нет необходимости перекомпилировать весь проект и выполнять абсолютно все тесты.

Например у нас какой либо проект интернет магазина, у него есть несколько основных доменов:

  • account (клиенты)

  • product (товары)

  • orders (заказы, этот модуль уже в свою очередь зависит от обоих модулей account и product)

Например вы вносите изменения в

  • модуль account - это вызовет перекомпиляцию модуля account и orders (как зависимого) и выполнит в них тесты. Модуль product не нужно перекомпилировать и выполнять в нем тесты, т.к. код не менялся

  • модуль orders - это вызовет перекомпиляцию только этого модуля и выполнение его тестов. Модули account и product остаются не тронутыми - профита тут уже гораздо больше!

Итак, наш скрипт запуска уже выглядит так: gradle clean build --build-cache

Запуск на CI

Окей, проверили локально - все супер, повторный запуск команды отрабатывает за секунды, отпрявлям все это на Gitlab CI. И что в итоге видим? А никакого профита нет. Конечно же, каждая джоба выполняется в изолированном контейнере, и в нем нет никаких кэшей, каждый раз выполянется загрузка всех зависимостей с Maven Cental, каждый раз проект компилируется, и каждый раз выполняются все тесты.

Нужно как то переносить "артефакты" между сборками. Тут нам может помочь Gitlab Cache. Окей, инструмент у нас нас есть, теперь нужно определиться, что же мы хотим переносить.

Кэшируем зависимости

Загруженные зависимости по умолчанию хранятся в ${USER_HOME}/.gradle

build:
  stage: build
  image: gradle:7.2-jdk17
  before_script:
    - export GRADLE_USER_HOME=`pwd`/.gradle_home
  script:
    - gradle clean build check --stacktrace --info --build-cache
  cache:
    - key: dep-cache
      paths:
        - .gradle_home

Теперь каждый раз не будут загружаться зависимости, а будут кэшироваться (скорее всего в s3, смотря как настоен ваш Gitlab). Это хоть и не бесплатно, как локальное хранение, но все равно быстрее чем походы во внешнюю сеть.

Кэшируем результаты билда

Дальше нам нужно как то кэшировать результаты компиляции проекта и результатов тестов. Артефакты лежат по умолчанию в папке build в каждом модуле. Прописывать кажду папку вручную не вариант, мы же программисты. Давайте изменим расположение buildDir в каждом моделе. Для этого внесем изменения в корневой build.gradle

ext {
    set('rootProjectDir', "${projectDir}")
}
allprojects {
    buildDir = "${rootProjectDir}/.build/${project.name}/build"
}

и добавляем папку .build из корня проекта под кэш.

cache:
  - key: build-cache
    paths:
      - .build

На данном шаге мы должны уже получить какой то профит от проделанной работы.

Немного про TestContainers

Давайте рассмотрим еще случай - например использование TestContainers для докеризации окружения в тестах. Здесь есть момент - что когда мы находимся внутри контейнера при запуске тестов мы будем каждый раз загружать образы для внутренних контрейнеров. Если у вас есть корпоративный docker hub (кэширующий прокси), то мы можем подсказать путь для более быстрой загрузки из него:

build:
  stage: build
  image: gradle:7.2-jdk17
  before_script:
    - export GRADLE_USER_HOME=`pwd`/.gradle_home
    - export TESTCONTAINERS_RYUK_CONTAINER_IMAGE=my-docker-hub.io/testcontainers/ryuk:0.3.3
  script:
    - gradle clean build check --stacktrace --info --build-cache

Либо же пишем код ручками.

P.S. Что не надо бить монолит на модули, а разносить на микросервисы писать замечания не стоит - цель была показать возможность конфигурации buildDir

Комментарии (5)


  1. Kalinavich
    26.01.2022 10:44
    +1

    иии для чего этот нам мануал читать?


    1. pavel_nv Автор
      26.01.2022 10:45
      -1

      Так никто не заставляет же. Демократия в действии. Надеюсь кому то будет полезным.


  1. isicju
    26.01.2022 14:05
    +1

    Кто ненавидит грэдл пожалуйста плюсаните этот комментарий. Хочу понять насколько я одинок.


    1. stgunholy
      27.01.2022 11:20

      После грейдла вообще не могу мейвен…


    1. zhuravlev_oe
      27.01.2022 14:29

      Ну, батенька, это вы погорячились - вы просто не умеете его готовить!