olrlobt

[INFRA] Maven Central Repository에 Gradle Artifacts 배포하기 본문

Infra

[INFRA] Maven Central Repository에 Gradle Artifacts 배포하기

olrlobt 2024. 5. 7. 01:38

Maven Central Repository

Maven Central Repository는 Maven 프로젝트를 위한 공개 아티팩트(Artifact) 저장소이다. 전 세계 개발자들이 개발에 활용할 수 있도록 수많은 라이브러리와 프레임워크, 플러그인을 중앙에 모아두고 공유하는 공간이다. 우리 같은 개발자들은 흔히 mvnrepository를 통하여 쉽게 라이브러리들을 찾고는 하는데, 이 mvnrepository는 Maven Central Repository나 다른 저장소들의 아티팩트를 인덱싱하는 웹사이트이다.

 

쉽게 말해  mvnrepository은 검색 엔진이고, Maven Central Repository은 실제 파일이 올라가는 저장소로 생각하면 된다.

 

 

 

Maven Central 공식 홈페이지

 

Maven Central

Official search by the maintainers of Maven Central Repository.

central.sonatype.com

 

 

 

Maven Central Repository를 검색하면 배포를 돕는 여러 가지 포스팅들이 나오지만, 2024년 3월 12일 기준으로 기존 OSSRH를 사용하던 방식에서 중앙 포털을 통해 배포하는 방식으로 변경되었다.

 

기존에 OSSRH를 통하여 Nexus repository로 업로드하는 방식을 사용하던 유저는 기존 방식을 그대로 사용할 수 있지만, 신규 유저라면 Jira 티켓을 발급할 수 없고, Nexus repository에 로그인할 수도 없다.

 

따라서, Maven Central 공식 문서에서 제공해 주는 중앙 포털 배포 방식을 따라야 한다.

 

2024년 3월 12일 이후로는 Maven Central 중앙 포털을 통해 배퐇해야 한다.

 

 

Maven Central 공식 문서

 

Register to Publish Via the Central Portal

Register to Publish Via the Central Portal Registration Note From March 12th, 2024, all registration will be via the Central Portal. For information about legacy registration, please see the relevant documentation. For support with switching to publish via

central.sonatype.org

 

 

 


Namespace 만들기

 

먼저 Maven Central Repository에 접속하여, 우측 상단의 Sign In으로 가입을 진행한다.

Maven Central Repository 메인화면

 

 

가입 후에는 상단 바의 Publish를 통하여 네임스페이스 생성 페이지로 이동한다.

Maven Central Repository Namespace

 

네임스페이스는 Maven 생태계에서 컴포넌트를 유일하게 식별하기 위한 Group Id이다. Maven Central에 호스팅 되는 모든 다른 게시자의 컴포넌트들과 이 네임스페이스를 통해 식별하게 된다.

 

우측 상단의 Add Namespace로 만들 수 있는데, DNS의 경우 Java 패키지 명명 규칙과 유사하게 도메인 이름을 역방향으로 작성하면 된다. 예를 들어 olrlobt.tistory.com이라면 com.tistory.olrlobt로 작성하면 된다.

 

만약 도메인이 없다면 Github를 사용해도 되는데, 이 경우 io.github. {사용자 이름} 형식을 사용한다.

 

 

Maven Central Repository Add Namespace modal

 

나는 이번에 배포할 도메인의 주소가 lightswitch.kr이었기 때문에 kr.lightswitch로 생성하려 했었다. 하지만, kr이 지역 도메인이어서 그런가 인식이 되지 않았고, 어쩔 수 없이 kr.lightswitch.www로 진행하였다.

 

 

Maven Central Repository create namespace

 

생성을 완료하면 네임스페이스의 소유권을 확인하라는 메시지가 나오게 된다. 이때, 확인이 완료되지 않고 무턱대고 Verify Namespace를 눌렀다가는 응답이 캐싱되어 버려 확인이 완료되어도 캐시가 만료될 때까지 진행을 하지 못하니 주의하자.

 

Namespace가 Github 저장소로 설정이 되어있다면, Verification Key의 이름으로 레퍼지토리를 하나 생성하는 방식으로 인증을 진행한다. 인증이 완료된 후에는 저장소를 삭제하여도 된다.

 

DNS를 사용하는 경우에는 TXT 레코드를 설정하여 인증을 진행해야 한다.

나는 가비아를 이용하여 도메인을 설정해 놓았기 때문에, 가비아에 접속하여 DNS 레코드를 추가해 주었다.

 

Gabia TXT레코드 추가

 

위와 같이 TXT레코드에 인증코드를 설정해 주자.

그런 다음 Ubuntu에서 인증코드가 잘 나오는지 아래의 명령어로 확인할 수 있다.

$ host -t txt www.lightswitch.kr

Ubuntu DNS 검증 명령어

 

 

코드가 정상적으로 나온다면, 홈페이지에서 인증을 진행하자.

 

바로 새로고침을 진행하면 아래와 같이 인증이 완료되었다고 표시된다.

 

Maven Central Repository Namespace verified

 

 

 

Deployments

 

이제 Namespace 인증을 완료했으니, Maven Central Repository에 배포해 보자.

Maven Central Repository 중앙 저장소에 배포하기 위해서는 배포된 아티팩트의 품질을 최소 수준으로 보장하기 위해 여러 가지 요구사항을 맞춰 주어야 한다.

 

그리고 이 요구사항에는 아래와 같은 것들이 있다.

  • Javadoc 및 소스 제공
  • 파일 체크섬 제공
  • GPG/PGP로 파일 서명
  • 충분한 메타데이터
  • 올바른 좌표
  • 프로젝트 이름, 설명 및 URL
  • 라이선스 정보
  • 개발자 정보
  • SCM정보

이러한 정보들은 간단하게 공식 문서에서 제공하는 플러그인으로 요구사항만 맞추어 작성해 주면 된다.

 

하지만,, 

 

there is no official Gradle plugin for publishing to Maven Central via the Central Publishing Portal

 

현재 공식적으로 Gradle 플러그인은 지원하지 않고 있고, 이에 따라 여러 비공식 플러그인들을 연결해 주고 있었다. 대부분은 kotlin으로 작성된 플러그인이라 내 java 프로젝트에서 사용하기에는 무리가 있었다.

 

따라서, 나는 여러 가지를 시도해 본 결과, 아래의 플러그인을 이용하기로 하였다.

 

 

GitHub - vanniktech/gradle-maven-publish-plugin: A Gradle plugin that publishes your Android and Kotlin libraries, including sou

A Gradle plugin that publishes your Android and Kotlin libraries, including sources and javadoc, to Maven Central or any other Nexus instance. - vanniktech/gradle-maven-publish-plugin

github.com

 

 

 

 

GPG 서명

중앙 저장소에 아티팩트를 게시하기 위한 요구 사항 중 하나는 PGP로 서명되어야 한다는 것이다. GPG(GNU Privacy Guard)는 데이터 암호화와 전자 서명에 사용되는 공개 키 암호화 소프트웨어로, 개인정보 보호 및 인증을 위해 사용된다. GPG로 프로젝트를 서명함으로써, 프로젝트를 사용하는 사용자가 신뢰된 프로젝트를 사용할 수 있게 한다.

 

 

GnuPG바이너리를 다운로드

https://gnupg.org/download/index.html#sec-1-2

먼저 위 사이트에서 GnuPG바이너리를 다운로드한다.

 

그리고 gpg --version 명령어를 통해 설치가 잘 진행되었는지 확인하자.

$ gpg --version
gpg (GnuPG) 2.2.19
libgcrypt 1.8.5
Copyright (C) 2019 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Home: /home/mylocaluser/.gnupg
Supported algorithms:
Pubkey: RSA, ELG, DSA, ECDH, ECDSA, EDDSA
Cipher: IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, AES256, TWOFISH,
        CAMELLIA128, CAMELLIA192, CAMELLIA256
Hash: SHA1, RIPEMD160, SHA256, SHA384, SHA512, SHA224
Compression: Uncompressed, ZIP, ZLIB, BZIP2

 

 

키 생성

이미 발급된 개인키/공개키가 없다면 아래 명령어로 키를 발급받을 수 있다.

$ gpg --gen-key

 

이 과정에서 이름, 이메일, 비밀번호를 요구하는데, 필요에 맞게 작성해 주자.

GPG 키 생성 명령어

 

생성된 키 확인

$ gpg --list-keys
/home/mylocaluser/.gnupg/pubring.kbx
---------------------------------
pub   rsa3072 2021-06-23 [SC] [expires: 2023-06-23]
      CA925CD6C9E8D064FF05B4728190C4130ABA0F98    // 끝 8자리 = key Id
uid           [ultimate] Central Repo Test <central@example.com>
sub   rsa3072 2021-06-23 [E] [expires: 2023-06-23]

 

위 명령어를 통해 키가 생성된 것을 확인할 수 있다. 여기서 키의 맨 뒤 8자리를 통해 키를 식별할 수 있는데, 이를 통해 전체키를 다 작성하지 않고도 다양한 작업을 수행할 수 있다.

 

 

공개 키 전송

GPG로 서명한 프로젝트를 다른 사람이 확인하기 위해서는 공개 키가 필요하다. 따라서 공개 키를 공캐 키 서버에 배포해야 하는데, Maven Central Repository에서 사용하는 공개 키 서버는 다음과 같다.

  • keyserver.ubuntu.com
  • keys.openpgp.org
  • pgp.mit.edu

 

// 공개 키 전송
gpg --keyserver keyserver.ubuntu.com --send-keys 0ABA0F98

 

 

 

Gradle 플러그인 스크립트 추가

Maven Central repository 패키지를 배포할 때, 프로세스를 자동화하기 위해 build.gradle에 앞전에 언급했던 플러그인을 사용할 것이다. 이를 위해, build.gradle에 mavenPublishing 블록을 추가해 주자.

 

플러그인 추가

먼저, build.gradle 설정에 플러그인을 추가해 주자.

앞서 언급한 비공식 배포 플러그인이며, 이를 통해 간단하게 배포할 수  있는 과정을 지원한다.

plugins {
  id "com.vanniktech.maven.publish" version "0.28.0"
  id 'signing' // GPG 서명을 위한 플러그인
}

 

 

Config 추가 (선택)

config를 통해 게시할 항목을 구성한다. 기본 구성으로는 소스 및 javadoc jar가 포함되어 있으며, 이를 수정하고 싶으면 아래와 같은 명령어를 통해 수정할 수 있다.

import com.vanniktech.maven.publish.JavaLibrary
import com.vanniktech.maven.publish.JavadocJar

mavenPublishing {
  // the first parameter configures the -javadoc artifact, possible values:
  // - `JavadocJar.None()` don't publish this artifact
  // - `JavadocJar.Empty()` publish an emprt jar
  // - `JavadocJar.Javadoc()` to publish standard javadocs
  // the second whether to publish a sources jar
  configure(new JavaLibrary(new JavadocJar.Javadoc(), true))
}

 

 

 

Maven Central repository 게시 방법

아래 명령어 중 하나를 선택한다. 기존 유저라면 DEFAUTL, S01 방법으로 사용하면 되고, 우리는 신규 유저기 때문에 중앙 배포 방식을 사용하므로 CENTRAL_PORTAL을 사용한다.

 

또한, 해당 플러그인에서는 환경변수를 이용하여 signAllPublications() 메서드로 GPG 서명을 진행한다.

만약 자바 9 버전 이상이라면 useGpgCmd()를 이용하여 서명을 진행하면 되기 때문에, 생략하여도 된다.

import com.vanniktech.maven.publish.SonatypeHost

mavenPublishing {
  // publishToMavenCentral(SonatypeHost.DEFAULT)
  // or when publishing to https://s01.oss.sonatype.org
  // publishToMavenCentral(SonatypeHost.S01)
  // or when publishing to https://central.sonatype.com/
  publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)

  //signAllPublications()
}

 

 

POM 구성

POM 구성에는 Maven Cetral Repository 배포를 위한 구성요소들이 들어간다. URL 및 사용된 라이선스와 같은 프로젝트에 대한 일부 일반 정보들이 포함되며, 여기서 작성되는 것들은 문서를 위해 작성되는 것들이다. 알맞게 작성해야 배포된 라이브러리를 사용하는 사용자들이 라이선스를 확인하고 사용하거나, 버그가 있을 때 문서를 찾아보고 제보하기 수월할 것이다. 

 

또한 coordinates에 들어가는 것들이 Maven Central Repository의 폴더 구성을 하므로, 네임스페이스, 라이브러리 이름, 버전 순으로 작성해 주어야 한다. 이때, 중앙 배포 방식은 SNAPSHOT 배포를 지원하지 않는 것에 유의하자.

 

mavenPublishing {
   coordinates("kr.lightswitch.www", "lightswitch", "1.0.0")

    pom {
        name = "lightswitch"
        description = "lightswitch for lunit"
        inceptionYear = "2024"
        url = "www.lightswitch.kr"
        licenses {
            license {
                name = "The Apache License, Version 2.0"
                url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
                distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt"
            }
        }
        developers {
            developer {
                id = "olrlobt"
                name = "Seungheon Lee"
                url = "https://github.com/olrlobt"
            }
        }
        scm {
            url = "https://github.com/olrlobt"
            connection = "scm:git:git://github.com/olrlobt"
            developerConnection = "scm:git:ssh://git@github.com/olrlobt"
        }
    }
}

 

 

 

Secret gradle.properties

/. gradle/gradle.properties에 아래와 같은 변수들을 작성해 준다.

만약 signAllPublications()를 사용한다면, gpg에 대한 정보를 작성해 주어야 하며, 그렇지 않다면 생략한다.

 

mavenCentralUsername=username
mavenCentralPassword=the_password

//signing.keyId=12345678
//signing.password=some_password
//signing.secretKeyRingFile=/Users/yourusername/.gnupg/secring.gpg

 

 

 

build.gradle 전체

import com.vanniktech.maven.publish.SonatypeHost
import com.vanniktech.maven.publish.JavaLibrary
import com.vanniktech.maven.publish.JavadocJar

plugins {
    id 'java'
    id "com.vanniktech.maven.publish" version "0.28.0"
    id 'signing'
}

group = 'com.lightswitch'
version = '1.0.0'

repositories {
    mavenCentral()
}

signing {
    useGpgCmd()
    sign publishing.publications
}


mavenPublishing {
//    configure(new JavaLibrary(new JavadocJar.Empty(), true))
    publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)

//    signAllPublications()

    coordinates("kr.lightswitch.www", "lightswitch", "1.0.0")

    pom {
        name = "lightswitch"
        description = "lightswitch for lunit"
        inceptionYear = "2024"
        url = "www.lightswitch.kr"
        licenses {
            license {
                name = "The Apache License, Version 2.0"
                url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
                distribution = "https://www.apache.org/licenses/LICENSE-2.0.txt"
            }
        }
        developers {
            developer {
                id = "olrlobt"
                name = "Seungheon Lee"
                url = "https://github.com/olrlobt"
            }
        }
        scm {
            url = "https://github.com/olrlobt"
            connection = "scm:git:git://github.com/olrlobt"
            developerConnection = "scm:git:ssh://git@github.com/olrlobt"
        }
    }
}

dependencies {
    compileOnly 'javax.servlet:javax.servlet-api:4.0.1'
    implementation group: 'com.google.code.gson', name: 'gson', version: '2.10.1'
    testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.25.3'
    ... 생략
}

... 생략

 

 


빌드

이제 Maven Central Repository 배포를 위한 설정은 모두 마쳤다.

터미널에서 아래 명령어를 이용하여 배포를 진행하자.

./gradlew publishAllPublicationsToMavenCentralRepository

Maven Central Repository 배포 명령어

 

 

 

빌드가 성공적으로 진행되었다면, Maven Central Repository의 Deployments에서 확인이 가능하다.

 

Maven Central Repository의 Deployments

 

 

 

배포 중이라는 화면에서 잠시만 기다리게 되면, 아래와 같이 배포가 완료된다.

Maven Central Repository의 Deployments 배포 완료

 

repo1.maven 확인

정상적으로 올라간 것을 확인할 수 있다.

repo1.maven.org 배포 완료

 

 

build.gradle 사용

이제 build.gradle에서 아래처럼 간편하게 내 라이브러리를 사용할 수 있다.

dependencies {
    implementation 'kr.lightswitch.www:lightswitch:1.0.0'
}
Comments