Nous utilisons Gradle et Github Actions pour publier un projet Java dans le Sonatype Maven Central Repository

Dans cet article, je vais examiner en détail le processus de publication d'un artefact Java depuis le début via Github Actions dans le dépÎt Maven Central de Sonatype en utilisant le systÚme de build Gradle.

J'ai décidé d'écrire cet article en raison de l'absence d'un tutoriel adéquat en un seul endroit. J'ai dû recueillir toutes les informations par morceaux à partir de diverses sources, qui n'étaient pas toujours trÚs récentes. Pour ceux que cela intéresse, bienvenue sous le cut.

Création d'un dépÎt dans Sonatype

La premiĂšre Ă©tape consiste Ă  crĂ©er un dĂ©pĂŽt dans Maven Central de Sonatype. Pour cela, nous allons ici, nous inscrire et crĂ©er une nouvelle tĂąche, avec une demande de crĂ©ation de dĂ©pĂŽt pour nous. Nous saisissons notre GroupId du projet, URL du projet un lien vers le projet et SCM url un lien vers le systĂšme de contrĂŽle de version dans lequel se trouve le projet. GroupId Ceci doit ĂȘtre sous la forme com.example, com.example.domain, com.example.testsupport, ou peut Ă©galement prendre la forme d'un lien vers votre GitHub : github.com/yourusername -> io.github.yourusername. Dans tous les cas, vous devrez confirmer la propriĂ©tĂ© de ce domaine ou de ce profil. Si vous avez indiquĂ© un profil GitHub, on vous demandera de crĂ©er un dĂ©pĂŽt public avec le nom requis.

AprÚs un certain temps, une fois la propriété confirmée, votre GroupId sera créé et nous pourrons passer à l'étape suivante, la configuration de Gradle.

Configuration de Gradle

Au moment de la rĂ©daction de cet article, je n'ai trouvĂ© aucun plugin pour Gradle qui pourrait aider Ă  la publication de l'artefact. C'est Le seul plugin que j'ai trouvĂ©, cependant, l'auteur a renoncĂ© Ă  son soutien ultĂ©rieur. J'ai donc dĂ©cidĂ© de tout faire moi-mĂȘme, d'autant plus que ce n'est pas trop compliquĂ©.

La premiÚre chose à déterminer est les exigences de Sonatype pour la publication. Elles sont les suivantes :

  • La prĂ©sence des codes sources et de la JavaDoc, c'est-Ă -dire que doivent figurer -sources.jar et-javadoc.jar des fichiers. Comme indiquĂ© dans la documentation, s'il n'est pas possible de fournir les codes sources ou la documentation, vous pouvez crĂ©er un vide -sources.jar ou -javadoc.jar avec un simple README Ă  l'intĂ©rieur, afin de passer la vĂ©rification.
  • Tous les fichiers doivent ĂȘtre signĂ©s Ă  l'aide de GPG/PGPet .asc le fichier contenant la signature doit ĂȘtre inclus pour chaque fichier.
  • PrĂ©sence pom fichiers
  • Valeurs correctes groupId, artifactId et version. La version peut ĂȘtre une chaĂźne arbitraire et ne peut pas se terminer par -SNAPSHOT
  • La prĂ©sence de nom, description et url
  • Informations sur la licence, les dĂ©veloppeurs et le systĂšme de contrĂŽle de version

Ce sont les principales rĂšgles qui doivent ĂȘtre respectĂ©es lors de la publication. Des informations complĂštes sont disponibles ici.

Nous allons mettre en Ɠuvre ces exigences dans build.gradle fichier. Pour commencer, ajoutons toutes les informations nĂ©cessaires sur les dĂ©veloppeurs, la licence, le systĂšme de contrĂŽle de versions, ainsi que dĂ©finissons l'url, le nom et la description du projet. Pour ce faire, Ă©crivons une mĂ©thode simple :

def customizePom(pom) {
    pom.withXml {
        def root = asNode()

        root.dependencies.removeAll { dep ->
            dep.scope == "test"
        }

        root.children().last() + {
            resolveStrategy = DELEGATE_FIRST

            description 'Une description de l’artefact'
            name 'Nom de l’artĂ©fact'
            url 'https://github.com/login/projectname'
            organization {
                name 'com.github.login'
                url 'https://github.com/login'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/login/projectname/issues'
            }
            licenses {
                license {
                    name 'La Licence Apache, Version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/login/projectname'
                connection 'scm:https://github.com/login/projectname.git'
                developerConnection 'scm:git://github.com/login/projectname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'NomDuDev'
                    email 'email@dev.fr'
                }
            }
        }
    }
}

