Incluir información de la versión en el artefacto distribuible con Gradle

Escrito por el , actualizado el .
java planeta-codigo programacion
Enlace permanente Comentarios

Tener trazabilidad entre el código fuente y el código que se está ejecutando en el entorno de producción es importante para saber cual es el origen de alguna excepción o error que se produzca. Con Gradle podemos conseguir esta trazabilidad haciendo unas pocas modificaciones al script de construcción.

Java

Gradle

Toda aplicación en último término genera un artefacto destinado a ejecutarse en el entorno de producción o un entregable a su destinatario. En Java según el tipo de aplicación el artefacto es un archivo jar ejecutable, un archivo war si es una aplicación web, un archivo zip si proporcionamos el código fuente o la documentación en formato Javadoc de las clases del proyecto. Un aspecto importante que tarde o temprano nos interesará conocer es que versión de la aplicación y por tanto que código está desplegada en el entorno de producción. Con Gradle no es muy complicado añadir la suficiente información para conseguir esta trazabilidad.

La forma tradicional es dar un número de versión al proyecto, hay diferentes nomenclaturas dependiendo del grado de precisión que necesitemos, suele bastar versión mayor, versión menor, y corrección de errores, los tres números que se van incrementando. Si usamos Jenkins para construir los artefactos nos puede interesar conocer el número de build que lo produjo o la fecha de creación. También nos puede interesar conocer el hash del último commit del código fuente del artefacto. Por otro lado puede que queramos que la aplicación nos informe de la versión que se está ejecutando ya que en algunas corporaciones el acceso al entorno de producción está restringido a sus administradores.

Para conseguir esta trazabilidad haremos dos modificaciones al archivo de construcción de Gradle, modificar el nombre del artefacto con el nombre de la build y el hash del commit e incluir en él un archivo properties con la información de la versión con el que la aplicación sea capaz de informar que versión es la que se está ejecutando. El hash del commit de Git se obtiene con el comando git log -n 1 --format=%h ejecutando con las facilidades que proporciona Groovy.

El nombre del artefacto se modifica con una clase cuyo método toString() proporciona la versión que podemos asignar a la propiedad version de la clase Project definida con el archivo de construcción Gradle. Para proporcionar la información de la versión en la aplicación se incluye un archivo al construir el artefacto modificando la tarea jar y generando el archivo con la tarea createBuildInfoFile. Este es el archivo de construcción de Gradle y la clase que contiene la información de la versión que se coloca en el directorio buildSrc.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import io.github.picodotdev.blogbitix.gradleversion.build.ProjectVersion

apply plugin: 'idea'
apply plugin: 'eclipse'
apply plugin: 'java'
apply plugin: 'application'

version = new ProjectVersion(major: 1, minor: 0, build: System.env.BUILD_NUMBER, hash: "git log -n 1 --format=%h".execute().text.trim())
mainClassName = 'io.github.picodotdev.blogbitix.gradleversion.Main'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.slf4j:slf4j-api:1.7.21'
    runtime 'org.apache.logging.log4j:log4j-api:2.5'
    compile 'org.apache.logging.log4j:log4j-slf4j-impl:2.5'
    runtime 'org.apache.logging.log4j:log4j-core:2.5'
}

task wrapper(type: Wrapper) {
    gradleVersion = '2.13'
}

task createBuildInfoFile << {
    def file = new File("$buildDir/build-info.properties")
    Properties props = new Properties()
    props.setProperty('version', project.version.toString())
    props.setProperty('timestamp', new Date().toString())
    props.store(file.newWriter(), null)
}

jar {
    dependsOn createBuildInfoFile

    manifest {
        attributes 'Main-Class': project.mainClassName
    }

    from(buildDir) {
        include 'build-info.properties'
    }
}
build.gradle
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package io.github.picodotdev.blogbitix.gradleversion.build

class ProjectVersion {

    private String major
    private String minor
    private String build
    private String hash

    public String toString() {
        def strings = []
        if (major) {
            strings.add(major)
        }
        if (minor) {
            strings.add(minor)
        }
        if (build) {
            strings.add("b$build")
        }
        if (hash) {
            strings.add(hash)
        }
        return strings.join('.')
    }
}
ProjectVersion.groovy

En el caso de un artefacto jar Gradle lo genera en build/libs/GradleVersion-1.0.b42.fea4d2f.jar. Ejecutando el jar con java -jar build/libs/GradleVersion-1.0.b42.77c083e.jar cuya clase con el método main informa de la versión leyendo el archivo properties incluído en el jar obtenemos la siguiente salida en la terminal.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package io.github.picodotdev.blogbitix.gradleversion;

import java.io.InputStream;
import java.util.Properties;

public class Main {

    public static void main(String[] args) throws Exception {
        Properties properties = new Properties();
        InputStream is = Main.class.getResourceAsStream("/build-info.properties");
        if (is != null) {
            properties.load(is);
        }
        String version = properties.getProperty("version");
        String timestamp = properties.getProperty("timestamp");

        System.out.printf("Versión: %s%n", version);
        System.out.printf("Timestamp: %s%n", timestamp);
    }
}
Main.java

Artefacto distribuible con información de versión

En este caso el artefacto que he usado ha sido un archivo jar si se tratase de una aplicación web y de un archivo war en el archivo de construcción de Grade se puede aplicar esto de forma similar. La aplicación podría devolver la versión como una cabecera HTTP o el en código fuente de HTML como un comentario que genere sus páginas web.

El libro Gradle in Action proporciona una aplicación más detallada de muchos conceptos de Gradle, otro buen punto de partida es la propia documentación de Gradle.

Terminal

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
export BUILD_NUMBER=42 && ./gradlew build


Comparte el artículo: