Como publicar nuestra librería Java con Gradle y Bintray

Publicando nuestra librería Open Source a los repositorios JCenter y Maven Central

No hay nada más lindo que tu trabajo sea útil a otros. Como desarrolladores nos vemos inmersos en muchos tipos de proyectos, pero no hay ninguno más que especial que poder construir una librería con la esperanza de que la usen mucha gente. Cuando empezamos esta faena y tenemos un producto mínimamente viable que queramos publicar nos damos cuenta de que no es un proceso tan sencillo ni tenemos los pasos claros para acometerlo.

Mediante este tutorial los enseñaré a publicar vuestra librería en los repositorios JCenter y Maven Central de una manera que podamos automatizar el proceso mediante Gradle.

Todo el mundo conoce el repositorio Maven Central, durante años ha sido la referencia donde encontramos todas las librerías que necesitábamos. Sin embargo en los últimos años y sobretodo a raiz de que Google lo escoge por defecto para sus proyectos he conocido JCenter que en mi opinión nos ofrece una serie de ventajas:

  • Maven Central es un subconjunto de JCenter, es decir contiene todo lo que haya en Maven Central y más.
  • Trabaja a través de CDN por lo que mejora el rendimiento de descarga de las librerías.
  • Tiene una interfaz amigable para gestionar las librerías.
  • Nos permite sincronizar a Maven Central con un solo clic.
  • Es el repositorio por defecto para proyectos Gradle.

Pero recordad, debemos soportar los dos repositorios porque todavía muchos desarrolladores usan Maven  hoy en día.


Creando proyecto en Bintray

Bintray es la empresa quien aloja el repositorio de JCenter. El primer paso que debemos dar es crearnos una cuenta para proyectos Open Source en https://bintray.com/signup/oss donde posteriormente ya tendremos un dashboard donde gestionaremos todas nuestras librerías.

 

Nuestro siguiente paso será crear un repositorio personal de tipo Maven dando click en "Add New Repository", aquí será donde se alojarán nuestras librerías antes de ser publicadas a los repositorios finales.

 

Cuando ya tengamos nuestro repositorio ya podremos subir ahí todas nuestras librerías. Por cada una de ellas será necesario crear un "Package" dentro de nuestro repositorio. Para eso le daremos en "Add New Package" y rellenaremos la información específica de nuestra librería.

 

Es muy importante tener en cuenta que debemos tener en propiedad el dominio del paquete Java de nuestra libreria, por ejemplo, en nuestro caso es.unblogdecode.samples o de lo contrario no podremos subirlo a Maven Central. O en otro caso nos permiten también un dominio asociado a nuestro repositorio remoto de control de versiones, por ejemplo, si le asignáramos el paquete com.github.atrujillofalcon.samples

Ya con esto tenemos completado el primer paso y ahora pasemos a la parte divertida, configurar nuestro fichero build.gradle para que nuestra librería se sincronize con nuestro "package" de Bintray.


Configurando proceso de release en Gradle

En este tutorial asumo que tenéis un conocimiento al menos básico de Gradle, me centraré en enseñaros como configurar para generar las releases de nuestra librería y como sincronizarlas con el paquete que hemos creado en Bintray. Para esto haremos uso de dos plugins de Gradle:


plugins {
    id 'net.researchgate.release' version '2.6.0'
    id "com.jfrog.bintray" version "1.8.1"
}

El primer plugin, el cual podemos ver toda su documentación en https://github.com/researchgate/gradle-release nos permite ir generando las releases de nuestra librería, promoviendo nuestra versión SNAPSHOT,  creando sus respectivas tag's en Git y creando la nueva versión de desarrollo.

El groupId de nuestra librería y lo definiremos explícitamente y la versión la añadiremos aparte en el fichero gradle.properties de la raíz del proyecto (crear si no existe).


group 'com.github.atrujillofalcon'
version project.property('version')

   Después de esto pasaremos a la configuración específica del plugin 'release':


release {
    failOnCommitNeeded = true
    failOnPublishNeeded = true
    failOnSnapshotDependencies = true
    failOnUnversionedFiles = false
    failOnUpdateNeeded = true
    revertOnFail = true
    preCommitText = ''
    preTagCommitMessage = '[Gradle Release Plugin] - pre tag commit: '
    tagCommitMessage = '[Gradle Release Plugin] - creating tag: '
    newVersionCommitMessage = '[Gradle Release Plugin] - new version commit: '
    tagTemplate = '${version}'
    versionPropertyFile = 'gradle.properties'
    scmAdapters = [
            net.researchgate.release.GitAdapter,
    ]

    git {
        requireBranch = 'master'
        pushToRemote = 'origin'
        pushToBranchPrefix = ''
        commitVersionFileOnly = false
    }
}

Como véis podemos configurar multitud de cosas como el nombre del tag, el mensaje de los commits, el tipo de control de versiones (espero que todo uséis Git jeje) y también la rama a la que queremos subir las releases.


Configurar sincronización con Bintray

El otro plugin a configurar es el encargado de subir las releases que generemos a Bintray. Podéis ver su documentación en https://github.com/bintray/gradle-bintray-plugin. Antes de configurar el plugin debemos ir a la web de Bintray y entrar en la edición de nuestro perfil para obtener el API Key de nuestro usuario.

 

Generar fichero pom.xml

Nuestra librería tiene que seguir la convención de Maven al ser publicada ya que con esto aseguraremos la compatibilidad lo mismo con Gradle que con Maven, por lo que es necesario que en nuestro artefacto generado exista un fichero pom.xml válido. Para esto debemos añadir los siguientes plugins Gradle:

apply plugin: 'maven'
apply plugin: 'maven-publish'

Y a continuación configuraremos el plugin:


//Aqui definimos los típicos atributos
def pomConfig = {
    licenses {
        license {
            name "The Apache Software License, Version 2.0"
            url "http://www.apache.org/licenses/LICENSE-2.0.txt"
            distribution "repo"
        }
    }
    developers {
        developer {
            id "atrujillo"
            name "Arnaldo Trujillo"
            email "atrujillo92work@gmail.com"
        }
    }

    scm {
        url "https://github.com/atrujillofalcon/bintray-publishing-sample"
    }
}

publishing {
    publications {
        MyPublication(MavenPublication) {
            from components.java
            groupId 'com.github.atrujillofalcon'
            artifactId 'bintray-publishing-sample'
            version project.property('version')
            pom.withXml {
                def root = asNode()
                root.appendNode('description', 'Bintray publishing example for UnBlogDecode')
                root.appendNode('name', 'Bintray Publishing Sample')
                root.appendNode('url', 'https://github.com/atrujillofalcon/bintray-publishing-sample')
                root.children().last() + pomConfig
            }
        }
    }
}

Donde hemos puesto "MyPublication" en realidad podemos definir cualquier nombre, pero teniendo en cuenta que lo vamos a referenciar dentro del plugin de bintray a continuación.


bintray {
    user = project.hasProperty('bintrayUser') ? project.property('bintrayUser') : System.getenv('BINTRAY_USER')
    key = project.hasProperty('bintrayApiKey') ? project.property('bintrayApiKey') : System.getenv('BINTRAY_API_KEY')
    configurations = ['archives']
    publications = ['MyPublication']
    pkg {
        repo = 'unblogdecode-repo'
        name = 'es-atrujillo-decode-binytray-publishing'
        licenses = ['Apache-2.0']
        vcsUrl = 'https://github.com/atrujillofalcon/bintray-publishing-sample.git'
    }
}

Como véis el plugin de Bintray necesita obligatoriamente los siguientes datos:

  • Usuario y API Key de Bintray (Podéis configurarlo en el fichero gradle.properties, aunque preferiblemente es mejor fuera de las fuentes del proyecto, por ejemplo en el fichero de properties global de Gradle <carpeta_usuario>/.gradle/gradle.properties o también podemos definirlo como una variable de entorno)
  • Nombre de la publicación Maven que hemos definido anteriormente.
  • Nombre del repo y del package que hemos creado en Bintray.

Uniendo las piezas

Con los dos plugins configurados y las credenciales de Bintray configuradas en nuestro gradle.properties o como variables de entorno ya estaríamos en condiciones de ejecutar estas dos tareas de Gradle, una para ejecutar el proceso de release y la otra para intentar subir nuestro artefacto a Bintray:

gradle release
gradle bintrayUpload

Pero seguramente si ejecutáis la segunda os fallará debido a que estará intentando subir una versión SNAPSHOT a nuestro repositorio de Bintray y eso no está permitido.

Para que todo encaje nos apoyaremos en una de las funcionalidades nativas de Gradle, asociar dependencias entre tareas, seas las que sean. Lo que queremos es automatizar todo el proceso y que cuando ejecutemos el release automáticamente nuestra versión (no snapshot), se suba a Bintray. Esto lo haremos añadiendo esta mágica línea a nuestro build.gradle:


afterReleaseBuild.dependsOn bintrayUpload

Con esto garantizaremos que se ejecute la subida a Bintray en el momento correcto, justo después de generar nuestra versión final y justo antes de crear la nueva versión SNAPSHOT y todo esto con solo ejecutar gradle release.

Puntualizar que antes de ejecutar el proceso de release debemos finalizar y "commitear" todos nuestros cambios.

Empaquetando javadoc y sources

Para que nuestro artefacto generado cumpla con todos los requisitos de la "convención Maven" debemos incluir también el jar de javadoc y el jar de las fuentes. Esto lo haremos añadiendo las siguientes tareas al final de nuestro fichero de build.gradle:


task sourcesJar(type: Jar, dependsOn: classes) {
    classifier = 'sources'
    from sourceSets.main.allSource
}

task javadocJar(type: Jar, dependsOn: javadoc) {
    classifier = 'javadoc'
    from javadoc.destinationDir
}

