之前我发布了一个关于如何从命令行更改Maven项目版本的问题,这导致我遇到了一个新问题。
以前我能够获得版本号,因为版本存储为一个属性,很容易从命令行(bash)进行grep和解析。现在pom.xml <version>元素用于此,它不再是唯一的,因为所有依赖项(也许其他一些依赖项也使用它)。我认为,如果没有用于解析XML的外部工具或一些非常能感知上下文的sed命令,就无法用bash脚本获得当前版本号。
在我看来,最干净的解决方案是Maven发布这个版本信息。我想写一个自定义的maven插件检索不同的属性,但我想我应该先问这里。
那么,是否有任何简单的方法来获取${project的值。版本}到命令行?
解决方案
我必须手动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)
我注意到一些虚假的下载:行来的输出打破了我原来的任务。这是我选定的滤镜;希望能有所帮助!
version=$(mvn org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | egrep -v '^\[|Downloading:' | tr -d ' \n')
EDIT
不是100%确定为什么,但是当在Jenkins中运行后构建脚本时,输出是[INFO]版本,例如[INFO]0.3.2。
我将输出转储到一个文件,并通过我的第一个过滤器直接从BASH运行,它工作得很好。所以,再次,不确定在詹金斯的土地上发生了什么。
为了在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')
EDIT
这里还有最后一点。我发现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=''
如果您不介意将版本写入临时文件,那么还有另一种解决方案(不含grep/sed)适合我。(编辑:请参阅rjrjr的回答,这是一个更简单的解决方案,没有任何临时文件的麻烦)
我使用Exec Maven插件和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项目版本,并抑制来自[INFO]和下载消息的无关输出:
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快照
所以,使用你的项目。版本在一个简单的bash脚本:
projectVersion=`mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\['`
cd "target/"$projectVersion"-build"
本页上的其他解决方案似乎并没有把所有的技巧结合在一起。
我最近开发了发布候选Maven插件来解决这个确切的问题,这样您就不必求助于任何hack shell脚本并解析Maven -help-插件的输出。
例如,要将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"
最终的版本。属性文件将如下所示:
PROJECT_VERSION=1.0.0-SNAPSHOT
在上述所有功能之上,Release Candidate还允许您基于在POM中定义的API版本设置项目的版本(这可能是您在CI服务器上所做的事情)。
如果您希望看到一个Release Candidate作为Maven生命周期一部分使用的示例,请查看我的另一个开源项目——Build Monitor for Jenkins的pom.xml。
只是为了记录,可以直接在命令行中配置Maven的Simple SLF4J日志记录,只输出我们需要的配置:
org.slf4j.simpleLogger.defaultLogLevel =警告和
org.slf4j.simpleLogger.log.org.apache.maven.plugins.help =信息
详见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执行而被重写。
要么让mvn给出答案(正如大多数答案所建议的那样),要么从pom.xml中提取答案。第二种方法的唯一缺点是可以很容易地提取<version/>标记的值,但只有当它是文字时,即不是Maven属性时,它才有意义。我选择了这种方法,因为:
mvn太啰嗦了,我只是不喜欢过滤它的输出。
与读取pom.xml相比,启动mvn非常慢。
我总是在<version/>中使用文字值。
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一直被指责,因为与DevOps工具的集成并不容易,因为它没有遵循一些关于CLI工具的良好实践,例如:
http://www.faqs.org/docs/artu/ch01s06.html (not available anymore)
"The Unix Way"
(ii) Expect the output of every program to become the input of another, as yet unknown, program. Don't clutter output with extraneous information. Avoid stringently columnar or binary input formats. Don't insist on interactive input.
What does it actually mean?
Your output should be:
"grepable": (One "record" per line)
"cutable": (Delimited "fields")
exit codes: 0 for success, nonzero for failure.
messaging (stderr) vs. output (stdout)
(参考:Dave Copeland于2012年1月18日用ruby制作很棒的命令行应用程序https://youtu.be/1ILEw6Qca3U?t=372)
Honesty I think Dave Copeland was right when he said that maven does't play fairly with others. So I decided to give a look to maven's source code as well as to maven-help-plugin's source code as well. It seems that they have fixed a little bit the maven's -q switch (I was using version 3.5.3 at that time), so now if you pass it, you won't get all the annoying non-sense logging stuff that prevents maven from being used within automated scripts. So you should be able to use something like this:
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)
同样,您应该始终验证命令的退出码,并在unix上将所有stderr重定向到/dev/null。
这是最干净的解决方案:
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(及以上版本)有强制输出选项。您可以从artifactory的mvn-help-plugin可用版本列表中替换上述命令中的3.2.0,如果可用的话。
选项-q抑制详细消息([INFO], [WARN]等)
或者,你可以在你的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,请检查这个答案。
在我的特拉维斯工作期间,我需要的正是这个要求,但有多个值。
我从这个解决方案开始,但当调用多次这是非常缓慢的(我需要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
玩得开心。