Предотвращение конфликта версий репозитория maven при использовании ветвей функций

Вопрос: как вы обрабатываете ветки функций для многопроектных сборок maven?

Jenkins создает и развертывает эти ветки, чтобы свести к минимуму накладные расходы разработчиков, но ветки разработки и функций не могут создавать одну и ту же версию maven, иначе мы рискуем несоответствием между артефактами и исходным кодом.

У нас есть скрипт для изменения родительской версии в дочерних помпах и версии в корневом помпе. Хотя это разделяет ветки в пространстве maven, это приводит к дополнительной работе при слиянии.

Я думаю, что промежуточная функция Nexus Pro может помочь нам избежать этого требования и заставить каждую ветку использовать определенное репо, которое мы легко удаляем после удаления/слияния веток.

Еще раз: как справиться с проблемой нескольких веток и maven?


person Peter Kahn    schedule 02.07.2016    source источник
comment
У нас есть скрипт для изменения родительской версии в дочерних pom и версии в корневом pom Почему бы не использовать maven-release-plugin:branch цель? Он автоматически создаст ветку и обновит версию, которую вы можете установить на x.x-my-branch или что-то подобное.   -  person Tunaki    schedule 02.07.2016
comment
Спасибо @Тунаки. Является ли слияние все еще проблемой, так как помпы были изменены?   -  person Peter Kahn    schedule 02.07.2016
comment
Да, с другой версией это будет больно... см. также stackoverflow.com/questions/3555160/ (или stackoverflow.com/questions/ 12197191/)   -  person Tunaki    schedule 02.07.2016
comment
Мы используем mvn versions:set -DnewVersion=1.2-BRANCH-SNAPSHOT в ветке, чтобы она была различима.   -  person vikingsteve    schedule 17.08.2016


Ответы (4)


Как насчет следующего подхода:

  • Используйте buildnumber-maven-plugin, чтобы получить информацию из git и заполнить определенные свойства Maven (нас конкретно интересует в свойстве scmBranch (то есть текущей ветке git)
  • Используйте build-helper-maven-plugin, чтобы проверить, находимся ли мы в функциональной ветке или нет (через a регулярное выражение, за исключением известных ветвей, таких как master, develop и т. д.) и заполните (или нет) новое свойство Maven, скажем, branch.classifier
  • Используйте maven-jar-plugin, чтобы установить классификатор для сгенерированных артефактов на основе того, что установлен предыдущий шаг, то есть с использованием нового свойства branch.classifier: если оно пусто, классификатор не применяется (поведение по умолчанию, например, применяется к ветке develop); в противном случае будет динамически применяться классификатор, названный в честь текущей ветви.

Вот минимальный пример:

<build>
    <plugins>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>buildnumber-maven-plugin</artifactId>
            <version>1.4</version>
            <executions>
                <execution>
                    <phase>validate</phase>
                    <goals>
                        <goal>create</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>build-helper-maven-plugin</artifactId>
            <version>1.10</version>
            <executions>
                <execution>
                    <id>regex-property</id>
                    <goals>
                        <goal>regex-property</goal>
                    </goals>
                    <configuration>
                        <name>branch.classifier</name>
                        <value>${scmBranch}</value>
                        <regex>(^develop)|(^master)|(^release.*)</regex>
                        <replacement></replacement>
                        <failIfNoMatch>false</failIfNoMatch>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.0.2</version>
            <configuration>
                <classifier>${branch.classifier}</classifier>
            </configuration>
        </plugin>
    </plugins>
</build>

Фрагмент ниже в основном динамически заполняет свойство scmBranch, затем устанавливает branch.classifier его значение, только если оно отличается от develop, master или release*, а затем устанавливает его как классификатор.

Основные преимущества этого подхода:

  • Никакие изменения pom не будут применены, поэтому проблем со слиянием не возникнет.
  • Никаких конфликтов на Nexus, работающих над одной и той же версией проекта, но в разных ветках: классифицированный артефакт будет иметь разные Координаты Maven, то есть GAV (groupId, ArtiftId, версия) становится уникальным GAVC (+классификатор)
  • На самом деле это осмысленное использование атрибута classifier артефакта:
    #P5#
  • Сгенерированный артефакт будет динамически отличаться в Nexus в соответствии с его исходной ветвью, следовательно, будет иметь неявную прослеживаемость: никакого вмешательства со стороны разработчиков (не подвержено ошибкам, неявное соглашение), никакого вмешательства со стороны задания CI (более простое обслуживание), полностью прозрачного
  • Используя классификаторы, будет проще использовать артефакты, сгенерированные веткой, в качестве зависимости от maven (например, в случае проекта библиотеки): я хочу использовать зависимость, которая в настоящее время разрабатывается в ветке xxx

Примеры
Таким образом, будут созданы следующие артефакты:

  • При работе с develop: например. project-1.0.0-SNAPSHOT.jar (пустой классификатор, поэтому не применяется, так как обрабатывается регулярным выражением)
  • При работе с featureA: например. project-1.0.0-SNAPSHOT-featureA.jar
  • При работе с hotfix-JIRA123: например. project-1.0.0-hotfix-JIRA123.jar
  • При работе над release-sprint42: это зависит от вас, я добавил этот случай, чтобы не применять имя ветки, просто потому, что в этих случаях я предпочитаю специально устанавливать специальный классификатор RC<number> для релиз-кандидатов, но это вопрос условностей/вкуса/ привычки, вы можете применить тот же подход и к этой ветке, если на Nexus не возникнет коллизий. Также обратите внимание: при использовании интеграции JIRA/Stash/Git имя ветки релиза обычно имеет вид release/v0.1.0, где символ / может вызывать проблемы в некоторых ОС (хотя это все еще можно исправить с помощью дальнейшей замены регулярных выражений, если это действительно необходимо).
  • При работе на master: эй, никто не должен работать на master :) дело там просто как перепроверка, но это на самом деле не требуется

