Как обновить версию дочерних модулей в Maven?
Как обновить версию дочерних модулей? Есть много вопросов Stackoverflow, подобных этому, но я не смог найти тот, который соответствует этому точному сценарию... хотелось бы, если это дубликат.
Рассмотрим следующий проект.
parent
--mod1
--mod2
В начале цикла выпуска dev мне нужно обновить родительский модуль и модули до той же версии. Если версия родителя и модулей осталась неизменной на протяжении всего выпуска, я бы просто опустил тег <version>
из модулей и выполнил versions:set -DnewVersion=1.1.1
, чтобы запустить цикл dev. Но, как оказалось, модули не все заканчивают цикл той же версией. Поскольку ошибки и исправления материализуют только те модули с ошибками и таким образом обновляются. Например, родительский и mod2 могут быть в версии 1.1.1-RC1, но mod1 может быть в 1.1.1-RC2.
Как таковой мне нужно:
1) Включите тег <version>
в модули для независимой отслеживания каждой версии модулей.
2) Если mod2 требует mod1 в качестве зависимости, мне нужно убедиться, что mod2 ссылается на последнюю версию mod1.
Это приводит к следующим двум вопросам.
1) В начале цикла, как я могу установить родительский и модули в одну и ту же версию в одной команде maven? Я попробовал version:set -DnewVersion=1.1.1
, но это только обновляет родительскую версию для всех POM, но не версию модуля. Я также пробовал -N versions:update-child-modules
, но я думаю, что использую его неправильно, потому что он ничего не делает, просто показывает пропущенные для всех модулей.
2) Это немного сложнее и соответствует пункту 2 выше. Как обновить версию mod1 и версию mod2 для версии mod1 за один шаг? Я знаю, как это сделать в 2 этапа:
parent pom:
<properties>
<!-- update this manually if mod1 version no longer matches parent -->
<mod1.version>${project.version}</mod1.version>
</properties>
mod2 pom:
<dependency>
<groupId>com.xxx</groupId>
<artifactId>mod1</artifactId>
<version>${mod1.version}</version>
</dependency>
Когда mod1 сталкивается с 1.1.1-RC2, я обновляю родительский POM и mod1 POM, чтобы отразить это. Это два шага. В любом случае, чтобы превратить его в один шаг?
Мой пример был небольшим, но в реальной жизни есть много модулей, которые делают эту важную экономию времени, плюс мне любопытно.
Ответы
Ответ 1
Вопрос 1)
Лучший способ управлять жизненным циклом и версиями приложений - использовать плагин выпуска.
Как вы знаете, философия Maven является условной конфигурацией. Соглашение Maven заключается в том, чтобы использовать версии моментальных снимков (заканчивающиеся на -SNAPSHOT) во время разработки и назначать версию без моментального снимка только для выпусков.
Предположим, вы разрабатываете версию 1.1.1. В процессе разработки вы просто используете 1.1.1-SNAPSHOT. Maven позаботится об обновлениях моментального снимка. Если вы используете репозиторий артефактов, вы можете использовать -U, чтобы всегда иметь последнюю версию снимков.
Когда релиз готов, релиз-плагин генерирует и развертывает версию 1.1.1 и обновляет POM с новой версией разработки, такой как 1.1.2-SNAPSHOT.
В многомодуальных проектах есть два сценария: модули связаны, но независимы (например, несколько веб-приложений), или они являются модулями одного большого приложения или библиотеки, и они обмениваются версиями. Вы, кажется, заинтересованы в последнем.
Лучшим способом в этом случае является наследование одного и того же родительского (возможно, корневого) модуля, включая его версию. Вы указываете родительскую группу: артефакт: версия, и вы не укажете версию для детей. Как правило, вы также наследуете группу, поэтому ваш ребенок-помпа может выглядеть так:
<parent>
<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject-parent</artifactId>
<version>1.1.1-SNAPSHOT</version>
<relativePath>../myproject-parent</relativePath>
</parent>
<artifactId>myproject-module1</artifactId>
Теперь вам просто нужно позаботиться о том, чтобы дети указывали на правильную версию родителя с помощью плагина release.
Чтобы помочь ему узнать о детях, вы должны сделать свой родительский pom также корневым помпом, включив раздел модулей, как показано ниже.
Вопрос 2)
Обычно я объявляю свойства родителя со всеми версиями всех артефактов, на которые можно ссылаться. Если несколько модулей используют версию, вам просто нужно одно свойство. Родитель может выглядеть так:
<groupId>com.mycompany.myproject</groupId>
<artifactId>myproject-parent</artifactId>
<version>1.1.1-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<myproject.version>1.1.1-SNAPSHOT</myproject.version>
</properties>
.......
<modules>
<module>../myproject-module1</module>
...
</modules>
Дети могут ссылаться на другие модули, используя
<version>${myproject.version}</version>
Очень плохая практика объявлять зависимости, используя LATEST. Скажем, вы делаете это для версии 1.1.1. Теперь вы работаете с версией 1.1.2-SNAPSHOT и, вероятно, у вас есть артефакты с этой версией, установленной в вашем локальном репо.
Теперь скажите, почему вам нужно перестроить версию 1.1.1, например, из-за ошибки в производстве. Ваша сборка будет использовать новую версию. Если вам повезет, это сломает сборку. Если вам не повезло, это может даже остаться незамеченным к производству.
И последнее, но не менее важное: некоторым людям нравится использовать значения свойств для объявления дочерних версий. Это сильно обескуражено и будет сообщено как предупреждение от maven. Я лично этого никогда не делаю. Причины также связаны с воспроизводимостью сборки и тем фактом, что maven предполагает, что сборка релиза никогда не изменится. Наличие версии модуля внешне настраиваемой не является хорошей идеей.
EDIT:
Случай, когда версии модулей не выровнены.
На самом деле оба сценария могут быть смешаны.
Вы можете иметь, например:
Родитель
--- Component1
--- Component2
--- Component3
------ Comp3Module1
------ Como3Module2
------ Comp3Module3
Если родительская и трехкомпонентная версии отличаются друг от друга, и три модуля компонента3 используют одну и ту же версию, как описано ранее.
Вопрос 1)
В этом случае каждый модуль имеет определенную версию.
Как уже было сказано, это плохой способ использовать свойство для указания версии модуля, поэтому я могу только рекомендовать буквально указать версии.
Как уже говорилось, для управления версиями лучше всего использовать плагин release и интегрировать его с системой управления версиями, такой как SVN.
В других ответах дается подробная информация о том, как его использовать, поэтому я не буду подробно останавливаться на этом, если только не будет запрошено.
Вопрос 2)
Рекомендуемый подход тот же, что и для случая использования одной и той же версии, только для того, чтобы вам нужно несколько свойств.
Родитель может выглядеть так:
<properties>
<myproject.group>com.mycompany.myproject</myproject.group>
<component1.version>1.1.1-RC1</component1.version>
<component2.version>1.1.1-RC2</component2.version>
<component3.version>2.0.0</component3.version>
<properties>
Затем вы можете использовать управление зависимостями для централизации управления версиями в родительском.
Например, в родительском pom
<dependencyManagement>
<dependencies>
<dependency>
<groupId>${myproject.group}</groupId>
<artifactId>component1</artifactId>
<version>${component1.version}</version>
</dependency>
<dependency>
<groupId>${myproject.group}</groupId>
<artifactId>component2</artifactId>
<version>${component2.version}</version>
<type>war</type>
</dependency>
<dependency>
<groupId>${myproject.group}</groupId>
<artifactId>comp3module1</artifactId>
<version>${component3.version}</version>
<type>ejb</type>
</dependency>
<dependency>
<groupId>${myproject.group}</groupId>
<artifactId>comp3module1</artifactId>
<version>${component3.version}</version>
<type>ejb-client</type>
</dependency>
<dependency>
<groupId>${myproject.group}</groupId>
<artifactId>comp3module2</artifactId>
<version>${component3.version}</version>
<type>war</version>
</dependency>
</dependencies>
</dependencyManagement>
Теперь, чтобы ссылаться на любой модуль из любого другого модуля, это так же просто, как:
<dependency>
<groupId>${myproject.group}</groupId>
<artifactId>component1</artifactId>
</dependency>
<dependency>
<groupId>${myproject.group}</groupId>
<artifactId>comp3module1</artifactId>
<type>ejb-client</type>
</dependency>
Версии автоматически управляются из родителя. Вам не нужно поддерживать их в зависимостях детей, которые также становятся менее подробными.
Ответ 2
1) Я также пробовал версию: установлен в прошлом, но никогда не работал правильно. Предполагается, что он выполняет тот же процесс, что и выпуск: подготовьте, но на самом деле этого не делает. Итак, вы можете попробовать mvn release:prepare -DautoVersionSubmodules -DdryRun
. Это должно сделать все обновления без проверки чего-либо в репо и без создания каких-либо тегов.
2) Я считаю, что проект ClearTK последовали аналогичной стратегии, как и вы: они поддерживали многомодульный проект, каждый из которых имел свой собственный цикл выпуска. Чтобы остаться на вершине ситуации, они внедрили собственный плагин maven, чтобы предупредить их о несоответствиях версии зависимостей.
https://github.com/ClearTK/cleartk/tree/master/consistent-versions-plugin
Хотя такой плагин не будет производить обновления, которые вы запрашиваете, он должен по крайней мере уведомлять вас о необходимости обновления. Чтобы действительно исправить вашу проблему, вы можете подумать о том же маршруте, что и ClearTK, и реализовать свой собственный плагин Maven (или вы делаете то, что в конечном итоге закончилось ClearTK: переход к синхронизированному циклу выпуска;))
Ответ 3
Хорошо, вот что я придумал. Это основано на этой статье continuous-releasing-of-maven-artifacts.
Родительский POM:
<properties>
<!-- versions of modules -->
<main.version>1.0</main.version>
<revision>SNAPSHOT</revision> <!-- default revision -->
<Module1.revision>${revision}</Module1.revision>
<Module2.revision>${revision}</Module2.revision>
<Module3.revision>${revision}</Module3.revision>
<Module4.revision>${revision}</Module4.revision>
<Module5.revision>${revision}</Module5.revision>
<Module1.version>${main.version}-${Module1.revision}</Module1.version>
<Module2.version>${main.version}-${Module2.revision}</Module2.version>
<Module3.version>${main.version}-${Module3.revision}</Module3.version>
<Module4.version>${main.version}-${Module4.revision}</Module4.version>
<Module5.version>${main.version}-${Module5.revision}</Module5.version>
</properties>
Пример дочернего POM с зависимостью между проектами:
<groupId>com.xyz</groupId>
<artifactId>Module4</artifactId>
<packaging>jar</packaging>
<version>${Module4.version}</version>
<parent>
<groupId>com.xyz</groupId>
<artifactId>ParentProject</artifactId>
<version>1.0</version>
</parent>
<dependencies>
<dependency>
<groupId>com.xyz</groupId>
<artifactId>Module1</artifactId>
<version>${Module1.version}</version>
</dependency>
<dependency>
<groupId>com.xyz</groupId>
<artifactId>Module2</artifactId>
<version>${Module2.version}</version>
</dependency>
<dependency>
<groupId>com.xyz</groupId>
<artifactId>Module3</artifactId>
<version>${Module3.version}</version>
<type>jar</type>
</dependency>
<dependencies>
1) В начале цикла, как я могу установить родительский и модули в одну и ту же версию в одной команде maven?
Вам больше не нужно. Родительский POM может оставаться в той же версии, но только при изменении родительского ПОМ. В этом случае вы можете использовать mvn version:set -DnewVersion=1.1.1
. Но вам не нужно этого подхода.
Вместо этого вы можете динамически устанавливать версию с помощью свойства main.version
. Например. mvn clean deploy -Dmain.version=1.1.1
Кроме того, чтобы принудительно выполнить динамическую передачу номера версии, вы можете оставить свойство main.version
по умолчанию, которое я включил в родительский POM выше.
2) Как мне обновить версию mod1 и ссылку mod2 на версию mod1 за один шаг?
Это в основном сводится к тому, как управлять ревизиями. Если я не установил свойство revision
в команде mvn, то все модули будут использовать SNAPSHOT
как ревизию. Если я установил свойство revision
в RC1
, тогда все модули получат эту ревизию. Более того, если я установил revision
в RC1
, но Module4.revision
в RC2
, то Module4 получит RC2, а все остальные модули получат RC1. Это удовлетворяет запрос клиентов на динамические изменения для каждого модуля.
Вот несколько примеров:
-
mvn clean deploy -Dmain.version=1.1.1
Устанавливает все модули в версию 1.1.1-SNAPSHOT
.
-
mvn clean deploy -Dmain.version=1.1.1 -Drevision=RC1
Устанавливает все модули до версии 1.1.1-RC1
.
-
mvn clean deploy -Dmain.version=1.1.1 -Drevision=RC1 -DModule4.revision=RC2
Устанавливает все модули в версии 1.1.1-RC1
, за исключением модуля 4, установленного на версию 1.1.1-RC2
.
Существует предостережение, о котором следует упомянуть. Если вы увеличиваете версию зависимого модуля, например Module1, до RC2, то вы также должны увеличивать версию всех модулей, которые его используют, например, Module4 также не должен увеличиваться до RC2 (или следующей версии). Это то, о чем клиент узнал, и поэтому я предпочел бы, чтобы все модули имели одну и ту же версию. Но мне действительно нравится, как динамично это получилось. По существу, версия теперь устанавливается через командную строку без обновлений требуемых файлов POM.
Ответ 4
1) Как сказал @rec, плагин релиза maven сделает трюк
$ mvn release:prepare -DautoVersionSubmodules=true
$ mvn release:perform
2) Вы можете определить зависимость от mod2 до mod1 как:
<dependency>
<groupId>com.xxx</groupId>
<artifactId>mod1</artifactId>
<version>LATEST</version>
</dependency>
Дополнительная информация: Как сообщить Maven, чтобы использовать последнюю версию зависимости?
Я тестировал оба решения, и он работает! Надеюсь, это поможет вам:)