Собираем многомодульный Gradle проект в Gitlab CI

от автора

Что может быть проще? Пишем команду сборки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


ссылка на оригинал статьи https://habr.com/ru/post/647809/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *