Publicar y utilizar una dependencia de Java en un repositorio de Git con GitHub Packages

Escrito por el .
java planeta-codigo
Enlace permanente Comentarios

El software ofrecido como servicio tiene la ventaja principal de que delega en el proveedor del servicio su administración. El delegar la administración del servicio está motivada por tener ciertas garantías de que el software tiene mayor disponibilidad y fiabilidad. A cambio de esa administración el proveedor del software como servicio establece un precio por su SaaS que llega a compensar tener que administrar el servicio uno mismo que también tiene unos costes en servidor, personal, fiabilidad y también tiempo de dedicación. Una de las funcionalidades que requiere una infraestructura en una organización para desarrollar sus aplicaciones es un repositorio de artefactos o paquetes, GitHub Packages el servicio de repositorio de artefactos compatibles con los artefactos producidos por los principales lenguajes de programación para compartir bibliotecas de código entre ellos Java y JavaScript y tecnologías de contenedores como imágenes Docker.

GitHub

Java

La forma de compartir y reutilizar código en Java es a través de librerías java que se incluyen como dependencias en los proyectos que las quieran usar. Las dependencias pueden tener a su vez dependencias que han de incluirse de forma transitiva, ni Java ni el JDK ofrece soporte para gestionar las dependencias en un proyecto más allá de los módulos del incorporados en Java 9 pero que no cubren la resolución de dependencias ni el versionado.

Para gestionar las dependencias en un proyecto Java se ha de utilizar la herramienta de construcción como Gradle o Maven. Estas herramientas se encargan de descargar las versiones correctas y las dependencias transitivas declaradas en el proyecto y sus dependencias. Las dependencias se guardan en repositorios de Maven que además de los propios archivos Java de las librerías almacenan metadatos en los que se incluyen qué dependencias tiene cada una de esas librerías.

Los repositorios de dependencias se almacenan en un servidor que se encarga de permitir acceder y agregar nuevos artefactos. El software de servidor Nexus como repositorio de artefactos tiene una versión open source que se puede descargar e instalar para ofrecer el servicio.

Aunque Nexus permite disponer de un software de servidor para guardar artefactos de un repositorio Maven es un servidor que tiene los inconvenientes de un servidor administrado por uno mismo. Administrar el servidor que no debería fallar pero ocasionalmente requiere dedicación para aplicar actualizaciones de seguridad, actualizaciones a nuevas versiones o cuando deja de funcionar. Un servidor administrado por uno mismo requiere dedicarle tiempo del que a veces no se dispone. Para no tener que dedicar tiempo a mantener un servidor la opción de los software como servicio o los servicios gestionados son interesantes. Los software como servicio suelen tener un coste pero puede compensar teniendo en cuenta el coste de dedicarle a administrar uno mismo el servicio, el coste de la infraestructura en la que alojarlo, además se tiene más garantías de que el software administrado tenga mayor disponibilidad y fiabilidad.

Una alternativa a Nexus es el servicio GitHub Packages que permite almacenar artefactos para distintos repositorios entre ellos artefactos de Maven, imágenes de Docker y paquetes de npm.

El servicio de Github Packages

GitHub Packages es un servicio de GitHub que permite almacenar artefactos de distintos tipos y paquetes de las principales plataformas de programación como librerías jar de Java, paquetes npm de JavaScript e imágenes de contenedores de Docker. Un repositorio de GitHub Packages se almacena en un repositorio de git de GitHub.

Los artefactos de Java se pueden publicar como en cualquier otro repositorio de Maven teniendo las credenciales de autenticación y los repositorios se pueden utilizar para descargar de ellos las dependencias al incluirlas en el archivo de construcción del proyecto.

Tiene unos límites de uso bastante reducidos en sus planes de precios de GitHub Packages que están divididos en espacio de almacenamiento usado y datos transferidos, sin embargo no dependen del número de usuarios.

Github Packages permite hacer las funcionalidades de repositorio de artefactos pero no soporta todos los tipos de artefactos de todos los lenguajes. Google Artifact Registry también ofrece un servicio para almacenar los artefactos resultado de compilación y Amazon con AWS CodeArtifact también tiene su servicio administrado de artefactos.

Crear y publicar una librería de Maven en GitHub Packages

Crear la librería

El siguiente proyecto es un proyecto de Gradle que contiene una clase de utilidad que se desea distribuir como una librería para compartirla y que se pueda utilizar en cualesquiera otros proyectos de Gradle o Maven que la necesiten. El código Java de la clase no tiene nada especial y en este caso simplemente contiene una utilidad para ofuscar una cadena de texto.

 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
