我有一个用Clojure编写的Web服务continuously delivered。为了使我们的自动部署工具能够知道已部署了哪个版本的代码库,Web服务应该提供一种方法来查询它是哪个版本。该版本在Leiningen构建工具中声明为项目设置的一部分,如下所示:
(defproject my-web-service "1.2-SNAPSHOT"
; ... rest of project.clj
)
代码库打包为JAR文件。
我们开发人员不希望在每次提交时增加版本号。相反,我们希望在我们的continuous integration服务器(在本例中为Jenkins)上触发新构建时,它会自动递增。例如,当版本控制签入提示此代码库的第四十二个版本时,版本为1.2.42
。
对于已构建和部署的任何特定JAR,我希望允许以某种方式查询版本号(例如,使用HTTP请求,但这是实现细节)。响应应包含字符串1.2.42
。
如何为正在运行的应用程序提供该版本号?
(可能重复,但不包括Jenkins方面:Embed version string from leiningen project in application)
答案 0 :(得分:19)
访问此版本号的一种方法是通过存储在JAR文件中的MANIFEST.MF
文件。这将允许在运行时通过Java的java.lang.Package
类进行访问。这需要以下三个步骤:
project.clj
的{{1}}声明。defproject
,其值为MANIFEST.MF
。Implementation-Version
以访问包含版本号的Package#getImplementationVersion()
。可以使用Jenkins的environment variables来访问构建号(名称为String
)。这在JVM进程中可用,使用BUILD_NUMBER
。在这种情况下,JVM进程可以是leiningen System.getenv("BUILD_NUMBER")
脚本,它是可以调用project.clj
的Clojure代码。按照上面的例子,返回的字符串将是“42”。
构建JAR时,Leiningen默认会包含(System/getenv "BUILD_NUMBER")
文件。它还有一个配置选项,允许在该文件中设置任意键值对。因此,当我们可以在Clojure中访问Jenkins构建号时,我们可以将它与静态版本声明相结合,以在清单中设置MANIFEST.MF
。 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}
... )
时(if-let ...)
是允许开发人员在本地构建JAR,而无需模拟Jenkins的环境变量。 build-version
配置允许创建使用Maven / Ivy约定命名的JAR文件。此示例中的结果文件为:uberjar-name
。
使用此配置,当Jenkins在内部版本号42上调用Leiningen时,生成的JAR中的清单将包含“Implementation-Version:1.2.42”行。
现在我们要使用的版本String在清单文件中,我们可以使用Clojure代码中的Java标准库来访问它。以下代码段演示了这一点:
my-web-service-1.2.42.jar
请注意,为了调用(ns version-namespace
(:gen-class))
(defn implementation-version []
(-> (eval 'version-namespace) .getPackage .getImplementationVersion))
,我们需要一个getImplementationVersion()
实例,为此我们需要一个Package
的实例。因此,我们确保从此命名空间生成Java类(对java.lang.Class
的调用)(然后我们可以从该类访问(:gen-class)
方法。
这个函数的结果是一个String,例如“1.2.42”。
值得注意的是,您可能需要担心几个问题,但我们的用例可以接受:
getPackage
project.clj
调用中定义的版本字符串可能导致其他一些工具无法正常工作,如果它们依赖于硬编码的版本(defproject ...)
的语义略有滥用。实际上版本应该是:getImplementationVersion
,但由于没有其他任何内容可以读取这些值,我们只需设置实现版本即可。请注意,正确执行此操作还需要在清单中添加“Specification-Version”。通过上述步骤,我运行的Clojure应用程序可以访问与打包代码的Jenkins构建相对应的版本号。