О нас


В 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.