以前我在 how to change Maven project vesion from command line 上发布了一个问题,这导致我遇到了一个新问题。
以前我能够获得版本号,因为版本被存储为一个易于从命令行(bash)进行grep和解析的属性。现在 pom.xml <version>
元素已用于此,它不再是唯一的,因为所有依赖项以及可能其他一些依赖项也使用它。我认为没有办法使用 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 } }'`
这给了我当前版本,然后我可以推进。 Grepping 可能更简单,但我认为我希望尽可能健壮,所以我对以数字开头的第一行感到满意,并尝试将其作为版本号处理。
# 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)
grep -e '^[[:digit:]]'
Maven Help Plugin 已经为此提出了一些建议:
help:evaluate 以交互模式计算用户给出的 Maven 表达式。
以下是在命令行中调用它以获取 ${project.version}
的方法:
mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate \
-Dexpression=project.version
作为 Seb T 的 noted in the comments,要仅打印没有 maven INFO 日志的版本,另外使用 -q -DforceStdout
:
mvn help:evaluate -Dexpression=project.version -q -DforceStdout
Tom 的 Exec Maven Plugin 解决方案要好得多,但仍然比需要的复杂。对我来说,这很简单:
MVN_VERSION=$(mvn -q \
-Dexec.executable=echo \
-Dexec.args='${project.version}' \
--non-recursive \
exec:exec)
mvn -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec
-Dexec.args='${project.groupId}:${project.artifactId}:${project.version}'
。
[ERROR] Failed to execute goal org.codehaus.mojo:exec-maven-plugin:1.3.2:exec (default-cli) on project audit-events-processor-parent: Command execution failed. Cannot run program "maven" (in directory "/tmp"): error=2, No such file or directory
shrug 又一个对我不起作用的答案,哦,好吧
set -o errexit
在做了一些研究后,我发现了以下内容:
Maven 受到指责是因为与 DevOps 工具的集成并不容易,因为它没有遵循有关 CLI 工具的一些良好实践,即:
http://www.faqs.org/docs/artu/ch01s06.html(不再可用)“Unix 方式”(ii)期望每个程序的输出成为另一个尚不为人知的程序的输入。不要用无关信息使输出混乱。避免严格的列式或二进制输入格式。不要坚持交互式输入。它实际上是什么意思?您的输出应该是:“grepable”:(每行一个“记录”)“cutable”:(分隔的“字段”)退出代码:0 表示成功,非零表示失败。消息传递(stderr)与输出(stdout)
(参考:Dave Copeland 用 ruby 制作很棒的命令行应用程序,2012 年 1 月 18 日https://youtu.be/1ILEw6Qca3U?t=372)
诚实我认为 Dave Copeland 说 maven 不公平对待他人时是对的。所以我决定看看 maven 的源代码以及 maven-help-plugin 的源代码。似乎他们已经修复了一些 maven 的 -q 开关(当时我使用的是 3.5.3 版),所以现在如果你通过它,你将不会得到所有阻止 maven 的烦人的无意义日志记录的东西从在自动化脚本中使用。所以你应该能够使用这样的东西: mvn help:evaluate -Dexpression=project.version -q
问题是这个命令没有打印任何内容,因为默认情况下,帮助插件通过 -q 开关静音的记录器输出。 (当时该插件的最新可用版本是 2018 年 6 月 3 日发布的 3.1.0)
Karl Heinz Marbaise (https://github.com/khmarbaise) 通过添加一个允许您以下列方式调用它的可选参数来修复它: mvn help:evaluate -Dexpression=project.version -q -DforceStdout
提交说明位于:(https://github.com/apache/maven-help-plugin/commit/316656983d780c04031bbadd97d4ab245c84d014)
同样,您应该始终验证命令的退出代码并将所有 stderr 重定向到 unix 上的 /dev/null。
-q
开关时,它会正确打印出版本(在日志行之间)。有任何想法吗?
-q
开关配合使用。
-q -DforceStdout
的输出是空的,即使确保插件的3.1.0版本与pluginManagement一起使用);我用 3.5.4 版的 maven 配置了 maven 包装器,它工作正常
mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\['
2> /dev/null
,否则您可以获得 Picked up _JAVA_OPTIONS:
| findstr /v "["
。
这是最干净的解决方案:
mvn org.apache.maven.plugins:maven-help-plugin:3.2.0:evaluate \
-Dexpression=project.version -q -DforceStdout
优点:
这适用于所有操作系统和所有 shell。
无需任何外部工具!
[重要] 即使项目版本是从父 pom.xml 继承的,这也有效
笔记:
maven-help-plugin 版本 3.2.0(及更高版本)具有 forceStdout 选项。如果可用,您可以将上述命令中的 3.2.0 替换为来自 artifactory 的 mvn-help-plugin 可用版本列表中的较新版本。
选项 -q 抑制详细消息([INFO]、[WARN] 等)
或者,您可以在 plugins
部分下的 pom.xml
中添加此条目:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-help-plugin</artifactId>
<version>3.2.0</version>
</plugin>
然后紧凑地运行上面的命令,如下所示:
mvn help:evaluate -Dexpression=project.groupId -q -DforceStdout
如果您还想获取 groupId
和 artifactId
,请选中 this answer。
在我看来,最重要的答案是相当垃圾,你必须使用一堆 grep 来破解 maven 控制台输出。为什么不使用正确的工具来完成这项工作?使用 xpath 语法是检索版本号的最佳方法,因为它是访问 XML 数据结构的预期方法。下面的表达式使用元素的“本地名称”遍历 pom,换句话说,忽略了 xml 中可能存在或不存在的命名空间声明。
xmllint --xpath "//*[local-name()='project']/*[local-name()='version']/text()" pom.xml
mvn
是正确的方法,exec
插件实际上工作得很好,但所有 mvn 解决方案都必须解决,我正在尝试引导一个构建管道,其中依赖解决方案不起作用,所以我要替换此解决方案的 exec
方法。幸运的是,我不必担心从父母那里继承的版本。
${revision}
之类的属性。参考。 maven.apache.org/maven-ci-friendly.html
这将避免从输出中删除日志条目的需要:
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。这种方法很快,不需要下载任何额外的插件。它也适用于不使用 xmllint 验证的格式错误的 pom.xml 文件,例如我需要解析的文件。在 Mac 和 Linux 上测试。
在这里使用其他一些答案时,我一直遇到一些侧面案例,所以这里还有另一种选择。
version=$(printf 'VER\t${project.version}' | mvn help:evaluate | grep '^VER' | cut -f2)
printf 'VER\t${project.version}' | mvn help:evaluate 2> /dev/null | grep '^VER' | cut -f2
还有一种不需要Maven的选择:
grep -oPm1 "(?<=<version>)[^<]+" "pom.xml"
grep -oPm2 "(?<=<version>)[^<]+" pom.xml | sed -n 2p
为了获得第三次出现使用:grep -oPm3 "(?<=<version>)[^<]+" pom.xml | sed -n 3p
等等
这是迄今为止最简单的 bash 剪切和粘贴解决方案:
VERSION=$(mvn exec:exec -Dexec.executable='echo' -Dexec.args='${project.version}' --non-recursive -q)
echo $VERSION
它呼应
1.4
exec:exec
。当 plugin:goal part exec:exec
部分丢失时发生的错误
如果您不介意将版本写入临时文件,还有另一种解决方案(没有 grep/sed)对我很有效。 (编辑:请参阅 rjrjr's answer 以获得更简单的解决方案,无需任何临时文件麻烦)
我将 Exec Maven Plugin 与 echo
二进制文件一起使用。与 Maven Help Plugin 相比,Exec Plugin 允许将输出重定向到文件中,该文件可用于绕过 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"
只是为了记录,可以直接在命令行中配置Maven的Simple SLF4J日志,通过配置只输出我们需要的:
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
请注意,这是一个单行。仅针对此特定 mvn
执行重写 MAVEN_OPTS
。
我注意到输出中出现了一些虚假的 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 通过我的第一个过滤器运行它,它工作正常..,所以再次不确定 Jenkins 领域发生了什么。
为了在 Jenkins 中实现 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
之类的事情(同样仅在通过 Jenkins 运行时)。切换到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=''
一个简单的 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}'
我最近开发了 Release Candidate Maven 插件来解决这个确切的问题,这样您就不必求助于任何 hacky shell 脚本并解析 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 可以 use it):
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 还允许您根据您定义的 API 版本设置项目的版本(您可能会在 CI 服务器上执行此操作)在你的 POM 中。
如果您想查看将候选版本用作 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
因此,在一个简单的 bash 脚本中使用您的 project.version
:
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-archiver 插件创建 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 has no output in quiet mode 中修复。
Exec 插件无需任何输出解析即可工作,因为可以将输出重定向到文件并通过 EnvInject 插件注入回作业环境:
https://i.stack.imgur.com/kMo2H.png
要么让 mvn
给你答案(正如大多数答案所暗示的那样),要么你从 pom.xml
中提取答案。第二种方法的唯一缺点是您可以很容易地提取 <version/>
标记的值,但只有当它是 literal 时才有意义,即不是 Maven 属性。无论如何,我选择了这种方法,因为:
mvn 是冗长的方式,我只是不喜欢过滤它的输出。
与读取 pom.xml 相比,启动 mvn 非常慢。
我总是在
mvn-version
是一个 zsh
shell 脚本,它使用 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
将以下插件添加到您的 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
文件作为构建的正常部分。
然后,你需要做的就是把它放到 shell 中:
#!/bin/sh
value=`cat target/version.txt`
echo "$value"
#!/bin/bash
value=$(<target/version.txt)
echo "$value"
这是上面的编辑
猫 pom.xml | grep "" |头-n 1 | sed -e "s/版本//g" | sed -e "s/\s*[<>/]*//g"
我在 cmdline 上对其进行了测试,效果很好
grep "" pom.xml |头-n 1 | sed -e "s/版本//g" | sed -e "s/\s*[<>/]*//g"
是相同的另一个版本。我需要在没有安装 mvn 的情况下在 k8s 中获取 Jenkins CI 中的版本号,所以这是最有帮助的
谢谢大家。
我在 Travis 工作期间正是需要这个要求,但有多个值。我从 this solution 开始,但多次调用时速度很慢(我需要 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
玩得开心。
一种替代方法是使用 yq (https://github.com/kislyuk/yq) 解析,如下所示:
cat pom.xml | xq -r '.project.version'
注意可执行文件是 xq 而不是 yq
要获得 xq,请像这样安装 yq pip install yq
在只有busybox可用的docker容器中,我使用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
)
笔记:
如果没有给出工件版本,则打印父版本。
依赖项必须在工件和父版本之后。 (这可以很容易地克服,但我不需要它。)
VERSION=$(mvn -version | grep 'Apache Maven' | grep -o '[0-9].[0-9].[0-9]')
echo $VERSION
让您只获得文本而无需所有修饰的最快方式
mvn help:evaluate -q -DforceStdout -D"expression=project.version"
[INFO]
消息吗?我没有找到maven的开关。否则,我将添加一些命令行脚本来解析版本号。mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version|grep -Ev '(^\[|Download\w+:)'
删除所有日志记录(信息、警告等)和“下载”消息printf 'VERSION=${project.version}\n0\n' | mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate | grep '^VERSION'
mvn help:evaluate -Dexpression=project.version -q -DforceStdout
。要在 Bash 中的变量中捕获它,请使用version=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)