Ответ 1
Один из способов доступа к этому номеру версии - через файл MANIFEST.MF
, который хранится в файле JAR. Это позволит получить доступ во время выполнения через класс Java java.lang.Package
. Для этого требуются следующие три шага:
- Передача номера сборки Jenkins в Leiningen для включения в объявление
project.clj
defproject
. - Поручить Leiningen построить
MANIFEST.MF
со значением дляImplementation-Version
. - Вызов
Package#getImplementationVersion()
, чтобы получить доступ кString
, содержащему номер версии.
1 - Получение номера сборки Jenkins
Для доступа к номеру сборки (красиво названный BUILD_NUMBER
) можно использовать переменные среды Jenkins . Это доступно в процессе JVM, используя System.getenv("BUILD_NUMBER")
. В этом случае процесс JVM может быть leiningen project.clj
script, который является Clojure кодом, который может вызывать (System/getenv "BUILD_NUMBER")
. Следуя приведенному выше примеру, возвращаемая строка будет "42".
2 - Установка версии в MANIFEST.MF
При создании JAR Leiningen будет включать файл MANIFEST.MF
по умолчанию. Он также имеет опцию конфигурации, которая позволяет устанавливать произвольные пары ключ-значение в этом файле. Поэтому, когда мы можем получить доступ к номеру сборки Jenkins в Clojure, мы можем объединить это с объявлением статической версии, чтобы установить 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 ...)
при определении build-version
заключается в том, чтобы позволить разработчикам локально создавать JAR, не требуя эмуляции переменных среды Jenkins. Конфигурация :uberjar-name
позволяет создать JAR файл, который называется с использованием соглашений Maven/Ivy. Итоговый файл в этом примере будет my-web-service-1.2.42.jar
.
С этой конфигурацией, когда Leiningen вызывается Jenkins при построении числа 42, манифест в полученном JAR будет содержать строку "Version-Version: 1.2.42".
3 - Доступ к версии во время выполнения
Теперь, когда версия String, которую мы хотим использовать, находится в файле манифеста, мы можем получить к ней доступ с использованием стандартных библиотек Java в коде Clojure. Следующий фрагмент демонстрирует это:
(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, которая упаковала код.