Usando accións Gradle e Github para publicar o proxecto Java no repositorio central de Sonatype Maven

Neste artigo, quero botar unha ollada máis de cerca ao proceso de publicación dun artefacto Java desde cero a través de Github Actions para o repositorio central de Sonatype Maven usando o constructor Gradle.

Decidín escribir este artigo debido á falta dun titorial normal nun só lugar. Toda a información tiña que ser recollida peza a peza de diversas fontes, ademais, non totalmente fresca. Quen lle importa, benvido baixo gato.

Creando un repositorio en Sonatype

O primeiro paso é crear un repositorio en Sonatype Maven Central. Para iso imos aquí, rexistrarse e crear unha nova tarefa, solicitándonos que creemos un repositorio. Conducimos no noso GroupId proxecto, URL do proxecto ligazón do proxecto e URL SCM unha ligazón ao sistema de control de versións no que se atopa o proxecto. GroupId aquí debería ter o formato com.example, com.example.domain, com.example.testsupport e tamén pode ter a forma dunha ligazón ao teu github: github.com/yourusername -> io.github.yourusername. En calquera caso, terás que verificar a propiedade deste dominio ou perfil. Se especificaches un perfil github, pediráselle que crees un repositorio público co nome desexado.

Algún tempo despois da confirmación, crearase o teu GroupId e poderemos pasar ao seguinte paso, a configuración de Gradle.

Configurando Gradle

No momento de escribir este artigo, non atopei complementos de Gradle que puidesen axudar a publicar o artefacto. El o único complemento que atopei, con todo, o autor negouse a apoialo. Polo tanto, decidín facer todo eu, xa que non é moi difícil facelo.

O primeiro que hai que descubrir son os requisitos de Sonatype para publicar. Son os seguintes:

  • Dispoñibilidade de códigos fonte e JavaDoc, é dicir. debe asistir -sources.jar и-javadoc.jar arquivos. Como se indica na documentación, se non é posible proporcionar códigos fonte ou documentación, podes facer un simulacro -sources.jar ou -javadoc.jar cun sinxelo README dentro para pasar a proba.
  • Todos os ficheiros deben estar asinados con GPG/PGPE .asc para cada expediente deberá incluírse o ficheiro que conteña a sinatura.
  • dispoñibilidade pom arquivo
  • Valores correctos groupId, artifactId и version. A versión pode ser unha cadea arbitraria e non pode rematar con -SNAPSHOT
  • Requírese presenza name, description и url
  • A presenza de información sobre a licenza, os desenvolvedores e o sistema de control de versións

Estas son as normas básicas que se deben seguir á hora de publicar. Información completa dispoñible aquí.

Implementamos estes requisitos en build.gradle arquivo. En primeiro lugar, imos engadir toda a información necesaria sobre os desenvolvedores, licenzas, sistema de control de versións, e tamén establecer o URL, nome e descrición do proxecto. Escribamos un método sinxelo para iso:

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 protected]'
                }
            }
        }
    }
}

A continuación, cómpre especificalo durante a montaxe xerada -sources.jar и-javadoc.jar arquivos. Para esta sección java cómpre engadir o seguinte:

java {
    withJavadocJar()
    withSourcesJar()
}

Pasemos ao último requisito, configurar unha sinatura GPG/PGP. Para iso, conecte o complemento signing:

plugins {
    id 'signing'
}

E engade unha sección:

signing {
    sign publishing.publications
}

Finalmente, imos engadir unha sección 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
            }
        }
    }
}

Aquí sonatypeNome de usuario и sonatypePassword variables que conteñen o inicio de sesión e o contrasinal creados durante o rexistro sonatype.org.

Así a final build.gradle quedará así:

Código completo 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 protected]'
                }
            }
        }
    }
}

Quero notar que obtemos a versión da variable de ambiente: System.getenv('RELEASE_VERSION'). Expoñerémolo durante a montaxe e tomarémolo do nome da etiqueta.

Xeración de claves PGP

Un dos requisitos de Sonatype é que todos os ficheiros estean asinados cunha clave GPG/PGP. Para iso imos aquí e descarga a utilidade GnuPG para o teu sistema operativo.

  • Xeramos un par de claves: gpg --gen-key, introduza un nome de usuario, un correo electrónico e tamén configure un contrasinal.
  • Descubrindo id nosa chave co comando: gpg --list-secret-keys --keyid-format short. O ID especificarase despois da barra, por exemplo: rsa2048/9B695056
  • Publicación da chave pública no servidor https://keys.openpgp.org comando: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • Exportamos a clave secreta a un lugar arbitrario, necesitarémola no futuro: gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Configurando accións de Github

Pasemos á fase final, configure a compilación e a publicación automática usando Github Actions.
Github Actions é unha función que che permite automatizar o fluxo de traballo implementando un ciclo completo de CI/CD. A creación, proba e implantación pódense activar por varios eventos: push de código, creación de versións ou problemas. Esta funcionalidade é absolutamente gratuíta para os repositorios públicos.

Nesta sección, mostrarei como configurar código de compilación e inserción e implementación no repositorio de Sonatype na súa publicación, así como configurar segredos.

