Опыт использования flatten-maven-plugin для упрощения версионирования в maven-проектах
О нас
В 1С мы разрабатываем не только платформу 1С: Предприятие на С++ и JavaScript, но и приложения на Java – в частности новую среду разработки Enterprise Development Tools на базе Eclipse и сервер глубоко интегрированного с платформой мессенджера – Системы Взаимодействия.
Вступление
В качестве системы сборки Java-приложений чаще всего мы используем maven, и в этой небольшой статье хотели бы рассказать об одной из проблем, с которой пришлось столкнуться в процессе организации разработки, и о подходе, позволившем эту проблему преодолеть.
Предпосылки и рабочий процесс
В связи со спецификой разработки в наших maven-проектах мы используем достаточно много модулей, зависимостей и дочерних проектов. Количество pom-файлов в одном дереве может исчисляться десятками и даже сотнями.
Казалось бы: ничего страшного, один раз создали и забыли. Если надо что-то поменять или добавить во всех файлах сразу, существует масса удобных инструментов в редакторах и IDE. А какое самое распространённое регулярное изменение pom.xml? Полагаем, что изменение версий проекта и зависимостей. Возможно, кто-то захочет с этим поспорить, но у нас дело обстоит именно так. Причина кроется в том, что наряду с ядром мы параллельно разрабатываем много собственных библиотек, и для постоянной воспроизводимости результатов сборки и тестирования использование снэпшотов не представляется нам удобным подходом. По этой причине и приходится поднимать номер версии в проектах при каждой сборке.
Также у разработчика время от времени возникает необходимость собрать свою ветку какой-либо библиотеки и проверить ее работоспособность по всем зависимостям, для чего в них всех приходится вручную менять версию.
Первоначальное решение
С такими частыми и множественными изменениями версий процесс в рамках CI хочется упростить и автоматизировать. Тут на помощь приходит удобный общеизвестный плагин versions-maven-plugin — подключаем его и запускаем
mvn -N versions:set -DnewVersion=2.0.1
и мавен все как надо сделает: пробежит по иерархии сверху донизу, все версии подменит — красота! Теперь осталось поднять pull-request, коллеги изменения отсмотрят, и можно быстренько вливаться в trunk. Быстренько? Как бы не так. Пара-тройка сотен pom.xml на ревью, и это не считая кода. Вдобавок и от merge-конфликтов никто не застрахован при таком большом числе изменённых файлов. Здесь надо заметить, что в процессе CI изменения версий происходят автоматически вместе с изменением функциональности, а не как-то отдельно.
Новые возможности
На некоторое время мы успокоились и, смирившись, так и жили, пока парни из Maven Apache Project не включили в maven, начиная с версии 3.5.0-beta-1, поддержку так называемых «заменителей» версий (placeholders). Суть этих заменителей в том, что в pom.xml вместо конкретного указания версии проекта используются переменные ${revision}, ${sha1} и ${changelist}. Сами значения этих свойств задаются либо в элементе <properties>, либо их можно определить через системное свойство
mvn -Drevision=2.0.0 clean package
Значения системных свойств имеют преимущество перед значениями, определёнными в <properties>.
Родитель
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
<version>18</version>
</parent>
<groupId>org.apache.maven.ci</groupId>
<artifactId>ci-parent</artifactId>
<name>First CI Friendly</name>
<version>${revision}${sha1}${changelist}</version>
…
<properties>
<revision>1.3.1</revision>
<changelist>-SNAPSHOT</changelist>
<sha1/>
</properties>
</project>
Потомок
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.apache.maven.ci</groupId>
<artifactId>ci-parent</artifactId>
<version>${revision}${sha1}${changelist}</version>
</parent>
<groupId>org.apache.maven.ci</groupId>
<artifactId>ci-child</artifactId>
…
</project>
Если хочется собрать версию 2.0.0-SNAPSHOT, то просто используем
mvn -Drevision=2.0.0 clean package
Если хочется сделать релиз, то просто обнуляем SNAPSHOT
mvn -Dchangelist= clean package
*Примеры выше взяты из статьи на сайте Maven Apache Project
Суровая реальность
Всё хорошо и здорово, пора испытать чувство удовлетворения, но нет. Оказывается, что для install и deploy этот способ не подойдет, поскольку в описаниях публикующихся в репозиторий артефактов не будет подменяться ${revision} на её значение и maven дальше не поймет о чём вообще речь.
<parent>
<groupId>org.apache</groupId>
<artifactId>apache</artifactId>
<version>${revision}</version>
</parent>
Свет в конце тоннеля
Надо искать решение проблемы. Ситуацию мог бы спасти flatten-maven-plugin. Этот плагин разрешает все переменные в pom, но заодно вырезает массу другой информации, которая нужна только при сборке и не нужна при импорте опубликованных артефактов в другие проекты. Также плагин «выпрямляет» все parent-child зависимости, и в итоге получаются плоские pom, включающие в себя всё, что нужно. Неудобство заключалось в том, что вырезает «лишнего» он слишком много, что нас совсем не устраивало. После изучения информации по разработке этого плагина, выяснилось, что мы не одни такие во вселенной, и ещё в августе 2018 на гитхабе в репозитории плагина был создан pull-request с пожеланием сделать возможность определять самостоятельно как нужно «портить» pom.xml. Разработчики прислушались к голосам страждущих, и уже в декабре с выходом новой версии 1.1.0 во flatten-maven-plugin появился новый режим resolveCiFriendliesOnly, который как никогда пришёлся впору – он оставляет pom.xml как есть, кроме элемента <version> и разрешает ${revision}, ${sha1} и ${changelist}.
Добавляем плагин в проект
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.1.0</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
Готово!
Happy end
Отныне нам, чтобы изменить версию всего проекта и дать знать об этом всем зависимостям, надо всего-то отредактировать элемент <revision> в одном лишь корневом pom.xml. На ревью прилетает не сотня-другая этих файлов с одинаковым изменением, а один. Ну и отпадает необходимость в использовании versions-maven-plugin.