Ensuite, il faut indiquer que des -sources.jar et-javadoc.jar fichiers doivent ĂȘtre gĂ©nĂ©rĂ©s lors de la compilation. Pour cela, dans la section java , il faut ajouter ce qui suit :

java {
    withJavadocJar()
    withSourcesJar()
}

Passons Ă  la derniĂšre exigence, la configuration de la signature GPG/PGP. Pour cela, connectons le plugin signing:

plugins {
    id 'signing'
}

Et ajoutons la section :

signing {
    sign publishing.publications
}

Enfin, ajoutons la section publishing:

publishing {
    publications {
        mavenJava(MavenPublication) {
            customizePom(pom)
            groupId group
            artifactId archivesBaseName
            version version

            from components.java
        }
    }
    repositories {
        maven {
            url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            credentials {
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

Ici sonatypeUsername et sonatypePassword variables contenant le nom d'utilisateur et le mot de passe créés lors de l'inscription sur sonatype.org.

Ainsi, le final build.gradle devrait ressembler Ă  ceci :

Code complet build.gradle

plugins {
    id 'java'
    id 'maven-publish'
    id 'signing'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
    withJavadocJar()
    withSourcesJar()
}

group 'io.github.githublogin'
archivesBaseName = 'projectname'
version = System.getenv('RELEASE_VERSION') ?: "0.0.1"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}

test {
    useJUnitPlatform()
}

jar {
    from sourceSets.main.output
    from sourceSets.main.allJava
}

signing {
    sign publishing.publications
}

publishing {
    publications {
        mavenJava(MavenPublication) {
            customizePom(pom)
            groupId group
            artifactId archivesBaseName
            version version

            from components.java
        }
    }
    repositories {
        maven {
            url "https://oss.sonatype.org/service/local/staging/deploy/maven2"
            credentials {
                username sonatypeUsername
                password sonatypePassword
            }
        }
    }
}

def customizePom(pom) {
    pom.withXml {
        def root = asNode()

        root.dependencies.removeAll { dep ->
            dep.scope == "test"
        }

        root.children().last() + {
            resolveStrategy = DELEGATE_FIRST

            description 'Une description de l'artifact'
            name 'Nom de l'artifact'
            url 'https://github.com/login/projectname'
            organization {
                name 'com.github.login'
                url 'https://github.com/githublogin'
            }
            issueManagement {
                system 'GitHub'
                url 'https://github.com/githublogin/projectname/issues'
            }
            licenses {
                license {
                    name 'La licence Apache, version 2.0'
                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
                }
            }
            scm {
                url 'https://github.com/githublogin/projectname'
                connection 'scm:https://github.com/githublogin/projectname.git'
                developerConnection 'scm:git://github.com/githublogin/projectname.git'
            }
            developers {
                developer {
                    id 'dev'
                    name 'NomDuDev'
                    email 'email@dev.fr'
                }
            }
        }
    }
}

Je tiens à noter que nous obtenons la version à partir de la variable d'environnement : System.getenv('RELEASE_VERSION'). Nous la définirons lors de la construction et la prendrons à partir du nom de la balise.

Génération de la clé PGP

Une des exigences de Sonatype est de signer tous les fichiers à l'aide de la clé GPG/PGP. Pour cela, allons ici et téléchargeons l'utilitaire GnuPG pour notre systÚme d'exploitation.

  • GĂ©nĂ©rons une paire de clĂ©s : gpg --gen-key, saisissons le nom d'utilisateur, l'e-mail, et dĂ©finissons Ă©galement un mot de passe.
  • Nous dĂ©couvrons id notre clĂ© avec la commande : gpg --list-secret-keys --keyid-format short. L'ID sera indiquĂ© aprĂšs la barre, par exemple : rsa2048/9B695056
  • Publions la clĂ© publique sur le serveur https://keys.openpgp.org avec la commande : gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • Exportons la clĂ© secrĂšte Ă  un emplacement arbitraire, nous en aurons besoin par la suite : gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Configurer Github Actions

Passons à l'étape finale, configurons la compilation et la publication automatique en utilisant Github Actions.
Github Actions est une fonctionnalitĂ© qui permet d'automatiser le flux de travail, en rĂ©alisant un cycle CI/CD complet. La compilation, les tests et le dĂ©ploiement peuvent ĂȘtre dĂ©clenchĂ©s par divers Ă©vĂ©nements : un push de code, la crĂ©ation d'une release ou d'issues. Cette fonctionnalitĂ© est totalement gratuite pour les dĂ©pĂŽts publics.

Dans cette section, je vais montrer comment configurer la compilation et le push du code ainsi que le déploiement dans le dépÎt Sonatype lors de la sortie d'une release, ainsi que la configuration des secrets.

Configuration des secrets

Pour une compilation et un dĂ©ploiement automatiques, nous aurons besoin d'une sĂ©rie de valeurs secrĂštes, telles que l'ID de clĂ©, le mot de passe que nous avons saisi lors de la gĂ©nĂ©ration de la clĂ©, la clĂ© PGP elle-mĂȘme ainsi que le login/mot de passe pour Sonatype. Ils peuvent ĂȘtre dĂ©finis dans une section spĂ©ciale des paramĂštres du dĂ©pĂŽt :

Nous utilisons Gradle et Github Actions pour publier un projet Java dans le Sonatype Maven Central Repository

Nous définissons les variables suivantes :

  • SONATYPE_USERNAME/SONATYPE_PASSWORD — login/mot de passe que nous avons saisi lors de l'inscription sur Sonatype
  • SIGNING_KEYID/SIGNING_PASSWORD — ID de la clĂ© PGP et mot de passe dĂ©fini lors de la gĂ©nĂ©ration.

Je veux m'attarder un peu plus sur la variable GPG_KEY_CONTENTS. En effet, pour publier, nous avons besoin de la clé PGP privée. Pour l'intégrer dans les secrets, j'ai suivi l' instruction et j'ai également effectué plusieurs actions supplémentaires.

  • Chiffrons notre clĂ© avec gpg : gpg --symmetric --cipher-algo AES256 9B695056.gpg, en saisissant le mot de passe. Celui-ci doit ĂȘtre placĂ© dans la variable : SECRET_PASSPHRASE
  • Convertissons la clĂ© chiffrĂ©e obtenue en forme texte avec base64 : base64 9B695056.gpg.gpg > 9B695056.txt. Le contenu sera placĂ© dans la variable : GPG_KEY_CONTENTS.

Configuration de la compilation lors du push de code et de la création d'un PR

Tout d'abord, créez un dossier à la racine de votre projet : .github/workflows.

Dans celui-ci, placez un fichier, par exemple, gradle-ci-build.yml avec le contenu suivant :

name: build

on:
  push:
    branches:
      - master
      - dev
      - testing
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Configurer JDK 8
        uses: actions/setup-java@v1
        with:
          java-version: 8

      - name: Construire avec Gradle
        uses: eskatos/gradle-command-action@v1
        with:
          gradle-version: current
          arguments: build -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}

Ce workflow sera exécuté lors d'un push dans les branches master, dev et testing, ainsi que lors de la création de pull requests.

Dans la section jobs, les Ă©tapes qui doivent ĂȘtre rĂ©alisĂ©es selon les Ă©vĂ©nements spĂ©cifiĂ©s sont indiquĂ©es. Dans ce cas, nous construirons sur la derniĂšre version d'ubuntu, utiliserons Java 8, ainsi qu'un plugin pour Gradle. eskatos/gradle-command-action@v1, qui, en utilisant la derniĂšre version de l'assembleur, exĂ©cutera les commandes spĂ©cifiĂ©es dans arguments. Variables secrets.SONATYPE_USERNAME et secrets.SONATYPE_PASSWORD ce sont des secrets que nous avons dĂ©finis prĂ©cĂ©demment.

Les résultats de la construction seront reflétés dans l'onglet Actions :

Nous utilisons Gradle et Github Actions pour publier un projet Java dans le Sonatype Maven Central Repository

Déploiement automatique à la publication d'une nouvelle version

Pour le déploiement automatique, nous allons créer un fichier de workflow séparé gradle-ci-publish.yml:

name: publish

on:
  push:
    tags:
      - 'v*'

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: Configurer JDK 8
        uses: actions/setup-java@v1
        with:
          java-version: 8

      - name: Préparer à publier
        run: |
          echo '${{secrets.GPG_KEY_CONTENTS}}' | base64 -d > publish_key.gpg
          gpg --quiet --batch --yes --decrypt --passphrase="${{secrets.SECRET_PASSPHRASE}}" 
          --output secret.gpg publish_key.gpg
          echo "::set-env name=RELEASE_VERSION::${GITHUB_REF:11}"

      - name: Publier avec Gradle
        uses: eskatos/gradle-command-action@v1
        with:
          gradle-version: current
          arguments: test publish -Psigning.secretKeyRingFile=secret.gpg -Psigning.keyId=${{secrets.SIGNING_KEYID}} -Psigning.password=${{secrets.SIGNING_PASSWORD}} -PsonatypeUsername=${{secrets.SONATYPE_USERNAME}} -PsonatypePassword=${{secrets.SONATYPE_PASSWORD}}

Le fichier est pratiquement identique au précédent, à l'exception de l'événement qui va le déclencher. Dans ce cas, il s'agit de l'événement de création d'une balise dont le nom commence par v.

Avant le déploiement, nous devons extraire la clé PGP des secrets et la placer à la racine du projet, ainsi que la déchiffrer. Ensuite, nous devons définir une variable d'environnement spéciale RELEASE_VERSION à laquelle nous faisons référence dans gradle.build fichier. Tout cela est fait dans la section Préparer à publier. Nous récupérons notre clé à partir de la variable GPG_KEY_CONTENTS, convertissons-la en fichier gpg, puis la déchiffrons et la plaçons dans le fichier secret.gpg.

Ensuite, nous accédons à une variable spéciale GITHUB_REF, à partir de laquelle nous pouvons extraire la version que nous avons définie lors de la création de la balise. Cette variable a dans ce cas la valeur refs/tags/v0.0.2 de laquelle nous coupons les 11 premiers caractÚres pour obtenir la version exacte. Ensuite, nous utilisons normalement les commandes Gradle pour la publication : test publish

Vérification des résultats du déploiement dans le référentiel Sonatype

AprÚs la création de la version, le workflow décrit dans la section précédente doit se déclencher. Pour ce faire, nous créons une version :

Nous utilisons Gradle et Github Actions pour publier un projet Java dans le Sonatype Maven Central Repository

oĂč le nom de la balise doit commencer par v. Lorsque vous appuyez sur Publier la version, si le workflow s'exĂ©cute avec succĂšs, nous pouvons nous rendre sur Sonatype Nexus pour vĂ©rifier cela :

Nous utilisons Gradle et Github Actions pour publier un projet Java dans le Sonatype Maven Central Repository

L'artefact est apparu dans le dĂ©pĂŽt Staging. Il apparaĂźt immĂ©diatement dans le statut Ouvert, puis il doit ĂȘtre manuellement transfĂ©rĂ© au statut Clos en appuyant sur le bouton correspondant. AprĂšs vĂ©rification du respect de toutes les exigences, l'artefact passe au statut Clos et n'est plus disponible pour modification. Sous cette forme, il sera intĂ©grĂ© dans MavenCentral. Si tout est en ordre, vous pouvez appuyer sur le bouton Publication, l'artefact sera alors transfĂ©rĂ© dans le dĂ©pĂŽt Sonatype.

Pour que l'artefact atteigne MavenCentral, il est nĂ©cessaire d'en faire la demande dans la tĂąche que nous avons créée au tout dĂ©but. Cela doit ĂȘtre fait uniquement une fois, car c'est la premiĂšre publication. Pour les fois suivantes, cela n'est pas nĂ©cessaire, tout sera synchronisĂ© automatiquement. J'ai reçu la synchronisation rapidement, mais il a fallu environ 5 jours pour que l'artefact devienne disponible dans MavenCentral.

C'est tout, nous avons publié notre artefact dans MavenCentral.

Liens utiles

  • Semblable article, seulement la publication se fait par maven
  • Staging dĂ©pĂŽt Sonatype
  • Jira Sonatype, oĂč il est nĂ©cessaire de crĂ©er une tĂąche
  • Exemple dĂ©pĂŽt oĂč tout cela est configurĂ©

Source : habr.com

Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS đŸ”„ Acheter un hĂ©bergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster