如何获得运行时访问正在运行的Clojure应用程序的版本号?

时间:2012-09-26 10:40:37

标签: clojure continuous-integration jenkins leiningen continuous-deployment

我有一个用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

1 个答案:

答案 0 :(得分:19)

访问此版本号的一种方法是通过存储在JAR文件中的MANIFEST.MF文件。这将允许在运行时通过Java的java.lang.Package类进行访问。这需要以下三个步骤:

  1. 将Jenkins内部版本号传递给Leiningen,以纳入project.clj的{​​{1}}声明。
  2. 指示Leiningen构建defproject,其值为MANIFEST.MF
  3. 调用Implementation-Version以访问包含版本号的Package#getImplementationVersion()
  4. 1 - 获取Jenkins内部版本号

    可以使用Jenkins的environment variables来访问构建号(名称为String)。这在JVM进程中可用,使用BUILD_NUMBER。在这种情况下,JVM进程可以是leiningen System.getenv("BUILD_NUMBER")脚本,它是可以调用project.clj的Clojure代码。按照上面的例子,返回的字符串将是“42”。

    2 - 在MANIFEST.MF中设置版本

    构建JAR时,Leiningen默认会包含(System/getenv "BUILD_NUMBER")文件。它还有一个配置选项,允许在该文件中设置任意键值对。因此,当我们可以在Clojure中访问Jenkins构建号时,我们可以将它与静态版本声明相结合,以在清单中设置MANIFEST.MFImplementation-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”行。

    3 - 在运行时访问版本

    现在我们要使用的版本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构建相对应的版本号。