通过访问存储在JAR文件中的MANIFEST.MF
文件,可以一种方法访问此版本号。这将允许在运行时通过Java的java.lang.Package
类进行访问。这需要以下三个步骤:
- 向Leiningen传递Jenkins构建号以合并到
project.clj
的defproject
声明中。
- 指示Leiningen使用
Implementation-Version
的值构造MANIFEST.MF
。
- 调用
Package#getImplementationVersion()
以访问包含版本号的字符串。
1 - 获取Jenkins构建号
可以使用Jenkins的环境变量来访问构建号(称为BUILD_NUMBER
)。这在JVM进程内可用,使用System.getenv("BUILD_NUMBER")
。在这种情况下,JVM进程可以是Clojure代码的leiningen project.clj
脚本,它可以调用(System/getenv "BUILD_NUMBER")
。按照以上示例,返回的字符串将是"42"。
2- 在MANIFEST.MF中设置版本
构建JAR时,Leiningen将默认包括一个MANIFEST.MF
文件。它还有一个配置选项,允许在该文件中设置任意键值对。因此,当我们可以在Clojure中访问Jenkins构建号时,可以将其与静态版本声明相结合,以在清单中设置Implementation-Version
。 project.clj
的相关部分如下:
(def feature-version "1.2")
(def build-version (or (System/getenv "BUILD_NUMBER") "HANDBUILT"))
(def release-version (str feature-version "." build-version))
(def project-name "my-web-service")
(defproject project-name feature-version
:uberjar-name ~(str project-name "-" release-version ".jar")
:manifest {"Implementation-Version" ~release-version}
... )
值得注意的是这个例子中有一些细节。在定义build-version
时使用(if-let ...)
,是为了让开发人员能够在本地构建JAR文件,而不需要仿真Jenkins的环境变量。配置:uberjar-name
是为了创建一个按照Maven/Ivy约定命名的JAR文件。在这个例子中生成的文件将会是my-web-service-1.2.42.jar
。
通过这个配置,在第42次构建时,当Jenkins调用Leiningen时,生成的JAR文件中的清单将包含"Implementation-Version: 1.2.42"这一行。
3 - 在运行时访问版本信息
既然我们想要使用的版本字符串已经在清单文件中了,我们可以在Clojure代码中使用Java标准库来访问它。以下代码片段展示了如何实现:
(ns version-namespace
(:gen-class))
(defn implementation-version []
(-> (eval 'version-namespace) .getPackage .getImplementationVersion))
请注意,为了调用
getImplementationVersion()
,需要一个
Package
实例,而要获取该实例,需要一个
java.lang.Class
的实例。因此,我们确保从这个命名空间生成了一个Java类(调用
(:gen-class)
),以便我们可以从该类访问
getPackage
方法
这个函数的结果是一个字符串,例如"1.2.42"。
注意事项
值得注意的是,可能会有一些需要注意的问题,但对于我们的使用情况来说是可接受的:
- 动态设置
project.clj
中(defproject ...)
调用中定义的版本字符串可能会导致其他工具无法正常工作,如果它们依赖于版本固定不变。
getImplementationVersion
的语义被稍微滥用了一下。实际上,版本应该是:pkg.getSpecificationVersion() + "." + pkg.getImplementationVersion()
,但由于没有其他地方读取这两个值,我们可以只设置实现版本即可。请注意,正确执行此操作还需要将“Specification-Version”添加到清单中。
通过上述步骤,我的运行中的Clojure应用程序可以访问与打包代码的Jenkins构建相对应的版本号。
or
而不是if-let
。如果读取清单的最简单方法是这样的:https://dev59.com/_k3Sa4cB1Zd3GeqPv4i7,我认为我更喜欢使用标准JDK获取此特定属性的方式。非常感谢! - Grundlefleck