Maven Central Repository 에 플러그인 배포하기

이 문서에서는 Maven 중앙 저장소에 플러그인을 출판하는 과정에 대해서 설명합니다. 2024년 3월 12일부터 기존의 legacy OSSRH 로의 신규 배포는 중단됩니다. 기존에 배포된 플러그인들과 달리 새롭게 배포하는 플러그인은 문서에 설명한 Maven Central Repository로 배포를 해야 합니다.

Maven Centeral Repository

Maven 저장소에 라이브러리를 등록하려면 아래와 같은 단계를 거쳐야 한다.

  1. Maven Central Repository 계정 및 namespace 인증
  2. pom.xml 안에 최소한의 요구사항 입력
  3. signing 설정
  4. 배포 및 확인

0. Maven 로컬 저장소 위치 확인

다음과 같이 명령어를 입력해보자.

로컬 저장소 위치
$ mvn help:evaluate -Dexpression=settings.localRepository ... /Users/____/.m2/repository
  • ____ - 로그인 이름

0.1. 명렁어 실패하면

mvn 명령어가 실패하면 maven 설치가 필요하다.

Maven은 자바 애플리케이션이라서 따로 설치 과정이 없고 압축 파일을 내려받아서 경로만 설정해준다.

윈도우, 리눅스, 맥 환경 모두 비슷하니 구글링하면 수많은 문서가 나온다. 아무거나 참고해서 설치를 끝내자.

그리고 다음 명령어로 설치가 제대로 됐는지 확인함.

버전 확인
$ mvn --version

이 단계를 실패하면 더 이상 아래 단계를 진행할 수 없다.

0.2. settings.xml 위치 확인

저장소 경로가 나오면 settings.xml 파일은 다음의 경로에 있다.

[확인] settings.xml
/Users/____/.m2/settings.xml
  • settings.xml이 없을 수도 있다.

또는 Section#0.1 에서 내려받은 maven의 conf/settings.xml을 확인한다. 둘 중 하나만 존재하면 된다.

나중에 파일을 수정해야 하니 참고하자.

1. Sonartype 계정 및 namespace 인증

흔히 알고 있는 maven repository는 등록된 플러그인을 조회하는 곳이고 자신의 플러그인을 등록해서 사용하려면 maven central repository에 회원 가입 후 이용해야 한다.

Maven Central Repository는 브랜드 이름이라고 생각하면 좋다.

계정 생성 및 라이브러리 배포 확인은 https://central.sonatype.com 인데 이게 처음에 헷갈릴 수 있다.

  • 이 문서에서는 라이브러리 출판과 관련해서는 통칭해서 sonartype 계정으로 언급하겠다.

github 계정으로 가입하면 자동으로 namespace가 활성화된다.

구글 계정으로 가입하면 namespace를 따로 설정해야 한다. 이 과정에서 본인 소유의 도메인을 인증하는 과정이 필요할 수도 있다.

물론 구글 계정으로 가입 후에 별도의 과정을 거치면 github 계정을 namespace로 사용할 수 있다고 한다.(시도하진 않았다.)

가입 후 publish > namespace 를 누르면 io.github.____ 로 namespace가 인증된다.

2. pom.xml 안에 최소한의 요구사항 입력

자세한 내용은 다음 링크에 잘 나와있다.

간단히 요약하면 <project> 요소 밑에 다음 요소들을 규칙에 맞게 정의해야 한다.

XML
<groupId></groupId> <artifactId></artifactId> <version></version> <packaging>jar</packaging> <name>${project.groupId}:${project.artifactId}</name> <description></description> <url></url> <licenses> ... </licenses> <developers> ... </developers> <scm> ... </scm>

사용자가 임의로 설정할 수 있는 속성들은 아래와 같다.

  • artifactId - 라이브러리를 나타내는 이름으로 지어줌
  • groupId - namespace로 시작하는 이름이면 된다. io.github.____ 로 지어도 되고, 좀 더 상세하게 io.github.____.awesome과 같이 이어질 수도 있다.
  • version - X.Y.X 형식으로 지어준다.
  • name - 자유롭게 값을 넣을 수 있지만 보통은 groupId:artifactId 로 지어준다.
  • description - 라이브러리에 대한 설명

