Рано или поздно все разработчики Java решают мелкие задачи в области Continuos Integration. Не обошла эта участь и меня. Озадачился я проблемой автоматического инкремента версий в pom.xml при каждой итерации сборки проекта.

Дано: maven проект с несколькими модулями, мастер pom.xml и Jenkins-сервер (все как у настоящих пацанов).

Нужно: чтобы при каждом коммите автоматически собирался проект в любом бранче, а в ветке develop проект не только собирался, но и инкрементился номер билда, который задан третьим числом в версии вида 1.0.100-SNAPSHOT.

Для автоматической сборки Java-проекта в бранчах у нас используется Jenkins-проект на основе модного нынче Multibranch pipeline.

image

Суть этого workflow — периодически (например, раз в минуту), в Multibranch pipeline запускается задача, которая определяет изменения в бранчах и запускает сборку для тех бранчей, в которых что-то закоммитили. При этом, как у настоящих пацанов, для сборки бранча используется самый настоящий Jenkinsfile. Немного ликбеза: Jenkinsfile — это код на языке Groovy который определяет последовательность и инструкции по сборке проекта. Даже придумали для этого специальный термин «pipeline as code». Казалось, ничего вроде бы сложного нет — через groovy-скрипт инкрементим номер версии, коммитим и запускаем maven-сборку. Но тут нарисовывается главная проблема — как предотвратить последующие (бесконечные) сборки после того, как мы автоматом обновили pom.xml? Да, в Jenkins-плагине под названием 'git' (тот самый, который предназначен для детекта изменений в бранчах) есть даже специальная фича — «Polling ignores commits», но вот незадача — она не работает в Multibranch-pipeline. По этому поводу жаловались многие пользователи и даже завели специальный Jira-айтем. Поэтому — вперед, будем изобретать свой велосипед!

Но к счастью, в git-плагине работает другая фича «Exclude branches». Поэтому заведем специальный бранч, куда будем коммитить только номера билдов при каждой сборке и добавим название этого бранча в исключения (чтобы новые коммиты не вызывали триггеринг новых сборок). Фактически, этот бранч нужен только для того, чтобы хранить одно число, которое указывает на номер билда. Такой бранч не имеет предков и называется «сирота». Для его создания сделаем следующее:

git checkout --orphan develop2
git reset --hard

И разместим в нем файл с именем current.tag, в который запишем номер билда. Ну а далее все за вас сделает Jenkinsfile, исходный текст которого найдете в репозитории на гитхабе.

Не буду больше утомлять вас кодом, вкратце, алгоритм Jenkinsfile такой:

  1. Склонировали проект
  2. Переключились на сиротливый бранч
  3. Прочитали номер последнего билда
  4. Увеличили номер билда
  5. Прикопали номер билда в переменной и записали его в файл в сиротливой бранч
  6. Переключились в основной бранч
  7. Распарсили номер версии из pom.xml
  8. Сгенерировали номер версии на основе версии из pom.xml и номером билда
  9. Проапдейтили версию в мастер pom.xml и всех его модулях при помощи соответствующего maven-плагина
  10. Собрали проект при помощи mvn package

В итоге получаем такую красоту:

image
Поделиться с друзьями
-->

Комментарии (8)


  1. sleeply4cat
    29.11.2016 01:17

    Модифицировать сценарий сборки — это точно хорошая практика?


    1. getaclue
      29.11.2016 10:16

      Немножко не понимаю термин — «модифицированный сценарий сбоки». Сценарий сборки остался без изменений — «mvn package». Просто билдится он фактически из бранча, изменения в котором не коммититятся. Если сильно надо — после mvn versions:set можно создать новый «релиз-кандидат» бранч и билдить артифакты из этого бранча (который добавить в exclude).


  1. sshikov
    29.11.2016 09:50

    Если уж вы собираете Jenkins, и используете специфичные для него вещи типа пайплайнов, то почему тогда не использовать номер билда, который отслеживает сам Jenkins?

    Он внутри pom.xml доступен, может быть записан куда нужно (скажем в манифест jar-а), и успешно использован.


    1. getaclue
      29.11.2016 10:11

      Была такая мысль, но номер билда Jenkins — вещь непостоянная. Да и строчек кода не сильно много сэкономишь. Аппроач, по сути дела один — где-то отдельно от pom-файла хранится номер билда. А где он конкретно хранится — это уже дело вкуса.


      1. sshikov
        29.11.2016 10:25

        Ну, строчек кода будет ровно ноль — jenkins номер кладет в env, откуда его сразу можно брать.

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


        1. mgstr93
          30.11.2016 19:25
          +1

          Иногда дженкинс зависает, Devops где-то нажимает рубильник и сервер востанавливается.
          Но номера билдов начинаются с 1 :)


  1. pashuk
    29.11.2016 20:05

    Я не гуру в java/maven, но даже у меня появился вопрос.


    А можно вместо изменений в файле pom.xml + коммит в git, использовать такую вещь, как git tag.
    В имени этого tag написать ровно ту версию что прописывается в pom.xml.


    Преимущество tag перед commit как раз в том что он никак не меняет историю коммитов.
    С помощью tag можно навесить на commit любую метаинформацию, например, версию.


    А уже во время билда вытаскивать версию через этот git tag
    Можно погуглить команду git describe, она примерно это и делает.


    Как смотрите на такой вариант?


    1. osigida
      29.11.2016 23:27

      Мне тоже про гит хеш подумалось… просто и не надо ничего коммитить.

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