如何获取运行时访问运行的Clojure应用程序的版本号? [英] How to get runtime access to version number of a running Clojure application?

查看:396
本文介绍了如何获取运行时访问运行的Clojure应用程序的版本号?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用Clojure编写的网络服务,它持续投放。为了允许我们的自动部署工具知道已经部署了哪个版本的代码库,Web服务应该提供一种查询其版本的方法。该版本在 Leiningen 构建工具中被声明为项目设置的一部分,如下所示:

 (defproject my-web-service1.2-SNAPSHOT
; ... project.clj的其余部分

代码库打包为JAR文件。



不想在每次提交时增加版本号。相反,我们希望在持续集成服务器(在本例中为Jenkins)中触发新建建议时,系统会自动将其递增。 。例如,当一个版本控制检入提示该代码库的第四十二版本时,版本为 1.2.42



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



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



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

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


  1. 将Jenkins构建号传递给Leiningen,并入 project.clj defproject 声明。

  2. 指示Leiningen构建 MANIFEST.MF

  3. 调用 Package#getImplementationVersion 可以访问包含版本号的 String


$ b b

1 - 获取Jenkins版本号



可以使用Jenkins的环境变量以访问构建号(很好地命名为 BUILD_NUMBER )。这在JVM进程中可以使用 System.getenv(BUILD_NUMBER)。在这种情况下,JVM进程可以是leiningen project.clj 脚本,它是可以调用(System / getenvBUILD_NUMBER)的Clojure代码。以上例子,返回的字符串将为42。



2 - 在MANIFEST.MF中设置版本



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

  def feature-version1.2)
(def build-version(或(System / getenvBUILD_NUMBER)HANDBUILT))
-version))
(def project-namemy-web-service)

(defproject project-name feature-version
:uberjar-name〜 name - ease-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



配置,当Leiningen由Jenkins在内部版本号42上调用时,生成的JAR中的清单将包含Implementation-Version:1.2.42行。



3 - 在运行时访问版本



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

 (ns版本命名空间
(:gen-class))

(defn implementation-version []
( - >(eval'version-namespace).getPackage .getImplementationVersion))

注意,为了调用 getImplementationVersion(),我们需要一个 Package instance,并得到我们需要一个 java.lang.Class 的实例。因此,我们确保从这个命名空间(调用(:gen-class))生成一个Java类(我们可以访问 getPackage 方法。



此函数的结果是一个String,例如1.2.42。



注意事项



值得注意的是,您可能需要担心几个问题,但我们的用例可以接受:




  • 动态设置 project.clj (defproject ...)调用可能会导致一些其他工具无法正常工作,如果他们依赖于硬编码的版本

  • getImplementationVersion 已被略微滥用,真正的版本应该是: pkg.getSpecificationVersion()+。+ pkg.getImplementationVersion()因为没有其他东西读取这些值,我们可以通过设置实现版本来避免。注意,正确的做法还需要在清单中添加Specification-Version。






通过上面的步骤,我运行的Clojure应用程序可以访问一个版本号,该版本号对应于打包代码的Jenkins版本。


I have a web service written in Clojure which is continuously delivered. To allow our automated deployment tools to know which version of the codebase has been deployed, the web service should provide a way to query which version it is. The version is declared as part of the project setup in the Leiningen build tool, like this:

(defproject my-web-service "1.2-SNAPSHOT"
  ; ... rest of project.clj
  )

The codebase is packaged as a JAR file.

We developers do not want to increment the version number on each commit. Instead, we want it to be incremented automatically whenever a new build is triggered on our continuous integration server (in this case Jenkins). For example, when a version control checkin prompts the forty-second build of this codebase, the version is 1.2.42.

For any particular JAR that's been built and deployed, I want to allow querying the version number somehow (e.g. with an HTTP request, but that's an implementation detail). The response should include the string 1.2.42.

How do I make that version number available to the running application?

(Possible duplicate, though it doesn't include the Jenkins aspect: Embed version string from leiningen project in application)

解决方案

One way to access this version number is through the MANIFEST.MF file which is stored within the JAR file. This will allow access at runtime, through Java's java.lang.Package class. This requires the following three steps:

  1. Passing the Jenkins build number to Leiningen, to incorporate in project.clj's defproject declaration.
  2. Instructing Leiningen to construct a MANIFEST.MF with a value for Implementation-Version.
  3. Invoking Package#getImplementationVersion() to get access to a String containing the version number.

1 - Getting the Jenkins build number

It is possible to use Jenkins' environment variables to access the build number (nicely named BUILD_NUMBER). This is available within a JVM process, using System.getenv("BUILD_NUMBER"). In this case, the JVM process can be the leiningen project.clj script, which is Clojure code which can invoke (System/getenv "BUILD_NUMBER"). Following the above example, the String returned would be "42".

2 - Setting the version in MANIFEST.MF

When building a JAR, Leiningen will include a MANIFEST.MF file by default. It also has a configuration option, which allows setting arbitrary key-value pairs in that file. So when we can access the Jenkins build number in Clojure, we can combine that with the static version declaration to set the Implementation-Version in the manifest. The relevant portions of the project.clj look like this:

(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}

  ... )

It's worth noting a couple of the details in this example. The (if-let ...) when defining build-version is to allow developers to build the JAR locally, without needing to emulate Jenkins' environment variables. The :uberjar-name configuration is to allow creating a JAR file which is named using Maven/Ivy conventions. The resulting file in this example would be my-web-service-1.2.42.jar.

With this configuration, when Leiningen is invoked by Jenkins on build number 42, the manifest in the resulting JAR will contain the line "Implementation-Version: 1.2.42".

3 - Accessing version at runtime

Now that the version String we want to use is in the manifest file, we can access it using Java standard libraries in Clojure code. The following snippet demonstrates this:

(ns version-namespace
  (:gen-class))

(defn implementation-version []
  (-> (eval 'version-namespace) .getPackage .getImplementationVersion))

Note here, that in order to invoke getImplementationVersion(), we need a Package instance, and to get that we need an instance of java.lang.Class. Hence we ensure that a Java class is generated from this namespace (the call to (:gen-class)) (we can then access the getPackage method from that class.

The result of this function is a String, e.g. "1.2.42".

Caveats

It's worth noting there's a couple of gotchas you may have to worry about, but were acceptable for our use case:

  • dynamically setting the version string defined in the project.clj's (defproject ...) call may cause some other tools not to work, if they rely on the version being hardcoded
  • the semantics of getImplementationVersion have been abused slightly. Really the version should be: pkg.getSpecificationVersion() + "." + pkg.getImplementationVersion(), but since nothing else reads either of these values, we can get away with just setting the implementation version. Note that doing this correctly would require also adding "Specification-Version" to manifest.

With the steps above, my running Clojure application can access a version number which corresponds to the Jenkins build which packaged the code.

这篇关于如何获取运行时访问运行的Clojure应用程序的版本号?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