Establecemos segredos

Para a montaxe e o despregamento automáticos, necesitamos unha serie de valores secretos, como o ID da chave, o contrasinal que introducimos ao xerar a chave, a propia chave PGP e o inicio de sesión/contrasinal de Sonatype. Podes configuralos nunha sección especial na configuración do repositorio:

Usando accións Gradle e Github para publicar o proxecto Java no repositorio central de Sonatype Maven

Establecemos as seguintes variables:

  • SONATYPE_USERNAME / SONATYPE_PASSWORD - inicio de sesión / contrasinal que introducimos ao rexistrarse con Sonatype
  • SIGNING_KEYID/SIGNING_PASSWORD — ID da chave PGP e contrasinal definidos durante a xeración.

Quero determe na variable GPG_KEY_CONTENTS con máis detalle. O caso é que para a publicación necesitamos unha clave PGP privada. Para publicalo nos segredos, usei instrución e, ademais, realizou unha serie de accións.

  • Imos cifrar a nosa chave con gpg: gpg --symmetric --cipher-algo AES256 9B695056.gpgintroducindo un contrasinal. Debe colocarse nunha variable: SECRET_PASSPHRASE
  • Imos traducir a clave cifrada recibida nun formulario de texto usando base64: base64 9B695056.gpg.gpg > 9B695056.txt. O contido colocarase na variable: GPG_KEY_CONTENTS.

Construír a configuración ao presionar código e crear PR

Primeiro tes que crear un cartafol na raíz do teu proxecto: .github/workflows.

Nel, marca o ficheiro, por exemplo, gradle-ci-build.yml co seguinte contido:

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

Este fluxo de traballo executarase ao empurrar a ramas master, dev и testing, tamén ao crear solicitudes de extracción.

A sección de traballos especifica os pasos a executar nos eventos especificados. Neste caso, construiremos a última versión de ubuntu, usaremos Java 8 e tamén usaremos o complemento para Gradle eskatos/gradle-command-action@v1que, usando a versión máis recente do constructor, executará os comandos especificados en arguments. Variables secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD estes son os segredos que preguntamos anteriormente.

Os resultados da compilación reflectiranse na pestana Accións:

Usando accións Gradle e Github para publicar o proxecto Java no repositorio central de Sonatype Maven

Implementación automática cando se lanza unha nova versión

Imos crear un ficheiro de fluxo de traballo separado para a implementación automática 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}}

O ficheiro é case idéntico ao anterior, agás o evento no que se desencadeará. Neste caso, este é o evento de crear unha etiqueta cun nome que comeza por v.

Antes da implantación, necesitamos extraer a clave PGP dos segredos e colocala na raíz do proxecto, así como descifrala. A continuación, necesitamos establecer unha variable de ambiente especial RELEASE_VERSION ao que nos referimos gradle.build arquivo. Todo isto faise na sección Prepare to publish. Obtemos a nosa clave da variable GPG_KEY_CONTENTS, traducímola a un ficheiro gpg e despois desciframos poñéndoa no ficheiro secret.gpg.

A continuación, pasamos a unha variable especial GITHUB_REF, da que podemos obter a versión que establecemos ao crear a etiqueta. Esta variable é relevante neste caso. refs/tags/v0.0.2 do que cortamos os primeiros 11 caracteres para obter unha versión específica. A continuación, usamos os comandos estándar de Gradle para publicar: test publish

Comprobando os resultados da implantación no repositorio de Sonatype

Unha vez creada a versión, debería comezar o fluxo de traballo descrito na sección anterior. Para iso, crea unha versión:

Usando accións Gradle e Github para publicar o proxecto Java no repositorio central de Sonatype Maven

o nome da etiqueta debe comezar por v. Se despois de facer clic en Publicar versión, o fluxo de traballo remata con éxito, podemos ir a Sonatype Nexus para asegurarse:

Usando accións Gradle e Github para publicar o proxecto Java no repositorio central de Sonatype Maven

O artefacto apareceu no repositorio de Staging. Aparece inmediatamente no estado Aberto, entón debe transferirse manualmente ao estado Pechar premendo o botón correspondente. Despois de comprobar que se cumpren todos os requisitos, o artefacto pasa ao estado Pechar e xa non está dispoñible para a súa modificación. Deste xeito, rematará en MavenCentral. Se todo está ben, podes premer o botón Solte, e o artefacto acabará no repositorio de Sonatype.

Para que o artefacto entre en MavenCentral, debes solicitalo na tarefa que creamos ao principio. Só tes que facelo unha vez, polo que publicamos por primeira vez. En tempos posteriores, isto non é necesario, todo sincronizarase automaticamente. Activaron a sincronización para min rapidamente, pero o artefacto tardou uns 5 días en estar dispoñible en MavenCentral.

Iso é todo, publicamos o noso artefacto en MavenCentral.

Ligazóns útiles

  • Semellante artigo, só publica a través de Maven
  • Posición en escena repositorio Sonatipo
  • Jira Sonatipo no que crear a tarefa
  • Exemplo repositorio onde está todo configurado

Fonte: www.habr.com