Как получить версию проекта Maven в командной строке bash

Ранее я выпустил вопрос о том, как изменить версию проекта Maven из командной строки, что привело меня к новой проблеме.

Ранее я был в состоянии получить номер версии, так как версия была сохранена как свойство, которое было легко найти и проанализировать из командной строки (bash). Теперь, когда для этого используется элемент pom.xml, он больше не является уникальным, поскольку все зависимости и, возможно, некоторые другие тоже используют это. Я думаю, что нет способа получить номер текущей версии с помощью bash-скрипта без внешних инструментов для разбора xml или какой-либо очень контекстно-зависимой команды sed.

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

Итак, есть ли простой способ получить значение ${project.version} в командной строке? Заранее спасибо.

Решение

Спасибо вам за помощь. Мне пришлось cd в каталог вручную, но это можно сделать легко. В моем скрипте Bash у меня есть

version=`cd $project_loc && mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | sed -n -e '/^\[.*\]/ !{ /^[0-9]/ { p; q } }'`

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

# Advances the last number of the given version string by one.
function advance_version () {
    local v=$1
    # Get the last number. First remove any suffixes (such as '-SNAPSHOT').
    local cleaned=`echo $v | sed -e 's/[^0-9][^0-9]*$//'`
    local last_num=`echo $cleaned | sed -e 's/[0-9]*\.//g'`
    local next_num=$(($last_num+1))
    # Finally replace the last number in version string with the new one.
    echo $v | sed -e "s/[0-9][0-9]*\([^0-9]*\)$/$next_num/"
}

И я использую это, просто позвонив

new_version=$(advance_version $version)

Надеюсь, это кому-нибудь поможет.

34 ответа

Решение

Плагин помощи Maven как-то уже предлагает что-то для этого:

  • help:evaluate оценивает выражения Maven, заданные пользователем в интерактивном режиме.

Вот как вы можете вызвать его в командной строке, чтобы получить ${project.version}:

mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
    -Dexpression=project.version

Решение Тома с плагином Exec Maven гораздо лучше, но все же сложнее, чем нужно. Для меня это так просто, как:

MVN_VERSION=$(mvn -q \
    -Dexec.executable=echo \
    -Dexec.args='${project.version}' \
    --non-recursive \
    exec:exec)