Предупреждения относительно этого подхода:

  • Как поясняется в приведенном ниже обсуждении в комментариях, если соответствующий проект Maven уже использует классификаторы и, тем более, межмодульные зависимости (например, зависимости от классов области тестирования из другого модуля), то этот подход следует тщательно протестировать, поскольку он может иметь некоторые недостатки
  • Публикация файлов <artifactId>.pom, содержащих классификатор ветвей, может конфликтовать с основной сборкой (т.е. переопределять ее)
person A_Di-Matteo    schedule 03.07.2016
comment
благодаря. это очень круто. Я поэкспериментирую с небольшим проектом и посмотрю, что я могу сделать. Сложные моменты будут в разделе Tycho eclipse RCP моей сборки, но я, вероятно, смогу использовать плагин ресурсов для обработки замены тегов на свойства в этих файлах. - person Peter Kahn; 05.07.2016
comment
@PeterKahn FYI: в компании, где я работаю, мы на самом деле используем его прямо сейчас в нашем конвейере доставки и отлично работает, никаких конфликтов в Nexus, и разработчики также могут импортировать в качестве зависимостей maven артефакты, сгенерированные определенной веткой (редкий случай, но теперь возможно) без необходимости изменения версии проекта и проблем с его слиянием. - person A_Di-Matteo; 18.07.2016
comment
Как насчет источников, тестовых источников, javadoc и других классификаторов, которые обычно используют определенное имя? Продолжают ли они работать в командной строке и в IDE? Я ожидаю, что источники и тестовые источники не будут работать и будут извлекать/использовать значения по умолчанию для версии, а не для конкретной ветки. - person Peter Kahn; 06.02.2017
comment
@PeterKahn действительно, используя пример конфигурации выше, применит описанное вами поведение, но вы также можете настроить maven-source-plugin для использования classifier вместо значения по умолчанию sources, например, sources${branch.classifier}. - person A_Di-Matteo; 06.02.2017
comment
верно, но тогда затмение не сломается? Или он получит изменение через m2eclipse? - person Peter Kahn; 07.02.2017
comment
@PeterKahn хорошее замечание, я не проверял его, но я думаю, что это действительно не разрешит его должным образом. - person A_Di-Matteo; 08.02.2017
comment
A_Di-Matteo Кажется, я понял, смотрите ответ ниже. Пытался @ вы, но это не сработало, как ожидалось - person Peter Kahn; 08.02.2017
comment
Это приводит к тому, что конкурирующие помпы помещаются в локальные репозитории и репозитории нексуса/артефактора. Основная ветка без BranchSpecificClassifiers, featureBranch с. Следовательно, если сборка FB выполняется последней, то все разработки в команде загружают свои pom-файлы с фиктивными зависимостями. Я не думаю, что это вообще работает. Кажется, нет никакого способа достичь цели iv maven. Похоже, что сделать git умным слиянием pom или генерировать pom на лету — единственные варианты. - person Peter Kahn; 09.02.2017
comment
Почему с фиктивными зависимостями? Что ты имеешь в виду? Этот подход используется одной командой в течение одного года без каких-либо проблем и работает нормально. - person A_Di-Matteo; 09.02.2017
comment
Во время сборки любого модуля mvn публикует артефакт ID.pom, содержащий зависимости. При таком подходе сборки основной ветки и функциональной ветки публикуют одни и те же файлы ArtiftId.pom, содержащие конкурирующие данные для зависимостей. Это приводит к неудачным сборкам для обоих, поскольку они используют одно и то же определение требуемых зависимостей. Таким образом, если функциональная ветвь собирается последней, она публикует помпы с зависимостями, содержащими branchClassifier, которые используются как функциональной ветвью, так и основной веткой. Похоже, что версия или настройка репозитория DistributionMng для каждой ветки являются только разделителями. - person Peter Kahn; 09.02.2017
comment
Но почему у зависимостей тоже есть классификаторы? Зависимости не должны иметь классификаторы, если вы строите из родительского pom (в любом случае рекомендуемый подход). Похоже, вы пошли дальше, но теперь обвиняете первоначальный простой подход. Я все равно буду тестировать этот подход, спасибо за эти ценные отзывы! - person A_Di-Matteo; 09.02.2017
comment
Вот почему Часть I: У нас есть много настраиваемых классификаторов (dist - для tar, содержащего дистрибутив с библиотеками и дополнительными файлами, doc - содержащий документы для модуля). Наш установщик зависит от этих артефактов dist и doc для распаковки зависимостей и последующей сборки установщика. В настоящее время мы перечисляем их в нашем разделе управления зависимостями и в pom. - person Peter Kahn; 10.02.2017
comment
Вот почему часть II: у нас также есть модули, зависящие от тестов классификатора из других модулей. Итак, в этом случае я должен перечислить тест области зависимости, тесты классификатора, чтобы второй модуль мог использовать служебные методы из первого. Я думаю, мы могли бы провести рефакторинг, чтобы перенести тестовые утилиты в их собственный модуль и разделить каждый модуль, чтобы создать только один артефакт — перенос dist и doc в отдельные модули. Это может позволить этому подходу работать. - person Peter Kahn; 10.02.2017
comment
Я думаю, мы нашли другой способ, требующий меньшего количества изменений pom. Мы планируем использовать плагины и промежуточные репозитории нексуса, чтобы изолировать сборку, предоставив ей разные maven.local.repo и удаленные репозитории для каждой ветки в зависимости от имени ветки. Используя что-то вроде этого github.com/egineering-llc/gitflow-helper-maven- плагин. Я добавлю ответ на этот вопрос, когда закончу требования/дизайн/тестирование - person Peter Kahn; 10.02.2017
comment
@PeterKahn, насколько я понимаю из вашего объяснения, у вас действительно были дополнительные требования и уровни сложности, не описанные в первоначальных вопросах и, конечно, не учтенные в первоначальном ответе, поэтому я нашел ваш голос немного спорным, но Я не собираюсь троллить по этому поводу, в любом случае это не проблема. Что касается потенциального решения, пожалуйста, дайте мне знать об этом, так как я действительно заинтересован в этом. Спасибо! - person A_Di-Matteo; 12.02.2017
comment
Хорошая точка зрения. На самом деле нейтральный голос за меня. Согласен, здесь гораздо больше сложностей. Я опубликую свой прикрепленный файл, когда я соревновался. Мне нужно написать плагин для взаимодействия с Nexus и создания репозиториев, когда это необходимо. Так что через недельку что-нибудь выложу. - person Peter Kahn; 13.02.2017
comment
Любопытно узнать, как maven-jar-plugin будет работать в сочетании с maven-deploy-plugin. Плагин Deploy, похоже, игнорирует свойство branch.classifier. - person Mark; 27.12.2018