나머지 속성들은 저장소 위치나 라이선스에 따라서 설정값이 정해진다. 아래의 공식 문서를 참고하면 된다.

그리고 반드시 소스코드와 문서를 함께 출판해야 한다. 이 과정 역시 위 링크에 나와있다.

3. signing 설정

빌드가 끝나면 다음과 같은 파일이 target 디렉토리 아래에 생성된다.

TXT
awesomelib-javadoc.jar awesomelib-sources.jar awesomelib.jar pom.xml

중앙 저장소에 출판하려면 각각의 파일마다 다음과 같이 지문 역할을 하는 파일들을 생성해야 한다.

파일을 식별하는 지문값들
awesomelib.jar awesomelib.jar.asc awesomelib.jar.md5 awesomelib.jar.sh1 awesomelib.jar.sh256 awesomelib.jar.sh512
  • 모든 산출물마다 위와 같이 지문값을 생성해야 함.
  • 디렉토리 target/central-staging/.. 아래에 모여 있음.
  • 중앙 저장소에서는 이 값들을 이용해서 산출물들을(awesomelib.jar) 검증한다.

이걸 일일이 뽑아내는 것도 보통 일이 아니다.

그래서 지문값을 뽑아주는 플러그인을 제공하고 있다.

central-publishing-maven-plugin

[설정] pom.xml
<build> <plugins> <plugin> <groupId>org.sonatype.central</groupId> <artifactId>central-publishing-maven-plugin</artifactId> <version>0.6.0</version> <extensions>true</extensions> <configuration> <publishingServerId>central</publishingServerId> </configuration> </plugin> </plugins> </build>
  • central - username/password와 연결된 값으로 보통 central 이라고 이름을 붙인다. 여기서의 username/password는 Section #1에서 가입한 sonartype 계정과 상관없으니 혼동하지 말자. Section #3.2 에서 username과 password를 생성한다.

위 라이브러리가 모든 작업을 혼자서 처리하는 것은 아니다.

사용자는 플러그인을 위해서 다음 도구들을 준비해줘야 한다.

  • gpg 설치 - 지문값 생성 및 검증에 필요한 개인키/공개키를 생성하고 관리하는 도구. 로컬에 설치해야 한다.
  • token 설치 - username/password 정보를 제공해 줘야 중앙 저장소에 산출물들을 업로드할 수 있다. sonartype 계정에서 생성해야 한다.

3.1. gpg 설치 및 공개키 등록

central-publishing-maven-plugin 가 직접 gpg 명령어를 실행해서 지문값들을 뽑아낸다.

pgp, gpg 비슷하게 생긴 용어들이 헷갈리는데 간략히 요약하면 아래와 같다.

  1. PGP : Pretty Good Privacy의 약어로, 이메일 암호화를 위해 개발된 상용 소프트웨어. 개인에 한해서 무료, 상업용으로는 유료 버전을 구매해야 한다.
  2. OpenPGP : 벤더들이 만든 암호화 프로그램이 서로 호환 가능하도록 정한 표준(RFC 9580). 즉, 특정 소프트웨어가 아니라 인터페이스와 같은 역할을 한다.
  3. GPG: Gnu Privacy Guard의 약어로 OpenPGP 스펙에 맞춰서 별도로 구현된 공개 소프트웨어. 여기서는 이 구현체를 사용한다.

3.1.1. GPG 설치

운영체제마다 설치 방법이 조금씩 다르다.

자세한 내용은 공식 문서에도 나와있다.

설치가 잘 끝나면 아래와 같이 gpg --version을 실행해서 확인하자.

[확인] gpg --version
$ gpg --version gpg (GnuPG/MacGPG2) 2.2.41 libgcrypt 1.8.10 Copyright (C) 2022 g10 Code GmbH License GNU GPL-3.0-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: /Users/____/.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