artifacts {
    archives sourcesJar
    archives javadocJar
}

Sincronizar con JCenter

Después de ejecutar satisfactoriamente nuestra release veremos que en la interfaz web de Bintray se ha creado una nueva versión, lo que pasa es que aún esta versión no está disponible para el público. Para esto debemos sincronizarlo con JCenter y MavenCentral. La siguiente configuración solo la tendremos que realizar una sola vez.

 

Tendremos que dar click en "Add to jCenter" (recuadro rojo) y enviar la solicitud (no hace falta poner ningún comentario específico. En unas pocas horas recibiremos una respuesta del equipo de Bintray comentándonos si nuestra petición ha sido aprobada o si nos falta algo más por configurar.

Después que nos aprueben la petición ya se sincronizarán automáticamente con JCenter todas las futuras versiones que publiquemos de nuestra librería. Ya está disponible al público Yuhuuu!!!

Veremos un nuevo "badge" de JCenter y también si queremos podemos verificar directamente in-situ que nuestra librería se ha publicado yendo al repositorio público de http://jcenter.bintray.com y navegando hasta nuestra librería, en este caso publicada en http://jcenter.bintray.com/com/github/atrujillofalcon/bintray-publishing-sample


Sincronizar con Maven Central

Sincronizar con Maven Central desde Bintray es tan sencillo como darle a un botón, pero primero tendremos que hacer una petición para darnos de alta y también debemos configurar Bintray para autofirmar nuestras librerías. Vamos a verlo paso a paso.

Alta en Sonatype

Sonatype es la empresa que lleva el alojamiento y control de Maven Central. Para alojar nuestra librería primero tendremos que darnos de alta y crear la petición. Los pasos a seguir son los siguientes:

  1. Crear una cuenta en el JIRA de Sonatype https://issues.sonatype.org/secure/Signup!default.jspa
  2. Crear la petición en el JIRA https://issues.sonatype.org/secure/CreateIssue.jspa?issuetype=21&pid=10134

 

Desde que creamos la petición hasta que se nos aprueba puede demorar hasta dos días laborables, por lo que no desesperéis. En el caso de que el groupId no sea el mismo dominio del repo (com.github, etc...) nos pedirán que demostremos que somos los propietarios de ese dominio. Cuando nos lo aprueben nos llegará un mail de este estilo:

Si tenéis cualquier duda podéis consultar la documentación completa y todos los requisitos en https://central.sonatype.org/pages/ossrh-guide.html

 

Autofirma de librerías en Bintray

 El otro requisito fundamental para poder publicar nuestra librería en Maven Central es haberla firmado mediante GPG. Bintray nos facilita esto permitiendo que todas las versiones que creemos se firmen aunque primero hay que configurarlo. El primer paso es generar nuestro par de llave pública/privada GPG. Este proceso es depediente del sistema operativo, vamos a ver como hacerlo en Linux.

1- Generamos nuestro par de claves rellenando nuestros datos cuando nos lo solicite

gpg --gen-key

2- Listamos nuestras keys disponibles actualmente:

gpg --list-keys

lo cual debería generarnos una salida similar a la siguiente:

pub   2048R/ABCDEF12 2018-06-25
uid                  Arnaldo Trujillo <yourmail@email.com>
sub   2048R/98765432 2018-06-25

Subiremos nuestra llave pública a un "keyserver" para que tenga validez. Para esto llamaremos al siguiente comando sustituyendo el PUBLIC_KEY_ID por nuestro valor real que hemos obtenido al listar las llaves, en este caso ABCDEF12 .

gpg --keyserver hkp://pool.sks-keyservers.net --send-keys PUBLIC_KEY_ID

Y por último generaremos los ficheros que tenemos que subir a Bintray.

gpg --output private-gpg.key --armor --export-secret-keys PUBLIC_KEY_ID
gpg --output public-gpg.key --armor --export PUBLIC_KEY_ID

y copiamos el contenido de los ficheros a la sección correspondiente de nuestro perfil en Bintray

Por último debemos habilitar el autofirmado para que Bintray firme todas nuestras librerías con las credenciales GPG que hemos configurado. Para esto debemos ir al modo edición de nuestro repositorio y habilitar el siguiente checkbox:

 

Sincronizar con Maven

Ya después de tener configurado todo lo planteado anteriormente sincronizar con Maven Central es tan sencillo como ir a la pestaña correspondiente en la administración de nuestra librería en Bintray, escribir nuestro usuario y contraseña de Sonatype y darle al botón Sync.

Y ya por fin tenemos disponible nuestra librería para todos los desarrolladores, espero que os haya servido. Podéis ver el ejemplo completo del build.gradle del proyecto de ejemplo que he subido a Github aquí. ¡Felicitaciones y Happy Decoding!

Más entradas de blog

Patrón Step Builder

Hola a todos ¡cuanto tiempo sin vernos! Estos últimos meses he estado con mucho trabajo y no...

Añadir comentarios