Проблема сборки bootWar композитного проекта в GitLab CI

Требуется помощь коллективного разума…
Может кто-то сталкивался с подобной странной (для нас) ситуацией и подскажет куда смотреть.

Есть приложение мигрированное с Cuba на Jmix 1.5.5.
bootWar собирался в CI GitLab (Selfhostad) и успешно развертывался на тестовом стенде из WAR.

Т.к. есть другие приложения, расширяющие функционал основного - мы преобразовали проект в композитный.
В композитном проекте :

  • Основное мигированое приложение стало аддоном с основным функционалом. По сути все в нем вплоть до экранов и Users с DatabaseUserRepository.
  • Добавилось приложение-раннер. В раннере пока ничего своего - только завистмость на аддон основного функционала.

Из Jmix Studio в fatJar приложение-раннер запускается и весь функционал работает, как на уже существующей БД, так и с инициализацией новой БД.

Собираем раннера из Idea в bootWar на ПК разработчика и ручками развертываем тут же на TomCat 9 - развертывается и работает как часы.

Отдаем композитный проект в один репозитоий в GitLab, проходит сборка в CI:

  • приложение-раннер собирается в bootWar и публикуется.

В итоге в bootWar, собранном в CI GitLab в persistence.xml нет сущностей аддона основного функционала.

Есть все сущности, даже из других наших аддонов, использованных в аддноне основного функционала.
Приезжает все: liquibase change-logs из нашего аддона, кастомная тема из аддона, кастомный экран логина.

Но в persistence.xml варника ни одной сущности, реализованной в самом основном аддоне.

В выводе выполнения CI ничего в глаза не бросается.

  • Выполняется compileJava для аддона основного функционала
  • Выполняется compileJava для приложения-раннера.

Т.е. локально у нас на на компах bootWar собирается корректно с корректым наполнением persistence.xml. Та же таска в CI GitLab выдает варик в котором в persistence.xml нет сущностей основного аддона.

CI GitLab настроен давно и работал несколько месяцев без проблем до преобразования в композитный проект и выдавал корректный bootWar.
Сейчас он предельно прост:

stages:          
  - publishWar
image: gradle:7.6-jdk11
variables:    
  GRADLE_OPTS: "-Dorg.gradle.daemon=false"

before_script:
  - GRADLE_USER_HOME="$(pwd)/.gradle" 
  - export GRADLE_USER_HOME 
  - export CI_DEPLOY_PASSWORD

publishWar-job:       
  stage: publishWar
  script:
    - echo "Start publishWar..."
    - cd all
    - chmod +x gradlew
    #- ./gradlew :simplerunner:bootWar
    - ./gradlew :simplerunner:publishWebAppPublicationToMavenRepository

На локалных ПК (где war собирается корректно) используется JDK 11 Belsoft, в CI используется OpenJDK 11.

Проверили содержимое аддона основного функционала, собранного этим же конвейером в GitLab и опубликованным в репозиторий - в артефакте аддона в persistence.xml все сущности на месте.

Провели эксперимент:

  1. Создали Новый Full stack project Jmix 1.5.5 (в стороне, не в композитном проекте)

  2. Удалили из него Users, шаблонные экраны и реализацию User repository.

  3. Добавили зависимость на наш аддон с основным функционалом. Запушили в GitLab в отдельный репозиторий, настроив там CI по тому же шаблону, что и CI в композитном проекте.

  4. В отдельном проекте-раннере в CI собрался корректный варник.

Теперь самое загадочное для нас:

  1. Открываем в Jmix Studio наш композитный проект, добавляем в него средствами Studio наш новый проект раннера (из пункта 4) из существующего источника.

  2. Сдаем этот дополненный композитный в GitLab,

  3. CI собирает публикует варник нового проекта-раннера и в нем НЕТ сущностный основного аддона.
    ЭТОТ же проект До его добавления в композитный нормально собирал варик в том же CI.
    Но собирает некомплетный варник после помещения его в композитный проект.

CI работает на образе gradle:7.6-jdk11. Пробовали openjdk:11-jdk и openjdk:17-jdk - картина неменяется.

Что может подобным образом влиять на сборку композитного проекта с CI?

Вроде все перепроверили в проекте самого аддона.
Да его артефакт нормально собирается в CI persistence.xml все сущности на месте. И как мы уже проверили - для отдельного приложения-раннера варник собирается нормально в том же CI.

В каталоге all композитного проекта вроде по сути всего 2 значимых файла: build.gradle и settings.gradle.
В gradle-wrapper.properties указано на gradle 7.6 как и в подпроектах.

Что может быть не так со сборкой bootWar композитного проекта в CI?

Андрей, здравствуйте!

Как возможное быстрое решение, попробуйте, пожалуйста, добавить какую-нибудь сущность в проект раннер. jmix gradle plugin во время сборки создает persistence.xml, собирая сущности из зависимостей. Возможно, что-то идет не так из-за того, что в самом проекте нет сущностей.

Для того, чтобы точнее понять в чем проблема, не могли бы вы приложить логи сборки из CI (лучше с уровнем --info, если возможно)?
Стоит обратить внимание на часть вида

Project entities:
    JPA: [com.company.demo.entity.Root, com.company.demo.entity.MyKey, com.company.demo.entity.Branch, com.company.demo.entity.User];
    DTO: [];
Project converters: [].
Found entities:
    JPA: [io.jmix.securitydata.entity.RowLevelPolicyEntity, io.jmix.securitydata.entity.RoleAssignmentEntity, com.company.demo.entity.Root, io.jmix.flowuidata.entity.UserSettingsItem, io.jmix.securitydata.entity.StringCollectionConverter, io.jmix.core.entity.UriConverter, io.jmix.securitydata.entity.UserSubstitutionEntity, io.jmix.securitydata.entity.ResourceRoleEntity, com.company.demo.entity.MyKey, com.company.demo.entity.User, io.jmix.data.entity.ReferenceToEntity, io.jmix.securitydata.entity.RowLevelRoleEntity, io.jmix.flowuidata.entity.FilterConditionConverter, com.company.demo.entity.Branch, io.jmix.core.entity.FileRefConverter, io.jmix.flowuidata.entity.FilterConfiguration, io.jmix.securitydata.entity.ResourcePolicyEntity];
    DTO: [].

В ней “Found entities” (видно только начиная с уровня --info) – это сущности собранные из зависимостей. Совпадают ли они с теми, что при сборке локально?

Так же, на всякий случай, следует обратить внимание, нет ли фразы “Entities enhancing was skipped, because entity classes haven’t been changed since the last build” в логах.

Если с этим всем все ок, нужно посмотреть папку build локально и в CI:

Снимок экрана 2024-08-15 в 14.52.09

persistence.xml в глубине resources должен быть в наличии и содержать все сущности, так же как и тот, что в глубине tmp.

С уважением,
Дмитрий

Добавление в проект-раннер JPA и Dto общностей не изменило картину…
При локальной сборке bootWar в persistence.xml приходит все что надо.
При сборке в CI в persistence попадают все сущности, кроме сущностей основного аддона.
Созданные в раннере сущности тоже есть в persistence.

“Found entities” в части сборки war раннера нет.

Лог вывода с --info при локальной сборке и в CI сейчас вышлю в личку.

И сейчас убедился на разных ПК в еще одном странном эффекте.

После добавления сущностей в проект-раннер из Idea перестал корректно собираться - нет там сущностей основного аддона.
И при попытке запуска раннера из Idea соответствующей эффект - приложение стартует, но при логине получаем ошибки - в persistence.xml нет сущностей основного аддона.

Но вот если Idea закрыть и собрать bootWar из команной строки - варник собирается Корректно. :rofl:

Андрей, здравствуйте!

Прошу прощения за поздний ответ, посмотрел логи и, кажется, вижу в чем может быть проблема.

Для CI где-то между подтягиванием сущностей из самого проекта и подтягиванием из зависимостей происходит compileThemes и compileWidgets для аддона. А задача <названиеаддона>:jar происходит вообще после энхансинга сущностей для раннера. То есть jar-ник аддона еще не готов на момент поиска сущностей из ранера. А они именно в jar и ищутся по находящемуся там persistence.xml.

Может включена параллельная сборка в gralde?

Чтобы решить проблему нужно убедиться что задача jar для аддона уже выполнена на момент выполнения задачи compileJava для раннера.

По поводу возникших проблем при локальной сборке - возможно, происходит то же самое и нужно проверить порядок выполнения в логах.

С уважением,
Дмитрий

Добрый день, Дмитрий!

А вот это - очень хорошая идея!
Про это вообще бы не подумали, тем более, что у себя в проекте точно ни где не указывали org.gradle.parallel=true. В gradle.properties в пользовательских домашних каталогах этого свойства тоже ни у кого нет.

И как раз сегодня мы попробовали собрать композитный проект на паре ПК, где он еще ни разу не собирался и Внезапно в обоих случаях persistence.xml оказался некомплектным.

Т.к. вроде указанное в командной строке свойство имеем максимальный пробовали приоритет - попробовали собрать локально явно отключив параллельность: ./gradlew bootWar -Dorg.gradle.parallel=false.

И на обоих ПК, где сегодня воспроизвели локально эту проблему сборки - варник собрался Корректный!