3.1.2. 개인키 공개키 생성

다음의 두 가지 명령어를 주로 사용한다.

키 생성 명령어
gpg --full-generate-key gpg --gen-key

얼마나 상세하게 설정을 입력할 수 있는지 정도가 다르다.

다음 명령어로 키쌍을 생성한다.

[살행] gpg --full-generate-key
gpg --full-generate-key
  • 이름, 이메일 등을 물어본다.
  • 키의 길이를 물어본다 4096 추천함.
  • 만료기간을 설정할 수 있다.

passphrase를 물어본다.

여기서 입력한 값을 이후 나올 pgp 플러그인에 명시해야 한다.

입력하지 않고 건너뛰어도 상관없지만 가급적 입력하는게 좋다.

키가 생성됐으면 다음 명령어로 확인해보자.

[확인] gpg --list-keys (공개키)
$ gpg --list-keys pub rsa3072 2024-11-22 [SC] [expires: 2026-11-22] 59______71 uid [ultimate] YourName <your@email> sub rsa3072 2024-11-22 [E] [expires: 2026-11-22]
[확인] gpg --list-secret-keys (개인키)
$ gpg --list-secret-keys sec rsa3072 2024-11-22 [SC] [expires: 2026-11-22] 59______71 uid [ultimate] YourName <your@email> ssb rsa3072 2024-11-22 [E] [expires: 2026-11-22]

본인의 이름, 이메일 등을 uid에서 확인할 수 있다.

59______71는 공개키와 개인키 쌍을 대표하는 문자열이다. 이 문자열 자체는 키값이 아니고 공개키를 조회할 때 사용된다.

개인키의 sec와 공개키의 pub가 지문값을 생성하고 검증하는데 사용된다.

  • 로컬에서 개인키 sec를 사용해서 지문값을 생성하고
  • 중앙 저장소에서는 공개키 pub를 사용해서 지문값을 검증함

사람이 하는게 아니고, 플러그인이 다 해준다.

3.1.2. 공개키 등록

그렇다면 상대방(중앙 저장소)에게 어떻게 나의 공개키를 제공할 것인가?

이를 위해서 공개키를 모아두는 public keyserver 라는 곳이 존재한다.

메이븐 중앙 저장소에서는 아래의 서버들에서 공개키를 조회한다. distributing-your-publick-key

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

이제 아래의 명령어로 나의 공개키를 keysever에 등록한다. 중앙 저장소가 파일들을 검증할 때 사용할 것이다.

[살행] 공개키 등록
$ gpg --keyserver keyserver.ubuntu.com --send-keys 59______71

문자열 59______71에 대응하는 공개키를 keyserver.ubuntu.com 에 업로드한다.

3.2. maven-gpg-plugin 설정

다음과 같이 maven-gpg-plugin 을 설정한다.

[설정] pom.xml
<project> ... <build> ... <plugins> ... <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-gpg-plugin</artifactId> <version>3.1.0</version> <executions> <execution> <id>sign-artifacts</id> <phase>verify</phase> <goals> <goal>sign</goal> </goals> <configuration> <keyname>59______71</keyname> <passphrase>???????</passphrase> <gpgArguments> <arg>--pinentry-mode</arg> <arg>loopback</arg> </gpgArguments> </configuration> </execution> </executions> </plugin> ... </plugins> </build>
  • <keyname>59______71</keyname> - Section#3.1.2 에서 확인한 키쌍의 이름을 입력한다.
  • <passphrase>???????</passphrase> - Section#3.1.2에서 입력한 passpharse를 입력한다. 만일 passphrase를 설정하지 않았으면 <passphrase></passphrase>로 입력한다.

개인키 sec를 사용해서 .asc 파일을 생성한다.

설정 경로는 project/build/plugins 이어야 한다.

project/build/pluginManagement/plugins 에 설정하면 각자의 maven 설정 구조에 따라서 maven-gpg-plugin실행되지 않을 수 있다.

