Použití akcí Gradle a Github k publikování projektu Java do centrálního úložiště Sonatype Maven

V tomto článku se chci podrobně podívat na proces publikování Java artefaktu od nuly prostřednictvím Github Actions do centrálního úložiště Sonatype Maven pomocí Gradle builderu.

Tento článek jsem se rozhodl napsat kvůli nedostatku normálního tutoriálu na jednom místě. Veškeré informace bylo nutné sbírat kus po kuse z různých zdrojů, navíc ne zcela čerstvé. Koho to zajímá, vítej pod kočkou.

Vytvoření úložiště v Sonatype

Prvním krokem je vytvoření úložiště v Sonatype Maven Central. K tomu jdeme zde, zaregistrujte se a vytvořte nový úkol a požádejte nás o vytvoření úložiště. Jezdíme v našem GroupId projekt, URL projektu odkaz na projekt a URL SCM odkaz na systém správy verzí, ve kterém se projekt nachází. GroupId zde by mělo být ve tvaru com.example, com.example.domain, com.example.testsupport a může být také ve formě odkazu na váš github: github.com/vaše uživatelské jméno -> io.github.vaše uživatelské jméno. V každém případě budete muset ověřit vlastnictví této domény nebo profilu. Pokud jste zadali profil github, budete požádáni o vytvoření veřejného úložiště s požadovaným názvem.

Nějaký čas po potvrzení bude vaše GroupId vytvořeno a můžeme přejít k dalšímu kroku, konfiguraci Gradle.

Konfigurace Gradle

V době psaní tohoto článku jsem nenašel Gradle pluginy, které by mohly pomoci s publikováním artefaktu. To jediný plugin, který jsem našel, jej však autor odmítl dále podporovat. Proto jsem se rozhodl udělat vše sám, protože to není příliš obtížné.

První věc, kterou je třeba zjistit, jsou požadavky Sonatype na publikování. Jsou to následující:

  • Dostupnost zdrojových kódů a JavaDoc, tzn. se musí zúčastnit -sources.jar и-javadoc.jar soubory. Jak je uvedeno v dokumentaci, pokud není možné poskytnout zdrojové kódy nebo dokumentaci, můžete vytvořit figurínu -sources.jar nebo -javadoc.jar s jednoduchým README uvnitř, abyste prošli testem.
  • Všechny soubory musí být podepsány pomocí GPG/PGPA .asc soubor obsahující podpis musí být součástí každého souboru.
  • dostupnost pom soubor
  • Správné hodnoty groupId, artifactId и version. Verze může být libovolný řetězec a nemůže končit -SNAPSHOT
  • Nutná přítomnost name, description и url
  • Přítomnost informací o licenci, vývojářích a systému správy verzí

To jsou základní pravidla, která je třeba při publikování dodržovat. Kompletní informace k dispozici zde.

Tyto požadavky implementujeme v build.gradle soubor. Nejprve doplníme všechny potřebné informace o vývojářích, licencích, systému správy verzí a také nastavíme url, název a popis projektu. Napíšeme si na to jednoduchou metodu:

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

Dále to musíte určit během generovaného sestavení -sources.jar и-javadoc.jar soubory. Pro tuto sekci java musíte přidat následující:

java {
    withJavadocJar()
    withSourcesJar()
}

Přejděme k poslednímu požadavku, nastavení podpisu GPG/PGP. Chcete-li to provést, připojte plugin signing:

plugins {
    id 'signing'
}

A přidejte sekci:

signing {
    sign publishing.publications
}

Nakonec přidáme oddíl 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
            }
        }
    }
}

Zde sonatypeUsername и sonatypePassword proměnné obsahující přihlašovací jméno a heslo vytvořené při registraci na sonatype.org.

Tedy finále build.gradle bude vypadat takto:

Úplný kód 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]'
                }
            }
        }
    }
}

Chci poznamenat, že verzi získáme z proměnné prostředí: System.getenv('RELEASE_VERSION'). Při montáži jej vystavíme a převezmeme z názvu tagu.

Generování klíčů PGP

Jedním z požadavků Sonatype je, aby všechny soubory byly podepsány klíčem GPG/PGP. K tomu jdeme zde a stáhněte si nástroj GnuPG pro váš operační systém.

  • Vygenerujeme klíčový pár: gpg --gen-key, zadejte uživatelské jméno, e-mail a také nastavte heslo.
  • Zjistili jsme to id náš klíč s příkazem: gpg --list-secret-keys --keyid-format short. Id bude uvedeno za lomítkem, například: rsa2048/9B695056
  • Publikování veřejného klíče na server https://keys.openpgp.org s příkazem: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • Tajný klíč exportujeme na libovolné místo, budeme ho v budoucnu potřebovat: gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Nastavení akcí Github

Pojďme do závěrečné fáze, nastavme sestavení a automatické publikování pomocí Github Actions.
Github Actions je funkce, která vám umožňuje automatizovat pracovní postup implementací celého cyklu CI / CD. Sestavení, testování a nasazení mohou být spuštěny různými událostmi: odesláním kódu, vytvořením vydání nebo problémy. Tato funkce je pro veřejná úložiště zcela zdarma.

V této části vám ukážu, jak nastavit sestavení a push kód a nasazení do úložiště Sonatype při vydání, stejně jako nastavení tajných klíčů.

