Uso de acciones Gradle y Github para publicar un proyecto Java en el repositorio central de Sonatype Maven

En este artículo, quiero echar un vistazo detallado al proceso de publicación de un artefacto Java desde cero a través de las acciones de Github en el repositorio central de Sonatype Maven utilizando el recopilador Gradle.


Decidí escribir este artículo debido a la falta de un tutorial normal en un solo lugar. Toda la información tuvo que ser recopilada en piezas de varias fuentes, aunque no del todo fresca. A quién le importa, bienvenido al gato.


Crear un repositorio en Sonatype


El primer paso es crear un repositorio en Sonatype Maven Central. Para hacer esto, vaya aquí , regístrese y cree una nueva tarea, solicitándonos que creemos un repositorio para nosotros. Conducimos en nuestro GroupId del proyecto, el enlace URL del proyecto al proyecto y el enlace url de SCM al sistema de control de versiones en el que se encuentra el proyecto. El GroupId aquí debe tener la forma com.example, com.example.domain, com.example.testsupport, y también puede ser un enlace a su github: github.com/yourusername -> io.github.yourusername. En cualquier caso, deberá confirmar la propiedad de este dominio o perfil. Si especificó un perfil de github, le pedirán que cree un repositorio público con el nombre deseado.


Algún tiempo después de la confirmación, se creará su GroupId y podremos continuar con el siguiente paso, la configuración de Gradle.


Configurando Gradle


En el momento de escribir esto, no encontré ningún complemento para Gradle que pudiera ayudar con la publicación del artefacto. Este es el único complemento que he encontrado, sin embargo, el autor ha rechazado su apoyo adicional. Por lo tanto, decidí hacerlo todo yo mismo, ya que no es demasiado difícil hacerlo.


Lo primero que debe averiguar son los requisitos de publicación de Sonatype. Son los siguientes:


  • La presencia de códigos fuente y JavaDoc, es decir debe estar presente -sources.jary -javadoc.jararchivos. Como se indica en la documentación, si no es posible proporcionar códigos fuente o documentación, puede hacer un ficticio -sources.jaro -javadoc.jarcon un LÉAME simple para aprobar la prueba.
  • GPG/PGP, .asc , , .
  • pom
  • groupId, artifactId version. -SNAPSHOT
  • name, description url
  • ,

, . .


build.gradle . , , , url, . :


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

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

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

            description 'Some description of artifact'
            name 'Artifct name'
            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 'The Apache License, 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 'DevName'
                    email 'email@dev.ru'
                }
            }
        }
    }
}

, -sources.jar -javadoc.jar . java :


java {
    withJavadocJar()
    withSourcesJar()
}

, GPG/PGP . signing:


plugins {
    id 'signing'
}

:


signing {
    sign publishing.publications
}

, 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
            }
        }
    }
}

sonatypeUsername sonatypePassword , , sonatype.org.


, build.gradle :


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 'Some description of artifact'
            name 'Artifct name'
            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 'The Apache License, 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 'DevName'
                    email 'email@dev.ru'
                }
            }
        }
    }
}

, : System.getenv('RELEASE_VERSION'). .


PGP


Sonatype GPG/PGP . GnuPG .


  • : gpg --gen-key, , e-mail, .
  • id : gpg --list-secret-keys --keyid-format short. Id , : rsa2048/9B695056
  • https://keys.openpgp.org : gpg --keyserver https://keys.openpgp.org/ --send-keys 9B695056
  • , : gpg --export-secret-key 9B695056 > D:\\gpg\\9B695056.gpg

Github Actions


, , Github Actions.
Github Actions – , , CI/CD. , : , issues. .


Sonatype , .



, id , , , PGP , / Sonatype. :



:


  • SONATYPE_USERNAME/SONATYPE_PASSWORD — /, Sonatype
  • SIGNING_KEYID/SIGNING_PASSWORD — id PGP , .

GPG_KEY_CONTENTS . , PGP . , , .


  • gpg: gpg --symmetric --cipher-algo AES256 9B695056.gpg, . : SECRET_PASSPHRASE
  • base64: base64 9B695056.gpg.gpg > 9B695056.txt. : GPG_KEY_CONTENTS.

PR


: .github/workflows.


, , gradle-ci-build.yml :


name: build

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

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

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

master, dev testing, .


jobs , . ubuntu, Java 8, Gradle eskatos/gradle-command-action@v1, , arguments. secrets.SONATYPE_USERNAME secrets.SONATYPE_PASSWORD , .


Actions:




gradle-ci-publish.yml:


name: publish

on:
  push:
    tags:
      - 'v*'

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

      - name: Prepare to publish
        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: Publish with 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}}

, . , v.


PGP , . RELEASE_VERSION gradle.build . Prepare to publish. GPG_KEY_CONTENTS, gpg , , secret.gpg.


GITHUB_REF, , . refs/tags/v0.0.2 11 , . Gradle : test publish


Sonatype


, . :



v. Publish release, , Sonatype Nexus :



Staging . Open, Close, . , Close . MavenCentral. , Release, Sonatype.


, MavenCentral, , . , . , . , MavenCentral 5 .


Eso es todo, publicamos nuestro artefacto en MavenCentral.


Enlaces útiles


  • Artículo similar , solo publicación a través de Maven
  • Sonatype de repositorio de etapas
  • Jira Sonatype, en el que necesitas crear una tarea
  • Un repositorio de ejemplo donde todo esto está configurado

All Articles