การใช้ Gradle และ Github Actions เพื่อเผยแพร่โครงการ Java ไปยัง Sonatype Maven Central Repository

ในบทความนี้ ฉันต้องการดูรายละเอียดเกี่ยวกับกระบวนการเผยแพร่สิ่งประดิษฐ์ Java ตั้งแต่เริ่มต้นผ่าน Github Actions ไปยัง Sonatype Maven Central Repository โดยใช้ Gradle builder

ฉันตัดสินใจเขียนบทความนี้เนื่องจากไม่มีการสอนแบบปกติในที่เดียว ข้อมูลทั้งหมดต้องรวบรวมทีละส่วนจากแหล่งต่าง ๆ ยิ่งกว่านั้นไม่สดทั้งหมด ใครสนใจยินดีต้อนรับภายใต้แมว

การสร้างที่เก็บใน Sonatype

ขั้นตอนแรกคือการสร้างที่เก็บใน Sonatype Maven Central สำหรับสิ่งนี้เราไป ที่นี่ลงทะเบียนและสร้างงานใหม่โดยขอให้เราสร้างพื้นที่เก็บข้อมูล เราขับรถในของเรา รหัสกลุ่ม โครงการ, URL โครงการ ลิงค์โครงการและ URL SCM ลิงก์ไปยังระบบควบคุมเวอร์ชันที่โครงการตั้งอยู่ รหัสกลุ่ม ที่นี่ควรอยู่ในรูปแบบ com.example, com.example.domain, com.example.testsupport และยังสามารถอยู่ในรูปแบบของลิงก์ไปยัง GitHub ของคุณ: github.com/yourusername -> 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ชื่อผู้ใช้ и sonatypeรหัสผ่าน ตัวแปรที่มีการเข้าสู่ระบบและรหัสผ่านที่สร้างขึ้นระหว่างการลงทะเบียนบน 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

ไปที่ขั้นตอนสุดท้าย ตั้งค่าการสร้างและเผยแพร่อัตโนมัติโดยใช้ Github Actions
Github Actions เป็นคุณสมบัติที่ช่วยให้คุณทำให้เวิร์กโฟลว์เป็นไปโดยอัตโนมัติโดยใช้วงจร CI / CD เต็มรูปแบบ สร้าง ทดสอบ และปรับใช้สามารถทริกเกอร์ได้จากเหตุการณ์ต่างๆ เช่น การพุชรหัส การสร้างรุ่น หรือปัญหา ฟังก์ชันนี้ใช้งานได้ฟรีสำหรับที่เก็บข้อมูลสาธารณะ

ในส่วนนี้ ฉันจะแสดงวิธีตั้งค่าบิลด์และพุชโค้ด และปรับใช้กับที่เก็บ Sonatype เมื่อเผยแพร่ ตลอดจนตั้งค่าความลับ

เราตั้งความลับ

สำหรับการประกอบและการปรับใช้โดยอัตโนมัติ เราต้องการค่าลับจำนวนหนึ่ง เช่น ID ของคีย์ รหัสผ่านที่เราป้อนเมื่อสร้างคีย์ คีย์ PGP เอง และการเข้าสู่ระบบ/รหัสผ่าน Sonatype คุณสามารถตั้งค่าได้ในส่วนพิเศษในการตั้งค่าที่เก็บ:

การใช้ Gradle และ Github Actions เพื่อเผยแพร่โครงการ Java ไปยัง Sonatype Maven Central Repository

เราตั้งค่าตัวแปรต่อไปนี้:

  • SONATYPE_USERNAME / SONATYPE_PASSWORD - ล็อกอิน / รหัสผ่านที่เราป้อนเมื่อลงทะเบียนกับ Sonatype
  • SIGNING_KEYID/SIGNING_PASSWORD — รหัสคีย์ 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เช่นเดียวกับเมื่อสร้างคำขอดึง

ส่วนงานระบุขั้นตอนที่จะดำเนินการในเหตุการณ์ที่ระบุ ในกรณีนี้ เราจะสร้างบน Ubuntu เวอร์ชันล่าสุด ใช้ Java 8 และใช้ปลั๊กอินสำหรับ Gradle eskatos/gradle-command-action@v1ซึ่งใช้ตัวสร้างเวอร์ชันล่าสุดเพื่อเรียกใช้คำสั่งที่ระบุใน arguments. ตัวแปร secrets.SONATYPE_USERNAME и secrets.SONATYPE_PASSWORD นี่คือความลับที่เราถามก่อนหน้านี้

ผลลัพธ์ของบิลด์จะแสดงในแท็บการดำเนินการ:

การใช้ Gradle และ Github Actions เพื่อเผยแพร่โครงการ Java ไปยัง 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 ไปยัง Sonatype Maven Central Repository

ชื่อแท็กต้องขึ้นต้นด้วย v หากหลังจากคลิกเผยแพร่เผยแพร่เวิร์กโฟลว์เสร็จสมบูรณ์แล้ว เราสามารถไปที่ Sonatype Nexus เพื่อให้แน่ใจว่า:

การใช้ Gradle และ Github Actions เพื่อเผยแพร่โครงการ Java ไปยัง Sonatype Maven Central Repository

สิ่งประดิษฐ์ปรากฏในที่เก็บ Staging ทันทีปรากฏในสถานะเปิด จากนั้นจะต้องโอนไปยังสถานะปิดด้วยตนเองโดยกดปุ่มที่เหมาะสม หลังจากตรวจสอบว่าตรงตามข้อกำหนดทั้งหมดแล้ว อาร์ติแฟกต์จะเข้าสู่สถานะปิดและไม่สามารถแก้ไขได้อีกต่อไป ในรูปแบบนี้จะจบลงใน MavenCentral หากทุกอย่างเรียบร้อยดี คุณสามารถกดปุ่ม ปล่อยและสิ่งประดิษฐ์จะจบลงในที่เก็บ Sonatype

เพื่อให้สิ่งประดิษฐ์เข้าสู่ MavenCentral คุณต้องขอในงานที่เราสร้างขึ้นในตอนเริ่มต้น คุณต้องทำเช่นนี้เพียงครั้งเดียว เราจึงเผยแพร่เป็นครั้งแรก ในครั้งต่อไป ไม่จำเป็น ทุกอย่างจะถูกซิงโครไนซ์โดยอัตโนมัติ พวกเขาเปิดการซิงโครไนซ์ให้ฉันอย่างรวดเร็ว แต่ใช้เวลาประมาณ 5 วันกว่าที่สิ่งประดิษฐ์จะพร้อมใช้งานใน MavenCentral

นั่นคือทั้งหมด เราได้เผยแพร่สิ่งประดิษฐ์ของเราใน MavenCentral

ลิงค์ที่มีประโยชน์

  • คล้ายกัน บทความเผยแพร่ผ่าน maven เท่านั้น
  • การแสดงละคร ที่เก็บ โซนาไทป์
  • จิระ Sonatype ในการสร้างงาน
  • ตัวอย่าง พื้นที่เก็บข้อมูลที่มีการตั้งค่าทั้งหมด

ที่มา: will.com