Stanovili jsme tajemství

Pro automatické sestavení a nasazení potřebujeme řadu tajných hodnot, jako je ID klíče, heslo, které jsme zadali při generování klíče, samotný klíč PGP a přihlašovací/heslo Sonatype. Můžete je nastavit ve speciální sekci v nastavení úložiště:

Použití akcí Gradle a Github k publikování projektu Java do centrálního úložiště Sonatype Maven

Nastavíme následující proměnné:

  • SONATYPE_USERNAME / SONATYPE_PASSWORD - přihlašovací jméno / heslo, které jsme zadali při registraci u Sonatype
  • SIGNING_KEYID/SIGNING_PASSWORD — ID klíče PGP a heslo nastavené během generování.

Chci se podrobněji zabývat proměnnou GPG_KEY_CONTENTS. Faktem je, že pro zveřejnění potřebujeme soukromý klíč PGP. Abych to zapsal do tajů, použil jsem instrukce a navíc provedl řadu akcí.

  • Pojďme zašifrovat náš klíč pomocí gpg: gpg --symmetric --cipher-algo AES256 9B695056.gpgzadáním hesla. Měl by být umístěn do proměnné: SECRET_PASSPHRASE
  • Přeložme přijatý šifrovaný klíč do textové podoby pomocí base64: base64 9B695056.gpg.gpg > 9B695056.txt. Obsah bude umístěn do proměnné: GPG_KEY_CONTENTS.

Sestavte nastavení při vkládání kódu a vytváření PR

Nejprve musíte vytvořit složku v kořenovém adresáři vašeho projektu: .github/workflows.

V něm označte soubor, např. gradle-ci-build.yml s následujícím obsahem:

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

Tento pracovní postup bude proveden při tlačení do větví master, dev и testing, také při vytváření požadavků na stažení.

Sekce úloh určuje kroky, které mají být provedeny u zadaných událostí. V tomto případě budeme stavět na nejnovější verzi ubuntu, používat Java 8 a také používat plugin pro Gradle eskatos/gradle-command-action@v1který pomocí nejnovější verze builderu spustí příkazy uvedené v arguments. Proměnné secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD to jsou tajemství, na která jsme se ptali dříve.

Výsledky sestavení se projeví na kartě Akce:

Použití akcí Gradle a Github k publikování projektu Java do centrálního úložiště Sonatype Maven

Automaticky nasadit při vydání nového vydání

Vytvořme samostatný soubor pracovního postupu pro automatické nasazení 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}}

Soubor je téměř totožný s předchozím, až na událost, při které bude spuštěn. V tomto případě se jedná o událost vytvoření značky s názvem začínajícím na v.

Před nasazením potřebujeme extrahovat klíč PGP z tajných klíčů a umístit jej do kořenového adresáře projektu a také jej dešifrovat. Dále musíme nastavit speciální proměnnou prostředí RELEASE_VERSION na který odkazujeme gradle.build soubor. To vše se provádí v sekci Prepare to publish. Náš klíč získáme z proměnné GPG_KEY_CONTENTS, přeložíme jej do souboru gpg a poté jej dešifrujeme vložením do souboru secret.gpg.

Dále se zaměříme na speciální proměnnou GITHUB_REF, ze kterého můžeme získat verzi, kterou jsme nastavili při vytváření tagu. Tato proměnná je v tomto případě relevantní. refs/tags/v0.0.2 ze kterého jsme odřízli prvních 11 znaků, abychom získali konkrétní verzi. Dále k publikování použijeme standardní příkazy Gradle: test publish

Kontrola výsledků nasazení v úložišti Sonatype

Po vytvoření vydání by se měl spustit pracovní postup popsaný v předchozí části. Chcete-li to provést, vytvořte vydání:

Použití akcí Gradle a Github k publikování projektu Java do centrálního úložiště Sonatype Maven

název značky musí začínat na v. Pokud se po kliknutí na Publikovat vydání pracovní postup úspěšně dokončí, můžeme přejít na Sonátype Nexus aby se ujistil:

Použití akcí Gradle a Github k publikování projektu Java do centrálního úložiště Sonatype Maven

Artefakt se objevil v úložišti Stagingu. Okamžitě se objeví ve stavu Otevřeno, poté je nutné jej ručně přenést do stavu Zavřít stisknutím příslušného tlačítka. Po kontrole, že jsou splněny všechny požadavky, artefakt přejde do stavu Zavřít a již není k dispozici pro úpravy. V této podobě skončí v MavenCentral. Pokud je vše v pořádku, můžete stisknout tlačítko Uvolněnía artefakt skončí v úložišti Sonatype.

Aby se artefakt dostal do MavenCentral, musíte o něj požádat v úkolu, který jsme vytvořili na úplném začátku. Musíte to udělat pouze jednou, takže publikujeme poprvé. V dalších časech to není nutné, vše se synchronizuje automaticky. Rychle mi zapnuli synchronizaci, ale trvalo asi 5 dní, než byl artefakt dostupný v MavenCentral.

To je vše, zveřejnili jsme náš artefakt v MavenCentral.

Užitečné odkazy

  • Podobný článek, publikovat pouze přes maven
  • Staging úložiště Sonatyp
  • Jira Sonatyp, ve kterém se vytvoří úkol
  • příklad úložiště, kde je vše nastaveno

Zdroj: www.habr.com