3.3. token 설치

메이븐 플러그인이 산출물과 지문값을 업로드할 때 사용할 username/password가 필요하다.

Section#1 에서 회원가입했던 Maven Centeral Repsitory 화면에서 내 계정 > View Account 를 클릭하면 토큰을 등록하는 화면이 나타난다.

[화면] 새로운 토큰
Setup Token-Based Authentication ---- Generate User Token ---- Revoke User Token

Generate User Token을 클릭해서 토큰을 생성한다.

다음과 같이 친절하게 화면이 나타난다.

[확인] 새로운 토큰
UserName [_____________] Password [_____________] Use the following... =================== <server> <id>central</id> <username>____</username> <password>_____</password> </server> ===================

이 화면은 지금 딱 한 번만 볼 수 있다. 잘 복사해 두자.

분실하면 다시 토큰을 발급해서 username 과 password를 생성해야 한다.

이제 <server>...</server>settings.xml maven 설정 파일에 입력해야 한다.

[설정] settings.xml
<settings> <servers> <server> <id>central</id> <username>_____</username> <password>_______</password> </server> </servers> </settings>

Section#3 에 입력한 pom.xml 파일의 <publishingServerId/> 를 통해서 username/password를 사용한다.

[확인] pom.xml
<configuration> <publishingServerId>central</publishingServerId> </configuration>

central-publishing-maven-plugin 플러그인에 입력한 값과 일치하는지 확인한다.

4. 배포

여기까지 설정했다면 다음의 메이븐 명령어로 빌드하고 저장소에 출판까지 하게 된다.

[실행] 컴파일, 패키징, 출판
$ mvn clean install deploy
  • clean - target 디렉토리를 지움
  • install - 프로젝트 컴파일 및 jar 패키징, 소스코드와 문서 역시 jar로 패키징
  • deploy - jar signing, 체크섬 생성, 및 중앙 저장소로 업로드

출판이 완료되려면 약 10분 정도가 걸린다.

sonartype 계정 화면에 들어가면 현재 배포 중인 컴포넌트를 확인할 수 있다.

publishing to maven central repository

4.1. 디버깅

전체 과정이 한 번에 성공할 가능성은 낮다.

문제가 발생하면 다음과 같이 로그를 파일로 얻어낼 수 있다.

로그 생성
$ mvn clean install deploy > debug.log

엄청나게 복잡한 내용이 들어있을텐데 대부분의 오류는 이 두 지점에서 발생할 것이다.

[로그] maven-gpg-plugin
[INFO] --- maven-gpg-plugin:3.1.0:sign (sign-artifacts) @ _____ --- [INFO] Signing 4 files with 59______71 secret key. ...
[로그] central-publishing-maven-plugin
[INFO] --- central-publishing-maven-plugin:0.6.0:publish (injected-central-publishing) @ _____ --- [INFO] Using Central baseUrl: https://central.sonatype.com [INFO] Using credentials from server id central in settings.xml [INFO] Using Usertoken auth, with namecode: _____ [INFO] Staging 7 files ... [INFO] Created bundle successfully /____/target/central-staging/central-bundle.zip [INFO] Going to upload /____/target/central-publishing/central-bundle.zip

central-bundle.zip 이 최종 산출물이다.

중앙 저장소에서는 압축을 해제해서 파일을 검증한 후 플러그인을 배포하는데 약 10분이 걸린다.

다음은 몇가지 배포 실패 이유들이다.

TXT
Component with package url: 'pkg:maven/io.github.___.___/___@1.0.0' already exists Missing signature for file: ___-1.0.0-javadoc.jar Missing signature for file: ___-1.0.0-sources.jar
  • 1.0.0이 이미 배포 중인데 동일한 버전으로 업로드했음.(pom.xml 에서 <version/>을 수정한다)
  • 소스코드와 java doc이 없음. 로그에서 maven-source-pluginmaven-javadoc-plugin 이 실행됐는지 확인해 본다.