Я провел некоторые исследования и обнаружил следующее:

  1. Maven обвиняют в том, что он не может быть легко интегрирован в сценарии работы системы, поскольку он не следует некоторым рекомендациям относительно инструментов CLI. (ссылка: https://youtu.be/1ILEw6Qca3U?t=372)

  2. Вдохновленный предыдущим утверждением, я решил взглянуть на исходный код maven, а также на плагин maven-help-plugin. Кажется, что они немного исправили ключ -q maven (я использую версию 3.5.3), так что теперь, если вы пропустите его, вы не получите все надоедливые логи, которые не позволяют использовать maven. в рамках автоматизированных сценариев. Таким образом, вы должны быть в состоянии использовать что-то вроде этого:

    mvn help:evaluate -Dexpression=project.version -q
    

    Проблема в том, что эта команда ничего не печатает, потому что по умолчанию плагин справки выводится через регистратор, который был отключен ключом -q. (последняя доступная версия плагина - 3.1.0, выпущенная 3 июня 2018 г.)

  3. Карл Хайнц Марбез ( https://github.com/khmarbaise) исправил это, добавив необязательный параметр, который позволяет вызывать его следующим образом:

    mvn help:evaluate -Dexpression=project.version -q -DforceStdout
    

    Описание фиксации доступно по адресу: ( https://github.com/apache/maven-help-plugin/commit/316656983d780c04031bbadd97d4ab245c84d014)

Это самое чистое решение, которое я нашел:

mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout

Преимущества:

  • Это отлично работает на всех операционных системах и всех оболочек.
  • Никаких внешних инструментов!
  • [важно] Это работает, даже если версия проекта унаследована от родительской pom.xml

Заметка:

  • maven-help-plugin версия 3.2.0 (и выше) имеет forceStdoutвариант. Вы можете заменить3.2.0в приведенной выше команде с более новой версией из списка доступных версий mvn-help-plugin из artifactory, если таковая имеется.
  • Вариант -q подавляет подробные сообщения ([INFO], [WARN] так далее.)

Если вы хотите получить groupId а также artifactIdа также проверьте этот ответ.

Верхний ответ, по моему мнению, довольно мусор, вы должны использовать кучу grep, чтобы взломать вывод консоли maven. Почему бы не использовать правильный инструмент для работы? Использование синтаксиса xpath - лучший подход к получению номера версии, поскольку это предполагаемый метод доступа к структуре данных XML. Выражение ниже пересекает pom, используя "локальное имя" элементов, другими словами, игнорируя объявления пространства имен, которые могут присутствовать или не присутствовать в xml.

xmllint --xpath "//*[local-name()='project']/*[local-name()='version']/text()" pom.xml
mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\['

Это позволит избежать необходимости извлечения записей журнала из выходных данных:

mvn -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive exec:exec -q
python -c "import xml.etree.ElementTree as ET; \
  print(ET.parse(open('pom.xml')).getroot().find( \
  '{http://maven.apache.org/POM/4.0.0}version').text)"

Пока у вас есть Python 2.5 или выше, это должно работать. Если у вас более низкая версия, установите python-lxml и измените импорт на lxml.etree. Этот метод быстрый и не требует загрузки дополнительных плагинов. Он также работает с искаженными файлами pom.xml, которые не проверяются с помощью xmllint, например, те, которые мне нужно проанализировать. Проверено на Mac и Linux.

Я продолжал сталкиваться с дополнительными случаями, когда использовал некоторые другие ответы здесь, так что вот еще одна альтернатива.

version=$(printf 'VER\t${project.version}' | mvn help:evaluate | grep '^VER' | cut -f2)

Существует также один вариант без необходимости Maven:

grep -oPm1 "(?<=<version>)[^<]+" "pom.xml"

Это, безусловно, самое простое решение для вырезания и вставки в bash:

VERSION=$(mvn -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive exec:exec -q)
echo $VERSION

это перекликается

1.4

Если вы не возражаете записать версию во временный файл, есть другое решение (без grep/sed), которое мне подходит. (РЕДАКТИРОВАТЬ: см . Ответ Rjrjr для гораздо более простого решения без каких-либо временных файловых хлопот)

Я использую плагин Exec Maven вместе с echo двоичный файл. В отличие от плагина справки Maven, плагин Exec позволяет перенаправлять вывод в файл, который можно использовать для обхода grep/sed, и даже позволяет анализировать странные вещи, такие как строки многострочных версий (с блоком CDATA в теге версии), по крайней мере, в определенной степени.

#!/usr/bin/env sh

MVN_VERSION=""
VERSION_FILE=$( mktemp mvn_project_version_XXXXX )
trap "rm -f -- \"$VERSION_FILE\"" INT EXIT

mvn -Dexec.executable="echo" \
    -Dexec.args='${project.version}' \
    -Dexec.outputFile="$VERSION_FILE" \
    --non-recursive \
    --batch-mode \
    org.codehaus.mojo:exec-maven-plugin:1.3.1:exec > /dev/null 2>&1 ||
    { echo "Maven invocation failed!" 1>&2; exit 1; }

# if you just care about the first line of the version, which will be
# sufficent for pretty much every use case I can imagine, you can use
# the read builtin
[ -s "$VERSION_FILE" ] && read -r MVN_VERSION < "$VERSION_FILE"

# Otherwise, you could use cat.
# Note that this still has issues when there are leading whitespaces
# in the multiline version string
#MVN_VERSION=$( cat "$VERSION_FILE" )

printf "Maven project version: %s\n" "$MVN_VERSION"

Просто для записи, можно настроить ведение журнала Simple SLF4J в Maven непосредственно в командной строке для вывода только того, что нам нужно, с помощью настройки:

  • org.slf4j.simpleLogger.defaultLogLevel=WARN а также
  • org.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO

как указано на http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html

MAVEN_OPTS="\
-Dorg.slf4j.simpleLogger.defaultLogLevel=WARN \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO" \
mvn help:evaluate -o -Dexpression=project.version

В результате можно просто запустить tail -1 и получить:

$ MAVEN_OPTS="\
-Dorg.slf4j.simpleLogger.defaultLogLevel=WARN \
-Dorg.slf4j.simpleLogger.log.org.apache.maven.plugins.help=INFO" \
mvn help:evaluate -o -Dexpression=project.version | tail -1

1.0.0-SNAPSHOT

Обратите внимание, что это однострочник. MAVEN_OPTS переписываются только для этого конкретного mvn выполнение.

Простое решение только для Maven

mvn -q -N org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
    -Dexec.executable='echo' \
    -Dexec.args='${project.version}'

А за бонусные баллы разбирается часть версии

mvn -q -N org.codehaus.mojo:build-helper-maven-plugin:3.0.0:parse-version \
    org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
    -Dexec.executable='echo' \
    -Dexec.args='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.incrementalVersion}'

Я заметил некоторые поддельные Downloaded: строки, выходящие на выходе, которые нарушали мое первоначальное назначение. Вот фильтр, на котором я остановился; Надеюсь, поможет!

version=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | egrep -v '^\[|Downloading:' | tr -d ' \n')

РЕДАКТИРОВАТЬ

Не уверен на 100%, почему, но при запуске этого сценария в Jenkins после сборки он получал [INFO]versionнапример, [INFO]0.3.2,

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

Чтобы получить 100% в Дженкинс, я добавил продолжение sed фильтр; вот мой последний

version=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | egrep -v '^\[|Downloading:' | tr -d ' \n' | sed -E 's/\[.*\]//g')

РЕДАКТИРОВАТЬ

Последнее замечание здесь.. Я узнал tr все еще приводил к таким вещам, как /r/n0.3.2 (опять только при запуске через Дженкинс). Перешли на awk и проблема ушла! Мой окончательный рабочий результат

mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version \
| egrep -v '^\[|Downloading:' | sed 's/[^0-9\.]//g' | awk 1 ORS=''

Недавно я разработал плагин Release Candidate Maven, который решает эту проблему, чтобы вам не приходилось прибегать к каким-либо хакерским сценариям оболочки и анализировать выходные данные maven-help-plugin,

Например, чтобы распечатать версию вашего проекта Maven в терминале, запустите:

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version

который дает вывод, аналогичный maven-help-plugin:

[INFO] Detected version: '1.0.0-SNAPSHOT'
1.0.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Однако вы также можете указать произвольный формат вывода (чтобы версия могла быть выбрана из журнала сервером CI, таким как TeamCity):

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version \
   -DoutputTemplate="##teamcity[setParameter name='env.PROJECT_VERSION' value='{{ version }}']"

Что приводит к:

[INFO] Detected version: '1.0.0-SNAPSHOT'
##teamcity[setParameter name='env.PROJECT_VERSION' value='1.0.0-SNAPSHOT']
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Чтобы сохранить выходные данные в файл (чтобы его мог использовать CI-сервер, такой как Jenkins):

mvn com.smartcodeltd:release-candidate-maven-plugin:LATEST:version \
   -DoutputTemplate="PROJECT_VERSION={{ version }}" \
   -DoutputUri="file://\${project.basedir}/version.properties"

Результирующий version.properties Файл будет выглядеть следующим образом:

PROJECT_VERSION=1.0.0-SNAPSHOT

Помимо всего вышеперечисленного, Release Candidate также позволяет вам установить версию вашего проекта (что вы, вероятно, будете делать на своем CI-сервере) на основе версии API, которую вы определили в своем POM.

Если вы хотите увидеть пример использования Release Candidate как части жизненного цикла Maven, взгляните на pom.xml моего другого проекта с открытым исходным кодом - Build Monitor for Jenkins.

Простое в понимании решение "все в одном", которое выводит версию проекта maven и подавляет посторонний вывод [INFO] а также Download Сообщения:

mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\['

То же самое, но разбить на две строки:

mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
      -Dexpression=project.version | grep -v '\['

Выходы: 4.3-SNAPSHOT

Итак, используя ваш project.version в простом скрипте bash:

projectVersion=`mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\['`
cd "target/"$projectVersion"-build"

Другие решения на этой странице, похоже, не объединяют все уловки в одно.

Я нашел правильный баланс для меня. После mvn package плагин maven-архиватора создает target/maven-archiver/pom.properties с таким содержанием

version=0.0.1-SNAPSHOT
groupId=somegroup
artifactId=someArtifact

и я использую Bash только для его выполнения

. ./target/maven-archiver/pom.properties

затем

echo $version
0.0.1-SNAPSHOT

Конечно, запуск этого файла вовсе не безопасен, но его можно легко преобразовать в сценарий perl или bash для чтения и установки переменной окружения из этого файла.

Это работало для меня, в автономном режиме и вне зависимости от mvn:

VERSION=$(grep --max-count=1 '<version>' <your_path>/pom.xml | awk -F '>' '{ print $2 }' | awk -F '<' '{ print $1 }')
echo $VERSION

Должно быть проще, так как эта ошибка исправлена ​​в maven-help-plugin 3.0.0: MPH-99 Evaluate не выводит в тихом режиме.

Добавьте следующий плагин в свой pom.xml

          ...
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-help-plugin</artifactId>
        <version>3.2.0</version>
        <executions>
            <execution>
                <id>generate-version-file</id>
                <phase>prepare-package</phase>
                <goals>
                    <goal>evaluate</goal>
                </goals>
                <configuration>
                    <expression>project.version</expression>
                    <output>${project.build.directory}/version.txt</output>
                </configuration>
            <execution>
        </executions>
    </plugin>
    ...

Это создаст target/version.txt файл как обычная часть вашей сборки.

Все, что вам нужно сделать, чтобы передать это в оболочку, это следующее:

      #!/bin/sh
value=`cat target/version.txt`
echo "$value"
      #!/bin/bash
value=$(<target/version.txt)
echo "$value"

Либо у вас есть mvn дать вам ответ (как предлагает большинство ответов), или вы извлекаете ответ из pom.xml, Единственным недостатком второго подхода является то, что вы можете очень легко извлечь значение <version/> тег, но он будет иметь смысл только в том случае, если он буквальный, то есть не является свойством Maven. Я все равно выбрал этот подход, потому что:

  • mvn это способ многословия, и я просто не люблю фильтровать его вывод.
  • начало mvn очень медленно по сравнению с чтением pom.xml,
  • Я всегда использую буквальные значения в <version/>,

mvn-version это zsh сценарий оболочки, который использует xmlstarlet читать pom.xml и распечатайте версию проекта (если он существует) или версию родительского проекта (если он существует):

$ mvn-version .
1.0.0-SNAPSHOT

Преимущество в том, что это намного быстрее, чем бег mvn:

$ time mvn-version .
1.1.0-SNAPSHOT
mvn-version .  0.01s user 0.01s system 75% cpu 0.019 total

$ time mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
> -Dexpression=project.version
mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate   4.17s user 0.21s system 240% cpu 1.823 total

Разница на моей машине больше двух порядков.

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

#!/usr/bin/env bash  

# Advances the last number of the given version string by one.  
function advance\_version () {  
    local v=$1  
    \# Get the last number. First remove any suffixes (such as '-SNAPSHOT').  
  local cleaned=\`echo $v | sed \-e 's/\[^0-9\]\[^0-9\]\*$//'\`  
 local last\_num=\`echo $cleaned | sed \-e 's/\[0-9\]\*\\.//g'\`  
 local next\_num=$(($last\_num+1))  
  \# Finally replace the last number in version string with the new one.  
  echo $v | sed \-e "s/\[0-9\]\[0-9\]\*\\(\[^0-9\]\*\\)$/$next\_num/"  
}  

version=$(mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate -Dexpression=project.version -q -DforceStdout)  

new\_version=$(advance\_version $version)  

mvn versions:set -DnewVersion=${new\_version} -DprocessAllModules -DgenerateBackupPoms=false

Плагин Exec работает без разбора вывода, потому что вывод может быть перенаправлен в файл и вставлен обратно в рабочую среду через плагин EnvInject:

Самый быстрый способ получить только текст без обрезки

      mvn help:evaluate -q -DforceStdout -D"expression=project.version"

Альтернативой может быть синтаксический анализ с помощью yq (https://github.com/kislyuk/yq) следующим образом:

cat pom.xml | xq -r '.project.version'

Обратите внимание, что исполняемый файл - это xq, а не yq

Чтобы получить xq, установите yq вот так pip install yq

В контейнерах докеров, где доступен только busybox, я использую awk.

      version=$(
 awk '
  /<dependenc/{exit}
  /<parent>/{parent++};
  /<version>/{
    if (parent == 1) {
      sub(/.*<version>/, "");
      sub(/<.*/, "");
      parent_version = $0;
    } else {
      sub(/.*<version>/, "");
      sub(/<.*/, "");
      version = $0;
      exit
    }
  }
  /<\/parent>/{parent--};
  END {
    print (version == "") ? parent_version : version
  }' pom.xml
)

Примечания:

  • Если версия артефакта не указана, вместо нее печатается родительская версия.
  • Зависимости должны идти после артефакта и родительской версии. (Это можно легко преодолеть, но мне это не нужно.)

Мне нужно именно это требование во время моей работы в Трэвисе, но с несколькими значениями. Я начинаю с этого решения, но при многократном вызове это очень медленно (мне нужно 5 выражений).

Я написал простой плагин maven для извлечения значений pom.xml в файл.sh.

https://github.com/famaridon/ci-tools-maven-plugin

mvn com.famaridon:ci-tools-maven-plugin:0.0.1-SNAPSHOT:environment -Dexpressions=project.artifactId,project.version,project.groupId,project.build.sourceEncoding

Произведем это:

#!/usr/bin/env bash
CI_TOOLS_PROJECT_ARTIFACTID='ci-tools-maven-plugin';
CI_TOOLS_PROJECT_VERSION='0.0.1-SNAPSHOT';
CI_TOOLS_PROJECT_GROUPID='com.famaridon';
CI_TOOLS_PROJECT_BUILD_SOURCEENCODING='UTF-8';

теперь вы можете просто указать источник файла

source .target/ci-tools-env.sh

Радоваться, веселиться.

mvn help:evaluate -Dexpression=project.version | sed -e 1h -e '2,3{H;g}' -e '/\[INFO\] BUILD SUCCESS/ q' -e '1,2d' -e '{N;D}' | sed -e '1q'

Я просто добавляю маленький sed Улучшение фильтра, которое я недавно реализовал для извлечения project.version из Maven выхода.

      VERSION=$(mvn -version | grep 'Apache Maven' | grep -o '[0-9].[0-9].[0-9]')
echo $VERSION
Другие вопросы по тегам