如何获得运行中Clojure应用程序版本号的运行时访问权限?

如何获得运行中Clojure应用程序版本号的运行时访问权限?,clojure,continuous-integration,jenkins,leiningen,continuous-deployment,Clojure,Continuous Integration,Jenkins,Leiningen,Continuous Deployment,我有一个用Clojure编写的web服务。为了让我们的自动化部署工具知道部署了哪个版本的代码库,web服务应该提供一种查询它是哪个版本的方法。版本在生成工具中声明为项目设置的一部分,如下所示: (defproject my-web-service "1.2-SNAPSHOT" ; ... rest of project.clj ) (def feature-version "1.2") (def build-version (or (System/getenv "BUILD_NUMBE

我有一个用Clojure编写的web服务。为了让我们的自动化部署工具知道部署了哪个版本的代码库,web服务应该提供一种查询它是哪个版本的方法。版本在生成工具中声明为项目设置的一部分,如下所示:

(defproject my-web-service "1.2-SNAPSHOT"
  ; ... rest of 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}

  ... )
代码库打包为JAR文件

我们开发人员不希望在每次提交时增加版本号。相反,我们希望每当在我们的服务器上触发一个新的构建时(在本例中是Jenkins),它就会自动递增。例如,当版本控制签入提示该代码库的第四十二次构建时,版本为
1.2.42

对于已经构建和部署的任何特定JAR,我希望以某种方式允许查询版本号(例如,使用HTTP请求,但这是一个实现细节)。响应应包括字符串
1.2.42

如何使该版本号可用于正在运行的应用程序


(可能重复,但不包括Jenkins方面:)

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

  • 将Jenkins构建编号传递给Leiningen,以合并到
    project.clj
    defproject
    声明中
  • 指示Leiningen构造一个
    MANIFEST.MF
    ,其值为
    实现版本
  • 调用
    包#getImplementationVersion()
    以访问包含版本号的
    字符串
  • 1-获取詹金斯构建编号 可以使用Jenkins'访问内部版本号(很好地命名为
    内部版本号
    )。这在JVM进程中可用,使用
    System.getenv(“BUILD\u NUMBER”)
    。在这种情况下,JVM进程可以是leiningen
    project.clj
    脚本,它是可以调用
    (System/getenv“BUILD\u NUMBER”)
    的Clojure代码。按照上面的示例,返回的字符串将是“42”

    2-在MANIFEST.MF中设置版本 在构建JAR时,默认情况下,Leiningen将包含一个
    MANIFEST.MF
    文件。它还有一个配置选项,允许在该文件中设置任意键值对。因此,当我们可以访问Clojure中的Jenkins内部版本号时,我们可以将其与静态版本声明结合起来,在清单中设置
    实现版本。
    project.clj
    的相关部分如下所示:

    (defproject my-web-service "1.2-SNAPSHOT"
      ; ... rest of 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的环境变量。
    :uberjar name
    配置允许创建使用Maven/Ivy约定命名的JAR文件。本例中的结果文件是
    my-web-service-1.2.42.jar

    在这种配置下,当Jenkins在构建编号42上调用Leiningen时,结果JAR中的清单将包含行“实现版本:1.2.42”

    3-在运行时访问版本 现在我们要使用的版本字符串已经在清单文件中,我们可以使用Clojure代码中的Java标准库来访问它。以下代码段演示了这一点:

    (ns version-namespace
      (:gen-class))
    
    (defn implementation-version []
      (-> (eval 'version-namespace) .getPackage .getImplementationVersion))
    
    这里需要注意的是,为了调用
    getImplementationVersion()
    ,我们需要一个
    实例,为了得到它,我们需要一个
    java.lang.Class
    实例。因此,我们确保从该名称空间生成Java类(调用
    (:gen class)
    )(然后我们可以从该类访问
    getPackage
    方法

    此函数的结果是一个字符串,例如“1.2.42”

    警告 值得注意的是,您可能需要担心一些问题,但这些问题对于我们的用例来说是可以接受的:

    • 动态设置
      project.clj
      (defproject…
      调用中定义的版本字符串可能会导致其他一些工具无法工作,如果它们依赖于硬编码的版本
    • getImplementationVersion
      的语义有点被滥用。实际上版本应该是:
      pkg.getSpecificationVersion()+“+pkg.getImplementationVersion()
      ,但由于没有其他内容可以读取这两个值中的任何一个,因此只需设置实现版本即可。请注意,正确执行此操作还需要在清单中添加“规范版本”


    通过以上步骤,我正在运行的Clojure应用程序可以访问与打包代码的Jenkins版本相对应的版本号。

    Nice writeup。注意两点:您应该使用或而不是if-let-in-project.clj。此外,我非常确定您可以直接读取清单,而不是在类上调用.getPackage。但是如果您不能,您仍然可以不使用eval获得类。如果您想在没有AOT的情况下优雅地失败,您可以尝试解析。感谢您的响应。更新为使用
    而不是
    If let
    。如果读取清单的最简单方法是:我想我更喜欢使用标准JDK方法获得此特定属性。非常感谢!