Это не работает. Приводит к появлению предупреждений по всей сборке и ошибке GC при запуске из верхнего родителя.

В идеале мы хотим использовать версию, чтобы отличать ветку функций от основной ветки, потому что это обычный способ maven, а манипуляции с классификатором могут привести к всевозможным проблемам.

Поскольку maven может использовать переменные среды для свойств, и мы уже инициализируем среду сборки с помощью сценария (у нас также есть сценарии git hook, которые могут устанавливать переменные среды из имен веток), мы можем использовать env для управления версией.

<groupID>my.project</groupId>
<artifactID>database</artifactId>
<version>1.2.0${env.BRANCHMODIFIER}-SNAPSHOT</version>

Если при разработке наших скриптов установите для BRANCHMODIFIER значение ""
Если для feature/JIRA-30495 наши скрипты установите для BRANCHMODIFIER значение ".30495"

Как это работает в eclipse или Intellij? Пока нет подсказки.

person Peter Kahn    schedule 08.02.2017
comment
A_Di-Matteo наше сложное использование классификаторов быстро превратилось в беспорядок. Использование #nexus #staging привело к локальным репозиториям с тем, что выглядело как артефакт разработки, но таковым не являлось. Добавление модификатора ArtifactId также было запутанным. Манипулирование версией пока кажется самым чистым подходом. - person Peter Kahn; 08.02.2017
comment
Этот вид работает, но приводит к предупреждениям и проблемам с кучей при запуске из вершины дерева. - person Peter Kahn; 09.02.2017
comment
Этот подход может вызвать проблемы при выполнении сборок с локальных машин, верно? - person A_Di-Matteo; 09.02.2017