Специально проверили в локальной сборке на проблемных ПК и убедились, что:

  1. В проекте-раннере Обязательно должна быть определена хотя бы одна сущность. Достаточно DTO.
    Без нее war проекта-раннера соберется с некомплектным persistence.xml даже при отключенной параллельной сборке в gradle.
  2. Параллельную сборку в gradle надо отключить (хотя пока не обнаружили где она включена).
    В нашем случае пока просто явно указываем опцию -Dorg.gradle.parallel=false в таске сборки.

Но вот в CI это не помогло…
В качестве бреда даже добавили в CI сначала сборку bootWar, а потом publishWebAppPublicationToMavenRepository:

- ./gradlew :ProjectRunner:bootWar -Dorg.gradle.parallel=false --info
- ./gradlew :ProjectRunner:publishWebAppPublicationToMavenRepository -Dorg.gradle.parallel=false --info

Но на выходе из CI варник по прежнему некомплектный :frowning:

Решение явно уже где-то очень рядом…

В личку отправил лог из CI с отключенной параллельностью в gradle. Может заметите еще что-то…

нужно проверить порядок выполнения в логах.

А я правильно понимаю, что о порядке выполнения тасок в выводе gradle можно судить вот по этому compileJava (Thread[included builds Thread 2,5,main]) started?

Андрей, здравствуйте!

К сожалению, не увидел новых сообщений в личке, но по порядку выполнения тасок, да, я бы предложил ориентироваться на следующие строки, если рассматривать прошлый отправленный файл для CI:
> Task :*****Runner:compileJava (2501 строка, в подзадаче для этой задачи происходит анализ jar-ников из зависимостей и поиск сущностей в них)
> Task :s*******c:s*******c:jar (2619 строка, но только здесь, уже позже, появляется jar-ник откуда нужно было утащить сущности)

А должно идти наоборот: сначала jar аддона и только потом compileJava раннера.

Поэтому стоит попробовать на CI запустить скорее такие задачи:

# до сборки раннера собрать артефакт аддона (если я правильно понял стуктуру проекта).
- ./gradlew :Ps*******c:s*******c:jar -Dorg.gradle.parallel=false --info
# а потом уже делать все остальное. Не обязательно отдельно, gradle пропустит up-to-date задачи
- ./gradlew :ProjectRunner:bootWar -Dorg.gradle.parallel=false --info
- ./gradlew :ProjectRunner:publishWebAppPublicationToMavenRepository -Dorg.gradle.parallel=false --info

Если jar для аддона выполнить заранее и отдельно, то уже, может, и не будет требоваться отключать параллельный запуск.

Кстати, у нас есть issue на этот случай, можете добавить к нему какую-нибудь реакцию или комментарий, чтобы подчеркнуть, что проблема актуальна.

P.S. Возможно, при локальной сборке все же придется не отключать параллельный запуск, а сначала явно запускать jar аддона и только потом все остальное, потому что gradle, видимо, даже при последовательном запуске может сначала все кучей компилировать и только потом все кучей паковать.

С уважением,
Дмитрий

2 симпатии

Сказать “Огромное спасибо” - это ни сказать ничего :slight_smile:

Вот теперь, похоже, полностью победили!

Пробовали разные варианты и все отработали нормально и в CI, и на локальном ПК где воспроизводили проблему:

./gradlew :mainAddon:mainAddon:jar -Dorg.gradle.parallel=false
./gradlew :AppRunner:publishWebAppPublicationToMavenRepository -Dorg.gradle.parallel=false

## Или

/gradlew :mainAddon:mainAddon:jar :AppRunner:publishWebAppPublicationToMavenRepository -Dorg.gradle.parallel=false

## Или даже отключение параллельности только для сборки аддона

./gradlew :mainAddon:mainAddon:jar -Dorg.gradle.parallel=false
./gradlew :AppRunner:publishWebAppPublicationToMavenRepository

Итого:

  1. Обязательно хотя бы одна сущность в приложении-раннере.
  2. Запуск приложения или сборка War цепочкой команд gradle где сначала идет сборка Jar основного аддона с отключением параллельности, а потом bootRun/bootWar.

Кстати, у нас есть issue на этот случай, можете добавить к нему какую-нибудь реакцию или комментарий, чтобы подчеркнуть, что проблема актуальна.

Обязательно там отпишу итоговую сводку! Очень много времени убили на это…

И в личку сейчас Точно отправлю вывод gradle. Может, будет полезно. Вчера форум закрывал страницы с ошибкой…

1 симпатия

Спасибо за подробный рассказ о том, как победили проблему, и за логи! Теперь пришло)