¿Cómo obtener acceso en tiempo de ejecución al número de versión de una aplicación Clojure en ejecución?
continuous-integration jenkins (1)
Una forma de acceder a este número de versión es a través del archivo MANIFEST.MF
que se almacena dentro del archivo JAR. Esto permitirá el acceso en tiempo de ejecución, a través de la clase java.lang.Package
de Java. Esto requiere los siguientes tres pasos:
- Pasando el número de compilación de Jenkins a Leiningen, para incorporarlo en la declaración
defproject
project.clj
. - Indicar a Leiningen que construya un
MANIFEST.MF
con un valor paraImplementation-Version
. - Invocando el
Package#getImplementationVersion()
para obtener acceso a unaString
contiene el número de versión.
1 - Obtención del número de compilación de Jenkins
Es posible usar las variables de entorno de Jenkins para acceder al número de compilación (llamado BUILD_NUMBER
). Esto está disponible dentro de un proceso JVM, usando System.getenv("BUILD_NUMBER")
. En este caso, el proceso JVM puede ser el script leiningen project.clj
, que es un código Clojure que puede invocar (System/getenv "BUILD_NUMBER")
. Siguiendo el ejemplo anterior, la cadena devuelta sería "42".
2 - Configuración de la versión en MANIFEST.MF
Al crear un JAR, Leiningen incluirá un archivo MANIFEST.MF
de forma predeterminada. También tiene una opción de configuración, que permite establecer pares de clave-valor arbitrarios en ese archivo. Entonces, cuando podamos acceder al número de compilación de Jenkins en Clojure, podemos combinarlo con la declaración de la versión estática para establecer la Implementation-Version
en el manifiesto. Las partes relevantes de project.clj
ven así:
(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}
... )
Vale la pena mencionar un par de detalles en este ejemplo. El (if-let ...)
al definir build-version
es permitir a los desarrolladores compilar el JAR localmente, sin necesidad de emular las variables de entorno de Jenkins. La :uberjar-name
es permitir la creación de un archivo JAR que se nombra usando las convenciones de Maven / Ivy. El archivo resultante en este ejemplo sería my-web-service-1.2.42.jar
.
Con esta configuración, cuando Jenkins invoca a Leiningen en el número de compilación 42, el manifiesto en el JAR resultante contendrá la línea "Implementación-Versión: 1.2.42".
3 - Acceso a la versión en tiempo de ejecución
Ahora que la versión de la cadena que queremos usar está en el archivo de manifiesto, podemos acceder a ella utilizando las bibliotecas estándar de Java en código Clojure. El siguiente fragmento de código lo demuestra:
(ns version-namespace
(:gen-class))
(defn implementation-version []
(-> (eval ''version-namespace) .getPackage .getImplementationVersion))
Tenga en cuenta que para invocar getImplementationVersion()
, necesitamos una instancia de Package
, y para obtenerla necesitamos una instancia de java.lang.Class
. Por lo tanto, nos aseguramos de que se genere una clase Java desde este espacio de nombres (la llamada a (:gen-class)
) (luego podemos acceder al método getPackage
desde esa clase).
El resultado de esta función es una cadena, por ejemplo, "1.2.42".
Advertencias
Vale la pena señalar que hay un par de errores por los que debe preocuparse, pero que son aceptables para nuestro caso de uso:
- establecer dinámicamente la cadena de versión definida en la llamada del
project.clj
(defproject ...)
puede hacer que algunas otras herramientas no funcionen, si se basan en la versión que está codificada. - La semántica de
getImplementationVersion
ha sido abusada ligeramente. Realmente la versión debería ser:pkg.getSpecificationVersion() + "." + pkg.getImplementationVersion()
pkg.getSpecificationVersion() + "." + pkg.getImplementationVersion()
, pero como nada más lee ninguno de estos valores, podemos establecer la versión de implementación. Tenga en cuenta que hacer esto correctamente requeriría también agregar "Especificación-Versión" al manifiesto.
Con los pasos anteriores, mi aplicación Clojure en ejecución puede acceder a un número de versión que corresponde a la compilación Jenkins que empaquetó el código.
Tengo un servicio web escrito en Clojure que se entrega continuamente . Para permitir que nuestras herramientas de implementación automatizadas sepan qué versión de la base de código se ha implementado, el servicio web debe proporcionar una forma de consultar qué versión es. La versión se declara como parte de la configuración del proyecto en la herramienta de compilación Leiningen , como esta:
(defproject my-web-service "1.2-SNAPSHOT"
; ... rest of project.clj
)
El código base está empaquetado como un archivo JAR.
Nosotros los desarrolladores no queremos incrementar el número de versión en cada confirmación. En su lugar, queremos que se incremente automáticamente cada vez que se active una nueva construcción en nuestro servidor de integración continua (en este caso, Jenkins). Por ejemplo, cuando un control de versión solicita la compilación de cuarenta segundos de esta base de código, la versión es 1.2.42
.
Para cualquier JAR en particular que se haya creado e implementado, quiero permitir consultar el número de versión de alguna manera (por ejemplo, con una solicitud HTTP, pero eso es un detalle de implementación). La respuesta debe incluir la cadena 1.2.42
.
¿Cómo hago que ese número de versión esté disponible para la aplicación en ejecución?
(Posible duplicado, aunque no incluye el aspecto Jenkins: Cadena de versión incrustada del proyecto leiningen en la aplicación )