Publicar artefactos en MavenCentral

Algunos de mis proyectos (librerías Java) los he publicado usando Bintray, un servicio que tenía una capa gratuíta que te permitía crear tus repositorios y subir tus "artefactos" de una forma fácil. Además gestionaban por tí el publicarlos y sincronizarlos tanto en JCenter como en MavenCentral

Sin embargo hace poco, por las razones que fueran, la empresa ha dejado de ofrecer este servicio dejando JCenter como sólo lectura ( https://jfrog.com/blog/into-the-sunset-bintray-jcenter-gocenter-and-chartcenter/) y siendo imposible ya crear nuevos artefactos.

De las alternativas que se ofrecen me he decantado por publicar "directamente" en MavenCentral a través de Sonatype que al fín y al cabo son los creadores.

INFO

Si no lo había hecho antes es porque recordaba que cuando lo miré hace años el proceso era bastante complicado y se podía tardar varios días en que te aprobaran la cuenta, etc. Sin embargo siguiendo varios tutoriales he podido completar el proceso y (re)-publicar mis dos primeras extensiones en una tarde

Como uso principalmente Gradle, en este post voy a explicar los pasos y configuración que he usado para publicarlo en MavenCentral

Requisitos

Básicamente el único requisito que vas a tener es que seas el "propietario" del dominio con el que pretendes publicar tus artefactos.

En mi caso, con Bintray este requisito no era necesario y yo publicaba bajo com.puravida (me pareció más sencillo que com.puravida-software) pero como no soy el dueño de ese dominio ahora me toca migrar todos los proyectos a com.puravida-software el cual sí es mío.

Así mismo vas a necesitar acceso a la gestión de DNS que básicamente todos los proveedores de dominio te ofrecen de una forma u otra. Como contaré más adelante, una de las formas más rápidas y cómodas de validar que eres el propietario del dominio es creando un registro TXT a través de la consola de tu proveedor.

El segundo requisito es tener una cuenta de correo, que en principio no tiene porqué ser de ese dominio.

Cuenta SonaType

En primer lugar hay que crearse una cuenta en Sonatype (https://issues.sonatype.org/secure/Signup!default.jspa)

Una vez que te hayas registrado crearemos un ticket (Botón "Crear" en el menú superior) y nos aseguraremos que es del tipo "New Project"

En el cuerpo del ticket yo he puesto:

Hi

I want to create the `com.puravida-software` groupId where host several OSS I've developed

Y a los pocos minutos (5-8) alguien de soporte ha contestado en el ticket:

Do you own the domain puravida-software.com? If so, please verify ownership via one of the following methods:

* Add a TXT record to your DNS referencing this JIRA ticket: OSSRH-XXXXX (Fastest)

* Setup a redirect to your Github page (if it does not already exist)

You can find more information here: https://central.sonatype.org/publish/

Como comentaba al principio, crear una entrada TXT en el DNS es muy fácil en el panel de administración de mi proveedor. Simplemente creo una entrada tipo TXT con la clave el código del ticket y el valor la URL al tocket:

y una vez creada he comentado en el ticket que ya estaba creada. Si es necesario o no, ni idea, pero a los pocos minutos el ticket se marca como resuelto con un texto:

Central OSSRH resuelta OSSRH-XXXXX.
-----------------------------------

    Resolución: Solucionada

com.puravida-software has been prepared, now user(s) jorge.aguilera can:

Publish snapshot and release artifacts to https://s01.oss.sonatype.org

Have a look at this section of our official guide for deployment instructions:

https://central.sonatype.org/publish/publish-guide/#deployment

Please comment on this ticket when you've released your first component(s), so we can activate the sync to Maven Central.

Depending on your build configuration, this might happen automatically. If not, you can follow the steps in this section of our guide:

https://central.sonatype.org/publish/release/

Ahora podremos ir a la consola Nexus de SonaType https://s01.oss.sonatype.org/#welcome y crear un API token el cual copiaremos en lugar seguro antes de cerrar el diálogo. (Aquí https://blog.solidsoft.pl/2015/09/08/deploy-to-maven-central-using-api-key-aka-auth-token/ puedes ver cómo crear el token)

Tanto el username como el token lo guarderemos en nuestro fichero de configuración global gradle.properites:

$HOME/.gradle/gradle.properties
sonatypeUsername=XXXXX
sonatypePassword=XXXXXXXXXXXXXXXXXXXXXXXXXXXxx

Perfecto. Ahora ya tenemos un sitio donde subir nuestros artefactos y si pasan todas las validaciones se sincronizarán con Maven Central.

Firma de artefactos

Uno de los requisitos de Maven Central es que todos los artefactos que quieras subir tienen que ir firmados asi que ahora lo que toca es crear y publicar una firma gpg (también podía ser el primero puesto que son independientes).

Yo he seguido los pasos de está página https://weibeld.net/java/publish-to-maven-central.html que se resumen en:

  • Crearemos una clave si no la tenemos

$ gpg --gen-key

e introduciremos los detalles que nos pregunta, tipo nombre, organizacion, etc.

  • Publicaremos la clave

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

Yo también la he publicado en este otro servidor porque durante una de las ejecuciones me dió un error que no lo encontraba ahí:

$ gpg --keyserver hkp://keyserver.ubuntu.com --send-keys XXXXXXXXXXXXXXX

  • Configuramos Gradle con los datos de la firma, siguiendo los pasos que recomienda la web mencionada.

    WARNING

    Esto quiere decir que cuando quiera publicar un artefacto tendré que hacerlo desde un ordenador que tenga la clave (?)

$HOME/.gradle/gradle.properties
signing.keyId=XXXXXX
signing.password=<YOUR-PASSWORD>
signing.secretKeyRingFile=/home/jorge/.gnupg/secring.gpg

Proyecto Java

Supongamos que lo que queremos publicar es una simple librería. En mi caso una librería java standard para crear Gif animados a partir de un array de imágenes (en fichero o en memoria).

En sí el proyecto Gradle es algo como

plugins{
    id 'java-library'
}

repositories {
    mavenCentral()
}

group 'com.puravida-software.gif'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

dependencies {
    // Use the latest Groovy version for Spock testing
    testCompile 'org.codehaus.groovy:groovy-all:2.5.4'
    // Use the awesome Spock testing and specification framework even with Java
    testCompile 'org.spockframework:spock-core:1.2-groovy-2.5'
    testCompile 'junit:junit:4.12'
}

javadoc {
    title = "$project.description"
}

java {
    withJavadocJar()
    withSourcesJar()
}

Básicamente podemos construir la libreria con ./gradlew build y la tendremos disponible en el directorio build/libs.

La idea ahora es poder publicarla en Maven Central por lo que añadiremos una serie de plugins que nos facilitarán la labor

Plugins de Gradle

Los plugins que vamos a incluir en nuestro gradle.build son tres:

  • maven-publish

  • io.github.gradle-nexus.publish-plugin

  • signing

El primero va a preparar nuestro proyecto para poder generar ficheros requeridos por maven (pom.xml, etc), mientras que el segundo es el encargado de subir al Nexus de SonaType los binarios y preparar la release. Por último signing se va a encargar de utilizar la clave gpg que hemos configurado para firmarlos

Así pues nuestro gradle.build se queda como:

plugins{
    id 'java-library'
    id 'maven-publish'
    id "io.github.gradle-nexus.publish-plugin" version '1.1.0'
    id 'signing'
}

repositories {
    mavenCentral()
}

group 'com.puravida-software'
archivesBaseName ='gif-generator'

sourceCompatibility = '1.8'
targetCompatibility = '1.8'

dependencies {
    // Use the latest Groovy version for Spock testing
    testCompile 'org.codehaus.groovy:groovy-all:2.5.4'
    // Use the awesome Spock testing and specification framework even with Java
    testCompile 'org.spockframework:spock-core:1.2-groovy-2.5'
    testCompile 'junit:junit:4.12'
}

javadoc {
    title = "$project.description"
}

java {
    withJavadocJar()
    withSourcesJar()
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            from(components.java)
            pom {
                name = 'PuraVida Software GifGenerator'
                description = 'A gif generator from an array of images'
                url = 'https://gitlab.com/puravida-software/gif-generator'
                properties = [:]
                licenses {
                    license {
                        name = 'The Apache License, Version 2.0'
                        url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                    }
                }
                developers {
                    developer {
                        id = 'jorge'
                        name = 'Jorge Aguilera'
                        email = 'jorge.aguilera@puravida-software.com'
                    }
                }
                scm {
                    connection = 'scm:git:git://puravida-software/gif-generator.git'
                    developerConnection = 'scm:git:ssh://puravida-software/gif-generator.git'
                    url = 'https://gitlab.com/puravida-software/gif-generator'
                }
            }
        }
    }
}

signing {
    sign publishing.publications.mavenJava
}


nexusPublishing {
    repositories {
        sonatype {  //only for users registered in Sonatype after 24 Feb 2021
            nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
            snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
        }
    }
}

Proceso

Para comprobar que todo es correcto y que podemos subir la libreria al Nexus ejecutaremos:

$ ./gradlew build publishToSonatype

Lo cual, si todo va bien, nos firmará la libreria y demás archivos y las subirá a nuestra área de staging (es decir, no hemos publicado nada en Maven, tranquilidad) donde podremos revisar si todo está bien.

En la salida del comando anterior te muestra la url, que en mi caso es https://s01.oss.sonatype.org/service/local/repositories/compuravida-software-XXXXXX/content/

NOTE

Cada vez que ejecutas una subida el identificador XXXXX irá incrementándose

Puedes acceder a tu repositorio para verificar las subidas en https://s01.oss.sonatype.org/#stagingRepositories

Seleccionando el elemento subido, puedes revisar la actividad del mismo y si presenta algún error que te impida publicarlo en Maven. Como ahora mismo sólo hemos hecho una subida, simplemente nos dirá que la actividad de subida ha ido bien.

A continuación lo que pretendemos es subir el artefacto y que se ejecuten todas las acciones que validen si nuestro artefacto puede ser desplegado en Maven Central:

$ ./gradlew publishToSonatype closeSonatypeStagingRepository

Y revisamos en la consola de Nexus la actividad de la última subida:

nexus gif generator

Si no has puesto bien una descripcion para el pom, autores, la licencia, etc puede que este paso te muestre errores. Por eso es recomendado ejecutarlo la primera vez de esta forma, para poder revisarlo y repetirlo hasta tener el ok de las validaciones. A mí me ha fallado la firma (tuve que publicar la firma en el servidor de ubuntu), mal puestas las urls al repo git, etc

Una vez que tengamos la subida y sus validaciones pasadas podemos liberar la primera versión simplemente pulando el icono de "Release" lo cual enviará nuestra librería a Maven Central en unos minutos (en el search aparecerá más tarde)

NOTA

Si recuerdas, en los pasos de creación de tickets nos decían que una vez hayamos hecho la primera release lo comentaramos en el ticket para ejecutar la sync con Maven. A mí no me dió tiempo!!! en cuestión de unos minutos había recibido el correo de que ya estaba publicada la release.

Desplegando sin manos

Una vez que hemos liberado nuestra primera versión de una forma cuasi-manual ya podremos liberar las siguientes ejecutando simplemente

$./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository

Lo cual ejecutará los dos pasos anteriores de forma automática

Control de versiones

Como tema tangencial voy a incluir un apartado sobre el control de versiones.

En mis proyectos personales intento aplicar (dentro de mi caos mental) semver , ya sabes, pasar de versión 1.2.0 a 1.3.0 cuando añado alguna funcionalidad nueva, y de versión 1.3.0 a 1.3.1 cuando arreglo algún defecto, etc

Si eres una persona cuidadosa o sigues una metodología probablemente no tengas muchos problemas. Yo personalmente uso un plugin (de los cientos que hay) que utiliza el estado de git para determinar la versión en la que te encuentras, de tal forma que no necesitas codificarla en ningún fichero

En mi caso uso el plugin reckon (https://github.com/ajoberstar/reckon) de tal forma que cuando estoy en disposición de liberar una versión (o parche) a Maven primero "cierro" la versión ejecutando:

$./gradlew build -Preckon.scope=minor -Preckon.stage=final reckonTagCreate

Este plugin comprueba que mi repositorio git se encuentra "limpio", con todos los commits hechos, y en base a los tags que existan y al stage que le indico determina la versión. Así mismo, si todo está correcto, me crea el tag correspondiente en el repo

Bolas extras

Andrés Almiray ha publicado JReleaser (https://jreleaser.org/) que promete publicar artefactos Java sin esfuerzo y al que hay que seguir de cerca porque viendo la documentación el proceso de liberar versiones suena muy fácil

Follow comments at Telegram group Or subscribe to the Channel Telegram channel

2019 - 2021 | Mixed with Bootstrap | Baked with JBake v2.6.5