访问此版本号的一种方法是通过存储在 JAR 文件中的 MANIFEST.MF 文件。这将允许在运行时通过 Java 的 java.lang.Package 类进行访问。这需要以下三个步骤:
- 将 Jenkins 内部版本号传递给 Leiningen,以合并到
project.clj 的 defproject 声明中。
- 指示 Leiningen 构造一个
MANIFEST.MF,其值为 Implementation-Version。
- 调用
Package#getImplementationVersion() 以访问包含版本号的String。
1 - 获取 Jenkins 内部版本号
可以使用 Jenkins 的 environment variables 访问内部版本号(很好地命名为 BUILD_NUMBER)。这在 JVM 进程中可用,使用 System.getenv("BUILD_NUMBER")。在这种情况下,JVM 进程可以是 leiningen project.clj 脚本,这是可以调用 (System/getenv "BUILD_NUMBER") 的 Clojure 代码。按照上面的例子,返回的字符串是“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。
使用此配置,当 Jenkins 在版本号 42 上调用 Leiningen 时,生成的 JAR 中的清单将包含“Implementation-Version: 1.2.42”行。
3 - 在运行时访问版本
现在我们要使用的版本字符串在清单文件中,我们可以在 Clojure 代码中使用 Java 标准库来访问它。下面的 sn-p 演示了这一点:
(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(),但由于没有其他任何东西读取这些值,我们只需设置实现版本即可。请注意,正确执行此操作还需要将“规范版本”添加到清单中。
通过上述步骤,我正在运行的 Clojure 应用程序可以访问与打包代码的 Jenkins 构建相对应的版本号。