Користење Gradle и Github Actions за објавување Java Project во Sonatype Maven Central Repository

Во оваа статија, сакам детално да го разгледам процесот на објавување артефакт Јава од нула преку Github Actions до централното складиште на Sonatype Maven со помош на градителот Gradle.

Решив да ја напишам оваа статија поради недостаток на нормален туторијал на едно место. Сите информации требаше да се собираат дел по дел од различни извори, згора на тоа, не сосема свежи. Кој се грижи, добредојде под мачка.

Создавање складиште во Sonatype

Првиот чекор е да се создаде складиште во Sonatype Maven Central. За ова одиме тука, регистрирајте се и креирајте нова задача, барајќи од нас да создадеме складиште. Возиме во нашите Групна ИД проект, URL на проект врска со проектот и УРЛ на SCM врска до системот за контрола на верзијата во кој се наоѓа проектот. Групна ИД тука треба да биде од формата com.example, com.example.domain, com.example.testsupport, а може да биде и во форма на врска до вашиот github: github.com/вашето корисничко име -> io.github.вашето корисничко име. Во секој случај, ќе треба да ја потврдите сопственоста на овој домен или профил. Ако наведовте профил на github, ќе биде побарано да креирате јавно складиште со саканото име.

Некое време по потврдата, вашиот GroupId ќе биде креиран и можеме да преминеме на следниот чекор, конфигурација на Gradle.

Конфигурирање Gradle

Во моментот на пишување, не најдов Gradle приклучоци кои би можеле да помогнат при објавувањето на артефактот. Тоа единствениот приклучок што го најдов, сепак, авторот одби дополнително да го поддржи. Затоа, решив да направам сè сам, бидејќи не е премногу тешко да се направи ова.

Првото нешто што треба да се сфати е барањата на Sonatype за објавување. Тие се следните:

  • Достапност на изворни кодови и JavaDoc, т.е. мора да присуствуваат -sources.jar и-javadoc.jar датотеки. Како што е наведено во документацијата, ако не е можно да се обезбедат изворни кодови или документација, можете да направите кукла -sources.jar или -javadoc.jar со едноставно README внатре за да го положите тестот.
  • Сите датотеки мора да бидат потпишани со 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 protected]'
                }
            }
        }
    }
}

Следно, треба да го наведете тоа за време на собранието генерирано -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
            }
        }
    }
}

Тука sonatypeКорисничко име и 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 protected]'
                }
            }
        }
    }
}

Сакам да забележам дека ја добиваме верзијата од променливата на околината: System.getenv('RELEASE_VERSION'). Ќе го изложиме за време на склопувањето и ќе го земеме од името на ознаката.

Генерирање на PGP клучеви

Едно од барањата на Sonatype е сите датотеки да бидат потпишани со клуч GPG/PGP. За ова одиме тука и преземете ја алатката GnuPG за вашиот оперативен систем.

  • Ние генерираме пар клучеви: gpg --gen-key, внесете корисничко име, е-пошта, а исто така поставете лозинка.
  • Дознаваме id нашиот клуч со командата: gpg --list-secret-keys --keyid-format short. ИД ќе биде наведен по коса црта, на пример: rsa2048/9B695056
  • Објавување на јавниот клуч на серверот https://keys.openpgp.org команда: gpg --keyserver [https://keys.openpgp.org](https://keys.openpgp.org/) --send-keys 9B695056
  • Го извезуваме тајниот клуч на произволно место, ќе ни треба во иднина: gpg --export-secret-key 9B695056 > D:\gpg\9B695056.gpg

Поставување Github Actions

Ајде да продолжиме до последната фаза, да го поставиме изградбата и автоматски да објавуваме со помош на Github Actions.
Github Actions е карактеристика што ви овозможува да го автоматизирате работниот тек со имплементирање на целосен циклус CI / CD. Изградбата, тестирањето и распоредувањето може да бидат активирани од различни настани: притискање на кодот, создавање ослободување или проблеми. Оваа функционалност е апсолутно бесплатна за јавните складишта.

Во овој дел, ќе ви покажам како да поставите build и туркање код и да го распоредите во складиштето Sonatype по објавувањето, како и да поставите тајни.

Ние поставуваме тајни

За автоматско склопување и распоредување, потребни ни се голем број тајни вредности, како што се идентификацијата на клучот, лозинката што ја внесовме при генерирањето на клучот, самиот клуч PGP и најава/лозинка на Sonatype. Можете да ги поставите во посебен дел во поставките на складиштето:

Користење Gradle и Github Actions за објавување Java Project во Sonatype Maven Central Repository

Ги поставивме следните променливи:

  • 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.

Изградете поставки кога туркате код и креирате ПР

Прво треба да креирате папка во коренот на вашиот проект: .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, исто така кога креирате барања за повлекување.

Делот за работни места ги одредува чекорите што треба да се извршат на наведените настани. Во овој случај, ќе ја изградиме најновата верзија на ubuntu, ќе користиме Java 8, а исто така ќе го користиме приклучокот за Gradle eskatos/gradle-command-action@v1кој, користејќи ја најновата верзија на градителот, ќе ги извршува командите наведени во arguments. Променливи secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD ова се тајните што ги прашавме претходно.

Резултатите од изградбата ќе се рефлектираат во табулаторот Дејства:

Користење Gradle и Github Actions за објавување Java Project во Sonatype Maven Central Repository

Автоматско распоредување кога ќе се објави ново издание

Ајде да создадеме посебна датотека со работниот тек за автоматско распоредување 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

Откако ќе се креира изданието, треба да започне работниот тек опишан во претходниот дел. За да го направите ова, креирајте порака:

Користење Gradle и Github Actions за објавување Java Project во Sonatype Maven Central Repository

името на ознаката мора да започнува со v. Ако, откако ќе кликнете на Објави издание, работниот тек успешно заврши, можеме да одиме до Nexus сонатип за да се уверите:

Користење Gradle и Github Actions за објавување Java Project во Sonatype Maven Central Repository

Артефактот се појави во складиштето Staging. Веднаш се појавува во статусот Отворено, потоа мора рачно да се префрли во статусот Затвори со притискање на соодветното копче. Откако ќе се провери дали се исполнети сите барања, артефактот оди во статусот Затвори и повеќе не е достапен за модификација. Во оваа форма, ќе заврши во MavenCentral. Ако сè е во ред, можете да го притиснете копчето Ослободување, а артефактот ќе заврши во складиштето на Sonatype.

За да може артефактот да влезе во MavenCentral, треба да го побарате во задачата што ја создадовме на самиот почеток. Треба да го направите ова само еднаш, затоа објавуваме за прв пат. Во следните времиња, ова не е потребно, сè ќе се синхронизира автоматски. Брзо ми вклучија синхронизација, но беа потребни околу 5 дена за артефактот да стане достапен во MavenCentral.

Тоа е сè, го објавивме нашиот артефакт во MavenCentral.

Корисни линкови

  • Слично Член, објавува само преку maven
  • Стадинг складиште Сонатип
  • Jira Сонатип во кој ќе се креира задачата
  • Пример складиште каде што се е поставено

Извор: www.habr.com