Publicar una versión
Cómo se publica resilience4cata en Maven Central. Todo usa los plugins propios de Gradle maven-publish y signing; no hay plugins de publicación de terceros.
Qué se publica
| artifactId | Contenido |
|---|---|
resilience4cata-annotations |
jar, fuentes, Javadoc, POM, metadatos de módulo de Gradle |
resilience4cata-core |
lo mismo; depende de org.jspecify:jspecify (scope compile) |
resilience4cata-processor |
lo mismo; depende de resilience4cata-annotations (scope runtime, para el processor path) |
resilience4cata-config |
lo mismo; depende de resilience4cata-core (compile) |
resilience4cata-micrometer |
lo mismo; depende de resilience4cata-core y de io.micrometer:micrometer-core (compile) |
resilience4cata-testkit |
lo mismo; depende de resilience4cata-core (compile) |
resilience4cata-opentelemetry |
lo mismo; depende de resilience4cata-core y de io.opentelemetry:opentelemetry-api (compile) |
resilience4cata-junit |
lo mismo; depende de resilience4cata-testkit y de org.junit.jupiter:junit-jupiter-api 5.10.5 (compile) |
resilience4cata-all |
jar (solo module-info), fuentes, un jar de Javadoc con un README (javadoc rechaza un módulo sin paquetes), POM, metadatos de módulo; depende de core, annotations y config (compile) |
resilience4cata-bom |
solo POM y metadatos de módulo: restricciones sobre todos los módulos anteriores, en la misma versión |
groupId dev.resilience4cata. Cada jar lleva LICENSE y NOTICE (Apache-2.0) en META-INF, y los archivos son reproducibles (sin marcas de tiempo, orden fijo). examples, benchmark y stress no se publican.
Cómo está organizado el build
r4c.publish(conventions/.../PublishConventionPlugin.java), aplicado por cada módulo publicado: una publicaciónmavenJavacon los campos de POM que exige Central (name, description tomada deldescriptiondel módulo, url, licencia, developer, scm, issue tracker), la firma, y un repositorio de archivoscentralStagingenbuild/central-staging.r4c.central(CentralConventionPlugin.java), aplicado por elbuild.gradleraíz:centralBundleborrabuild/central-staging, publica ahí todos los módulos, lo verifica y lo comprime enbuild/central-bundle.zip. La verificación falla con una versión-SNAPSHOT, un módulo ausente, un jar de fuentes o de Javadoc ausente, o cualquier archivo sin su firma.ascy sus checksums.md5/.sha1. Solo entran los archivos de la versión publicada (sinmaven-metadata.xml), y sin los checksums.sha256/.sha512que Gradle también escribe: Central no los exige, y cada archivo del bundle cuenta para la cuota mensual de archivos del Central Portal (1.000 archivos en el plan gratuito; una release de todos los módulos son unos 250 archivos).centralUploadenvía el zip a la Publisher API del Central Portal (POST /api/v1/publisher/upload,publishingType=USER_MANAGED). Nada depende de esta tarea, así quebuildnunca sube nada, y falla sin token. Central valida un deploymentUSER_MANAGEDy luego lo deja esperando en el portal hasta que alguien pulse Publish.
apiCompatibility(ApiCompatibilityTask.java, que añader4c.publishy ejecutacheck, así que tambiénbuildy el CI): japicmp compara el jar de cada módulo publicado con el mismo artefacto enr4c.apiBaseline(engradle.properties: la última release) descargado de Maven Central, miembros públicos y protegidos, compatibilidad binaria y de código fuente. La API son los paquetes que el módulo exporta enmodule-info.java, or4cApi.packagesen su build file si no tiene (micronaut,quarkus:runtime); un módulo que no exporta ninguno (processor,quarkus:deployment) se omite, igual que un módulo sin base (nuevo desde la última release, o sin conexión). El reporte está en<módulo>/build/reports/api-compatibility/api-changes.md. Antes de la 1.0 un cambio incompatible es un aviso; desde la 1.0 hace fallar el build salvo que cambie la versión mayor. japicmp corre con el Worker API de Gradle, así que no añade ningún plugin de Gradle.opentelemetryreporta su versión como versión del instrumentation scope de OpenTelemetry:processResourcesescribe laversiondel build endev/resilience4cata/opentelemetry/version.properties, así que una release (también con-Pversion=…) no necesita ningún paso manual.- Sin clave no se firma, así que
./gradlew buildy./gradlew publishToMavenLocalno necesitan secretos.
Propiedades de Gradle (o variables de entorno ORG_GRADLE_PROJECT_<nombre>):
| Propiedad | La usa | Significado |
|---|---|---|
signingInMemoryKey |
firma | Clave secreta en ASCII armor (gpg --armor --export-secret-keys <id>) |
signingInMemoryKeyPassword |
firma | Su passphrase |
signingInMemoryKeyId |
firma | Opcional: id de una subclave de firma |
centralPortalUsername, centralPortalPassword |
centralUpload |
User token del Central Portal |
r4c.scmRepository |
POM | owner/repositorio de GitHub, en gradle.properties |
r4c.developerId, r4c.developerName |
POM | Entrada de developer, en gradle.properties |
Guarda los secretos en ~/.gradle/gradle.properties o en el entorno, nunca en el repositorio. Con el configuration cache activo, Gradle guarda los valores de las propiedades en su caché (cifrada) bajo .gradle/; usa --no-configuration-cache al firmar en una máquina compartida.
Configuración inicial (una sola vez)
-
Crea una cuenta en central.sonatype.com.
-
Verifica el namespace
dev.resilience4cata: en el portal, Namespaces → Add namespace; agrega la clave de verificación que muestra como registro DNS TXT enresilience4cata.devy pulsa Verify. -
Genera un user token (View Account → Generate User Token). Sus dos partes son
centralPortalUsernameycentralPortalPassword. -
Crea una clave de firma y publica la parte pública en un keyserver que Central consulte (por ejemplo
keys.openpgp.orgokeyserver.ubuntu.com):gpg --quick-gen-key "resilience4cata releases <you@example.com>" rsa4096 sign 2y gpg --keyserver keys.openpgp.org --send-keys <key id> gpg --armor --export-secret-keys <key id> # valor de signingInMemoryKey -
Para el workflow de GitHub, crea el environment
production(Settings → Environments; el job de release lo declara) y agrégale los secretosSIGNING_KEY,SIGNING_KEY_PASSWORDy, para subir desde CI,CENTRAL_PORTAL_USERNAMEyCENTRAL_PORTAL_PASSWORD. Restringe el environment a los tagsv*para que solo un tag de release pueda leerlos. -
Pon en
gradle.propertiesel repositorio real de GitHub enr4c.scmRepository(y las propiedades de developer).
Pasos de una versión
- Revisa los cambios de API desde la última release:
./gradlew apiCompatibility, y lee los avisos y*/build/reports/api-compatibility/api-changes.md; todo cambio incompatible va en el changelog. - Fija la versión en
gradle.properties:version=0.1.0. Haz commit (release 0.1.0) y el tag:git tag v0.1.0; luego sube el commit y el tag. - Arma el bundle, de una de dos formas:
- en CI: ejecuta el workflow release (
.github/workflows/release.yml) sobre el tagv0.1.0. Primero corre el build completo con JDK 21 y 25 (el mismo workflowbuilddel CI, sobre el commit del tag), y solo después compila, firma, verifica, guardacentral-bundle.zipcomo artefacto del workflow y lo sube si están los secretos del token; o - en local:
./gradlew build centralBundle --no-configuration-cachecon las propiedades de firma definidas.
- en CI: ejecuta el workflow release (
- Sube el bundle, si CI no lo hizo:
./gradlew centralUpload -x centralBundle, o en el portal Publish Component y eligebuild/central-bundle.zip. - En el portal, abre Deployments, espera el estado Validated, revisa los archivos y pulsa Publish (o Drop para descartarlo). Los artefactos llegan a Maven Central en unos 30 minutos.
- Pasa a la siguiente versión de desarrollo:
version=0.2.0-SNAPSHOT, commit. - Cuando la versión ya aparezca en Maven Central, actualiza la versión escrita en la documentación y en el
README.mdraíz (snippets de build del sitio, portadas, changelog):grep -rn "0\.1\.0" README.md docs/en docs/es. Fija tambiénr4c.apiBaseline=0.1.0engradle.properties, para queapiCompatibilitycompare con la nueva release.
Para revisar un bundle sin publicar nada, ármalo con una clave desechable y una versión de release:
gpg --quick-gen-key "throwaway <throwaway@example.invalid>" rsa3072 sign 1d
ORG_GRADLE_PROJECT_signingInMemoryKey="$(gpg --armor --export-secret-keys throwaway@example.invalid)" \
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword=<passphrase> \
./gradlew centralBundle -Pversion=0.1.0 --no-configuration-cache
unzip -l build/central-bundle.zip