Мы используем ту же технику, что и Питер Кан, изменяя версию ветки перед сборкой. У нас есть три шага в наших «Предварительных шагах»:

  1. Выполнить оболочку: echo VERSION=$(echo ${GIT_BRANCH} | sed 's_^.*\/__') > env.properties
  2. Внедрить переменные среды: env.properties
  3. Вызвать цели Maven верхнего уровня: versions:set -DgenerateBackupPoms=false -DnewVersion=${VERSION}-SNAPSHOT

Я совершенно уверен, что это можно сделать и с двумя или даже с одним-единственным шагом, но принцип за этим будет тот же.

Причина, по которой мы не меняем версию в файлах pom.xml в ветке напрямую, действительно заключается в слиянии. С SVN это было возможно (слияние с --accept-mine-conflict. С GIT этого больше не существует, поэтому мы перестали менять версии и создали эти шаги перед сборкой.

person Paul Wellner Bou    schedule 29.09.2017

Для Maven ge 3.5.0 попробуйте это https://maven.apache.org/maven-ci-friendly.html Это рекомендуемое решение Maven. Единственная проблема (но необычная) может заключаться в разрешении числовых версий зависимостей maven. Но это появляется только в том случае, если вы используете разные зависимости SNAPSHOT модуля, что в любом случае является плохой идеей.

person babelfisk    schedule 01.05.2020