package io.github.picodotdev.blogbitix.githubpackages.utils;

import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;

public class Utils {

  public static String mask(String string) {
    return mask(string, 2, 6);
  }

  public static String mask(String string, int chars, int max) {
    String substring = string.substring(0, Math.min(chars, string.length()));
    String mask = repeat("*", Math.max(0, max - chars));
    return substring + mask;
  }

  public static Map<String, String> properties(String resource) throws IOException {
    Properties properties = new Properties();
    properties.load(Utils.class.getResourceAsStream(resource));
    return properties.entrySet().stream().collect(Collectors.toMap(it -> it.getKey().toString(), it -> it.getValue().toString()));
  }

  private static String repeat(String string, int count) {
    return string.repeat(count);
  }
}
utils/Utils.java
 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
plugins {
    id 'java'
    id 'maven-publish'
}

project.with {
    group = 'io.github.picodotdev.blogbitix.githubpackages'
    version = '1.1'
}

repositories {
    mavenCentral()
}

publishing {
    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/picodotdev/github-packages")
            credentials {
                username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
                password = project.findProperty("gpr.key") ?: System.getenv("TOKEN")
            }
        }
    }
    publications {
        gpr(MavenPublication) {
            from(components.java)
        }
    }
}
utils/build.gradle

Crear el repositorio de Maven en GitHub

Cada repositorio de git permite generar artefactos o paquetes para que puedan ser descargados. Para agrupar todos los artefactos se puede crear un repositorio específico de git y publicar en él todos los artefactos de modo que en los proyecto únicamente sea necesario incluir la configuración de un único repositorio.

Publicar la librería en el repositorio

Para publicar artefactos en el repositorio de Maven se requieren unas credenciales para limitar publicar únicamente artefactos a aquellas personas o procesos que tengan permisos. Una de las formas de publicar paquetes es utilizar un Personal Access Token o PAT al que se asocian unos permisos y se generan desde la sección Settings de la cuenta dentro de Developer settings y Personal access tokens.

GitHub Personal Access Token

GitHub Personal Access Token

Con el PAT y cambiando la configuración de la definición del proyecto de la herramienta de construcción el artefacto de cada uno de los módulos se publica en el repositorio de Git con el comando publish proporcionado por el plugin maven-publish.

1
2
3
#!/usr/bin/env bash

./gradlew publish
gradlew-publish.sh

Para no guardar el PAT en el repositorio de código fuente en el archivo de construcción Gradle permite externalizar las propiedades en el archivo de configuración de Gradle en un archivo dotfile ubicado en ~/.gradle/gradle.properties.

1
2
gpr.user=picodotdev
gpr.key=ghp_Oj*****
.gradle/gradle.properties

Una vez publicado el artefacto desde la administración del repositorio es posible descargar cada uno de los elementos del artefacto incluso eliminarlos asi como ver las diferentes versiones publicadas del mismo artefacto. En la siguiente imagen se muestra el artefacto anterior publicado en el repositorio con GitHub Packages.

Artefacto de Maven publicado un repositorio con GitHub Packages

Artefacto de Maven publicado un repositorio con GitHub Packages

Usar la librería del repositorio de Maven

Una vez publicado el artefacto en el repositorio de Maven para usarlo hay que declarar de forma explícita la ubicación del repositorio de Maven de GitHub Packages y las credenciales para acceder al repositorio.

Con esta definición Gradle se encarga de descargar la dependencia y hacer que esté disponible en tiempo de compilación y ejecución como cualquier otra dependencia descargada del repositorio por defecto Maven Central donde generalmente se publican las dependencias.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package io.github.picodotdev.blogbitix.githubpackages.app;

import io.github.picodotdev.blogbitix.githubpackages.utils.Utils;

public class Main {

  public static void main(String[] args) {
    System.out.println(Utils.mask("Hello World!"));
  }
}
app/Main.java
 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
plugins {
    id 'application'
}

project.with {
    group = 'io.github.picodotdev.blogbitix.githubpackages'
    version = '1.0'
}

repositories {
    mavenCentral()
}

repositories {
    maven {
        url = uri("https://maven.pkg.github.com/picodotdev/github-packages")
        credentials {
            username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
            password = project.findProperty("gpr.key") ?: System.getenv("TOKEN")
        }
    }
}

dependencies {
    implementation 'io.github.picodotdev.blogbitix.githubpackages:utils:1.1'
}

application {
    mainClass = 'io.github.picodotdev.blogbitix.githubpackages.app.Main'
}
app/build.gradle
1
2
$ ./gradlew app:run
He****
gradlew-app-run.sh
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:
./gradlew app:run


Comparte el artículo: