Собираем многомодульный 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/


Интересные статьи

Интересные статьи

Доброго времени суток, хабровчане!В данной статье я рассмотрю вопрос развертывания с нуля NuxtJS-проекта (либо любого другого проекта на NodeJS) на VDS-сервере с использо...
В данной статье хочу поделиться опытом разработки мобильного enterprise-приложения на платформе Apache Cordova, рассказать о вариантах реализации такого проекта, о плюсах...
На нашем форуме по искусственному интеллекту RAIF прозвучал интересный кейс от Дмитрия Бочарова, вице-президента по внутреннему контролю и аудиту в Segezha Group. Дмитрий рассказал, как испол...
В IT здоровый проект — это система или сервис, который, с одной стороны, качественный, то есть соответствует требованиям и нравится пользователям. С другой стороны, приносит прибыль, потому что б...
Реализация ORM в ядре D7 — очередная интересная, перспективная, но как обычно плохо документированная разработка от 1с-Битрикс :) Призвана она абстрагировать разработчика от механики работы с табл...