[나의 공부방] 나만의 오픈 소스 만들어서 Maven Central에 배포하기
이번에는 나만의 오픈소스를 만들고, maven central에 배포하는 방법에 대해 알아보도록 하겟습니다.
1. 나만의 오픈 소스 만들어서 Maven Central에 배포하기
[ 나만의 오픈 소스 만들어서 Maven Central에 배포하기 ]
OSSRH는 sonatype nexus라는 회사가 지원하는 서비스이며, 오픈 소스를 호스팅할 수 있도록 도와주고 있다. 소스 코드를 배포하기 위해서는 다음과 같은 절차가 필요하므로 순서대로 진행해보도록 하자. OSSRH 지라 계정이 없다면 먼저 계정 생성을 하도록 하자.
아래 수정 필요
- Jira 티켓 등록하기
- GPG키 생성 및 공개키 전송하기
- gradle 스크립트 추가 및 설정 수정하기
- artifact 스테이지와 리얼에 배포하기
1. Jira 티켓 등록하기
OSSRH에서 오픈 소스를 배포하기 위해서는 지라로 들어가서 오픈 소스 등록을 요청하는 티켓을 만들어야 한다. 그러므로 다음과 같이 본인이 배포할 오픈 소스에 대한 내용을 적어주도록 하자. 여기서 가지고 있는 도메인이 없다면 깃허브 도메인을 이용하기 위해 groupId를 io.github로 시작해주도록 하자.
위와 같이 티켓을 생성하면 이슈가 등록되고, 계정 소유권에 대한 요청이 온다. 그러면 아래에서 요구하는 레포지토리를 생성하여 계정에 대한 소유권을 확인해주도록 하자.
그렇게 레포지토리에 접속한 후에 몇 분이 지나면 이슈가 Resolved 상태가 되어 사용이 가능해진다. 그러므로 이후에 남은 다음 단계들을 이어서 진행해보도록 하자.
2. GPG키 생성 및 공개키 전송하기
이제 우리가 배포할 artifact에 서명을 해서 신뢰할 수 있는 프로젝트임을 보장해야 한다. 이 과정에서는 gpg를 사용할 예정이다. 먼저 gpg가 없다면 다음의 명령어로 설치를 해주도록 하자.
brew install gpg
만약 발급된 개인키/공개키의 key pair가 없다면 다음의 명령어로 키를 발급해주도록 하자. 아래의 명령어를 입력하면 name, email, password를 요구하는데 순서대로 입력해주도록 하자. 그리고 이어서 바로 생성된 키를 확인하도록 하자.
# 키 생성 명령어
gpg --gen-key
# 키 확인 명령어
gpg --list-secret-keys --keyid-format LONG
그러면 다음과 같이 키가 생성된 것을 확인할 수 있다. 그리고 개인키/공개키의 쌍 중에서 공개키를 gpg key 관리 서버에 보내야 한다. 여기서 ABCDE12345678900의 끝에 8자리가 사용된다.
sec rsa4096/ABCDE12345678900 2022-04-04 [SC] [expires: 2024-04-03]
6925305B488E0597199799752C367DFC4783C254
uid MangKyu <whalsrb1226@gmail.com>
sub cv25519 2022-04-04 [E] [expires: 2024-04-03]
그러므로 다음과 같은 명령어를 통해 keyserver.ubuntu.co에 생성한 공개키를 등록해주도록 하자. 다른 서버에 보내도 상관없다. 그리고 이어서 바로 secret key ring에 해당하는 signing.gpg 파일을 생성해주도록 하자.
# 공개키 전송
gpg --keyserver keyserver.ubuntu.com --send-keys 45678900
# signing.gpg 파일 생성
gpg --export-secret-keys 45678900 > signing.gpg
3. gradle 스크립트 추가 및 설정 수정하기
gradle을 이용해 artifact를 배포하기 위해서는 먼저 스크립트가 필요하다. 프로젝트 폴더 하위에 scripts 디렉토리를 생성한 후에 다음과 같은 내용의 publish-maven.gradle파일을 생성해주도록 하자.
apply plugin: 'maven-publish'
apply plugin: 'signing'
apply from: 'publish.gradle'
task sourcesJar(type: Jar) {
archiveClassifier.set("sources")
}
task javadocJar(type: Jar) {
archiveClassifier.set("javadoc")
}
artifacts {
archives sourcesJar
archives javadocJar
}
group = PUBLISH_GROUP_ID
version = PUBLISH_VERSION
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.secretKeyRingFile"] = ''
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
ext["sonatypeStagingProfileId"] = ''
File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
Properties p = new Properties()
p.load(new FileInputStream(secretPropsFile))
p.each { name, value ->
ext[name] = value
}
} else {
ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_SECRET_KEY_RING_FILE')
ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
}
publishing {
publications {
release(MavenPublication) {
groupId PUBLISH_GROUP_ID
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION
artifact("$buildDir/libs/${project.getName()}-${version}.jar")
artifact sourcesJar
artifact javadocJar
pom {
name = PUBLISH_ARTIFACT_ID
description = PUBLISH_DESCRIPTION
url = PUBLISH_URL
licenses {
license {
name = PUBLISH_LICENSE_NAME
url = PUBLISH_LICENSE_URL
}
}
developers {
developer {
id = PUBLISH_DEVELOPER_ID
name = PUBLISH_DEVELOPER_NAME
email = PUBLISH_DEVELOPER_EMAIL
}
// Other devs...
}
scm {
connection = PUBLISH_SCM_CONNECTION
developerConnection = PUBLISH_SCM_DEVELOPER_CONNECTION
url = PUBLISH_SCM_URL
}
withXml {
def dependenciesNode = asNode().appendNode('dependencies')
project.configurations.implementation.allDependencies.each {
def dependencyNode = dependenciesNode.appendNode('dependency')
dependencyNode.appendNode('groupId', it.group)
dependencyNode.appendNode('artifactId', it.name)
dependencyNode.appendNode('version', it.version)
}
}
}
}
}
repositories {
maven {
name = "sonatype"
url = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
credentials {
username ossrhUsername
password ossrhPassword
}
}
}
}
nexusPublishing {
repositories {
sonatype {
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
packageGroup = PUBLISH_GROUP_ID
stagingProfileId = sonatypeStagingProfileId
username = ossrhUsername
password = ossrhPassword
}
}
}
signing {
sign publishing.publications
}
그리고 프로젝트 위치에 publish.gradle 파일을 추가해주도록 하자. 여기서 publish.gradle에는 본인이 배포하고자 하는 프로젝트 정보에 맞게 수정해주어여야 한다.
ext {
PUBLISH_GROUP_ID = 'io.github.mangkyu'
PUBLISH_VERSION = '0.0.1'
PUBLISH_ARTIFACT_ID = 'spring-boot-test-automock'
PUBLISH_DESCRIPTION = 'SpringBoot Test AutoMock Library'
PUBLISH_URL = 'https://github.com/MangKyu/spring-boot-test-automock'
PUBLISH_LICENSE_NAME = 'GNU License'
PUBLISH_LICENSE_URL = 'https://github.com/MangKyu/spring-boot-test-automock/blob/master/LICENSE'
PUBLISH_DEVELOPER_ID = 'MangKyu'
PUBLISH_DEVELOPER_NAME = 'MinKyu Jo'
PUBLISH_DEVELOPER_EMAIL = 'whalsrb1226@gmail.com'
PUBLISH_SCM_CONNECTION = 'scm:git:github.com/MangKyu/spring-boot-test-automock.git'
PUBLISH_SCM_DEVELOPER_CONNECTION = 'scm:git:ssh://github.com:MangKyu/spring-boot-test-automock.git'
PUBLISH_SCM_URL = 'https://github.com/MangKyu/spring-boot-test-automock/tree/master'
}
그 다음에 build.gradle의 가장 상단에는 빌드 스크립트를, plugins 하위에는 추가적인 플러그인 정보를 넣어주도록 하자.
# 추가해야 하는 빌드 스크립트
buildscript {
dependencies {
classpath "io.github.gradle-nexus:publish-plugin:1.1.0"
}
}
# 기존에 존재하는 플러그인 설정
plugins {
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
# 추가해야 하는 플러그인 설정
apply plugin: 'io.github.gradle-nexus.publish-plugin'
apply from: "${rootDir}/scripts/publish-maven.gradle"
apply from: 'publish.gradle'
먼저 publish를 위한 publish.gradle을 프로젝트 위치에 생성해주도록 하자.
ext {
PUBLISH_GROUP_ID = 'io.github.mangkyu'
PUBLISH_VERSION = '0.0.1'
PUBLISH_ARTIFACT_ID = 'spring-boot-test-automock'
PUBLISH_DESCRIPTION = 'SpringBoot Test AutoMock Library'
PUBLISH_URL = 'https://github.com/MangKyu/spring-boot-test-automock'
PUBLISH_LICENSE_NAME = 'GNU License'
PUBLISH_LICENSE_URL = 'https://github.com/MangKyu/spring-boot-test-automock/blob/master/LICENSE'
PUBLISH_DEVELOPER_ID = 'MangKyu'
PUBLISH_DEVELOPER_NAME = 'MinKyu Jo'
PUBLISH_DEVELOPER_EMAIL = 'whalsrb1226@gmail.com'
PUBLISH_SCM_CONNECTION = 'scm:git:github.com/MangKyu/spring-boot-test-automock.git'
PUBLISH_SCM_DEVELOPER_CONNECTION = 'scm:git:ssh://github.com:MangKyu/spring-boot-test-automock.git'
PUBLISH_SCM_URL = 'https://github.com/MangKyu/spring-boot-test-automock/tree/master'
}
그리고 프로젝트 위치에 local.properties 파일을 생성하고, artifact를 배포할 정보를 입력해주어야 한다. 그 전에 한가지 profieId를 확인해주어야 하는데, 먼저 레포지토리 관리 링크로 들어와 로그인하도록 하자. 그리고 로그인하여 왼쪽의 Staging Profile로 접속하자. 그러면 다음과 같이 하나의 정보가 나오고 그걸 누르면 주소 창에 프로파일 아이디(80f24104c0283a)가 나온다. 이를 보관하고 있도록 하자.
이제 생성한 키를 gradle에 적용해야 한다. 먼저 {프로젝트경로}/.gradle/gradle.properties에 다음의 내용을 추가해주도록 하자. 대괄호는 입력 시에 빼주도록 하자. ex) ossrhUsername=MangKyu
signing.keyId=[pubilc key id] // 8자리 값 ex)45678900
signing.password=[passphrase] // GPG Key 비밀번호
signing.secretKeyRingFile=[siginig file path] // signing.gpg 파일 위치
ossrhUsername=[ossrh username] // OSSRH 계정 이름
ossrhPassword=[ossrh password] // OSSRH 계정 비밀번호
sonatypeStagingProfileId[ossrh profileId] // OSSRH 프로파일 아이디
artifact 스테이지와 리얼에 배포하기
이제 우리가 만든 artifact를 배포할 차례이다. 만약 빌드가 안되어있다면 빌드를 먼저 해주고, 다음의 명령어로 배포를 해주도록 하자.
./gradlew publishReleasePublicationToSonatypeRepository
잘 마무리되었다면 스테이지에 배포가 되었을 것이다. 이번에는 레포지토리 관리 링크에서 Staging Repositories를 클릭하면 우리가 배포한 artifact가 보일 것이다. 그러면 여기서 보이는 close를 눌러서 마무리해주도록 하자.
그리고 잠깐의 시간이 지나면 다음과 같이 Release 버튼이 활성화되는 것을 볼 수 있다. Release를 눌러서 우리가 만든 artifact를 배포해주면 되는데, 경우에 따라서 빠르면 몇 분 느리면 몇시간이 걸린다고 한다.
정상적으로 배포가 되었으면 검색했을때 성공적으로 나올 것이다. 또한 maven 레포지토리에서도 정상적으로 확인할 수 있을 것이다.
그러면 이제 배포된 라이브러리를 직접 사용해주면 된다.