Maven Central에 안드로이드 라이브러리 배포하기
이번에 첫 라이브러리 Compose Dynamic Theme을 배포하면서, Maven Central에 라이브러리를 배포하기 위한 한국어 자료가 거의 없다는 것을 알게 되었다. 이에 따라 Step by Step으로 배포하는 방법에 대해 정리하면 한국에서 오픈소스 기여 문화가 더욱 활성화될 수 있을 것 같아서 정리하게 되었다.
*Compose Dynamic Theme 라이브러리를 보시고 유용하다고 생각되면 Star ⭐ 을 눌러 오픈소스 프로젝트를 지원해 주세요!
배포 순서
- Sonatype Jira 계정 생성 및 로그인
- Jira에서 오픈소스 프로젝트 이슈 생성하기
- 오픈소스 프로젝트 소유주 인증하기
- Nexus에 로그인하기
- GPG Key Pair 생성하기
- Gradle 설정하기
- Gradle Task 실행해 Nexus Repository Manager에 라이브러리 올리기
- Nexus에 배포하기
Maven Central에 안드로이드 라이브러리를 위에서는 Sonatype에 Jira Account를 생성해야 한다.
Sonatype Jira 계정 생성하기
1. Sonatype Jira 주소인 https://issues.sonatype.org/secure/Dashboard.jspa 에 들어가서 Sign up 버튼을 누른다.
2. Email(이메일), Full Name(이름), Username(유저 아이디), Password(패스워드)를 모두 입력한다. 이 중 Username은 로그인하는데 쓰이는 아이디이니 꼭 기억한다.
3. 앞서 만든 아이디로 로그인을 진행한다.
Jira에서 오픈소스 프로젝트 이슈 생성하기
1. 로그인을 하면 보이는 화면에서 오른쪽 상단의 '만들기'를 클릭한다.
그러면 아래와 같은 이슈 만들기 화면이 나타난다.
2. 프로젝트는 Community Support - Maven Central(MVNCENTRAL)로 설정한다.
3. 이슈 유형은 New Project로 설정한다.
4. 요약과 설명은 프로젝트의 설명으로 채운다.
5. Group Id는 io.github.[Github User Name] 으로 만든다. 이후 이에 대한 검증이 들어가기 때문에 꼭 제대로 만들어야 한다.
6. Project Url에는 배포할 프로젝트의 Url을 적는다.
SCM url은 Github Repository의 Code -> SSH에서 확인할 수 있다. git@github.com:[GitHub User Name]/[Github Repository Name].git 으로 하면 된다.
예를 들어 아이디가 seyoungcho2이고 Repository이름이 ComposeDynamicTheme이라면 git@github.com:seyoungcho2/ComposeDynamicTheme.git 와 같이 설정하면 된다.
7. 마지막으로 Username에는 자신의 Jira Username을 적으면 된다.
자 이제 모두 입력했으면 만들기 버튼을 눌러 이슈를 만들자
그러면 다음과 같은 화면이 보일 것이다.
하지만 아직 이 프로젝트가 자기 것이라는 인증이 되지 않았으므로 인증을 해야 한다.
오픈소스 프로젝트 소유주 인증하기
1. 오픈소스 프로젝트의 소유주 인증을 하기 위해서는 Jira Issue 번호에 해당하는 이름을 가진 Repository를 해당 Url에 생성하라는 댓글이 달린다.
2. 내 Issue 번호는 OSSRH-91479 이므로 OSSRH-91479로 Repository를 만든다.
3. 만든 후 댓글을 달면 봇이 Central Repository에 공간이 만들어졌다는 댓글을 단다.
Nexus에 로그인하기
Nexus 주소 : https://s01.oss.sonatype.org/#welcome
1. 위의 Nexus 주소로 들어가 Log In을 누르면 아래와 같은 화면이 뜬다. 여기에 Jira Username과 비밀번호를 치면 로그인이 된다.
2. 여기서 왼쪽 메뉴 중 Staging Profile을 누른 후 들어가면 프로필이 뜨는데 여기서 프로필 줄(Row)을 클릭하면 주소창에 아래와 같이 생성된다.
https://s01.oss.sonatype.org/#stagingProfiles;[Profile Id]
여기서 [Profile Id]를 메모장에 메모해 둔다.
GPG Key Pair 생성하기
1. 아래 명령어를 사용해 gpg를 설치한다.
brew install gpg
2. gpg key를 생성하기 위해 아래 명령어를 사용한다.
gpg --full-gen-key
3. Please select what king of key you want 가 뜨면 1번 RSA and RSA를 선택한다.
gpg: directory '/Users/sy/.gnupg' created
Please select what kind of key you want:
(1) RSA and RSA
(2) DSA and Elgamal
(3) DSA (sign only)
(4) RSA (sign only)
(9) ECC (sign and encrypt) *default*
(10) ECC (sign only)
(14) Existing key from card
Your selection? 1
4. RSA key의 길이를 선택하라는 것이 나온다. 4096을 입력한다.
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (3072) 4096
5. Key의 expire 시간을 정하는 것이 나온다. 편의를 위해 기간 제한이 없는 0번을 선택하지만 다른 옵션을 선택해도 된다.
Please specify how long the key should be valid.
0 = key does not expire
<n> = key expires in n days
<n>w = key expires in n weeks
<n>m = key expires in n months
<n>y = key expires in n years
Key is valid for? (0) 0
0번을 선택하면 제대로 선택한 것이 맞는 질문이 나오고 y를 입력해 맞다고 답한다.
Is this correct? (y/N) y
6. 마지막으로 이름, 이메일, 코멘트를 쓰라는 란이 나오고 그대로 쓰면 된다.
Real name: [이름]
Email address: [이메일]
Comment: 안써도된다
모두 입력하면 맞게 입력되었는지 확인하는 것이 나오고 맞다면 O를 입력하면 된다.
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
7. 패스워드 입력
GPG Key Password를 입력한다. 2번 반복해서 입력한다.
8. GPG Key gen 완료
자 이제 키가 만들어졌다는 것이 나온다. 퍼블릭키의 마지막 8자리를 복사해 메모해두자. 16진수로 'ABCD1234' 와 같은 형식일 것이다.
pub rsa4096 2023-05-09 [SC]
[퍼블릭키]
uid [이름] <[이메일]>
sub rsa4096 2023-05-09 [E]
8. 이제 이 키를 서버에 등록하기 위해 아래와 같은 명령어를 사용한다.
gpg --keyserver keyserver.ubuntu.com --send-keys [퍼블릭키 마지막 8자리]
9. 다음 명령어를 통해 GPG Key를 export한다. 이 명령어를 통해 나오는 문자열을 GPG Key로 메모한다.
gpg --export-secret-keys [퍼블릭키 마지막 8자리] | base64
자 이제 키가 만들어지고 등록되었다. 위에서 보라색 색상으로 표기된 값들은 배포시 필요하니 모두 메모해두자.
Gradle 설정하기
1. 자 이제 앞서 저장한 값들을 사용해 local.properties나 Environment Variable에 다음 Key-Value Pair을 저장한다.
* local.properties에 저장하는 경우 Remote Repository에 올리지 않도록 조심한다. 왠만해서는 Environment Variable에 저장하는 것이 보안 사고 방지를 위해 좋다.
signing.keyId=[퍼블릭키 마지막 8자리]
signing.password=[GPG Key Password]
signing.key=[GPG Key]
ossrhUsername=[Jira Username]
ossrhPassword=[Jira Password]
sonatypeStagingProfileId=[Profile Id]
2. Root 디렉토리의 script 폴더에 publish-root.gradle 파일을 만든다.
publish-root.gradle 은 local.properties에 앞서 설정한 값이 있으면 읽어오고 없으면 Environment Variable에서 읽어온다.
publish-root.gradle
// Create variables with empty default values
ext["signing.keyId"] = ''
ext["signing.password"] = ''
ext["signing.key"] = ''
ext["ossrhUsername"] = ''
ext["ossrhPassword"] = ''
ext["sonatypeStagingProfileId"] = ''
File secretPropsFile = project.rootProject.file('local.properties')
if (secretPropsFile.exists()) {
// Read local.properties file first if it exists
Properties p = new Properties()
new FileInputStream(secretPropsFile).withCloseable { is -> p.load(is) }
p.each { name, value -> ext[name] = value }
} else {
// Use system environment variables
ext["ossrhUsername"] = System.getenv('OSSRH_USERNAME')
ext["ossrhPassword"] = System.getenv('OSSRH_PASSWORD')
ext["sonatypeStagingProfileId"] = System.getenv('SONATYPE_STAGING_PROFILE_ID')
ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID')
ext["signing.password"] = System.getenv('SIGNING_PASSWORD')
ext["signing.key"] = System.getenv('SIGNING_KEY')
}
// Set up Sonatype repository
nexusPublishing {
repositories {
sonatype {
stagingProfileId = sonatypeStagingProfileId
username = ossrhUsername
password = ossrhPassword
nexusUrl.set(uri("https://s01.oss.sonatype.org/service/local/"))
snapshotRepositoryUrl.set(uri("https://s01.oss.sonatype.org/content/repositories/snapshots/"))
}
}
}
3. 프로젝트 수준의 build.gradle.kts파일을 다음과 같이 설정한다.
plugins {
id("io.github.gradle-nexus.publish-plugin").version("1.1.0")
}
buildscript {
dependencies {
this.classpath("io.github.gradle-nexus:publish-plugin:1.1.0")
}
}
apply {
this.from("scripts/publish-root.gradle")
}
4. 배포를 원하는 모듈에 publish-remote.gradle 파일을 아래와 같이 생성한다.
[] 으로 감싸져 있는 부분을 모두 채운다.
ext {
PUBLISH_GROUP_ID = '[패키지명]'
PUBLISH_VERSION = '[버전명]'
PUBLISH_ARTIFACT_ID = '[Artifact명]'
}
apply plugin: 'maven-publish'
apply plugin: 'signing'
task androidSourcesJar(type: Jar) {
archiveClassifier.set('sources')
if (project.plugins.findPlugin("com.android.library")) {
// For android libraries
from android.sourceSets.main.java.srcDirs
from android.sourceSets.main.kotlin.srcDirs
} else {
// For pure kotlin libraries, in case you have them
from sourceSets.main.java.srcDirs
from sourceSets.main.kotlin.srcDirs
}
}
artifacts {
archives androidSourcesJar
}
group = PUBLISH_GROUP_ID
version = PUBLISH_VERSION
afterEvaluate {
publishing {
publications {
release(MavenPublication) {
// The coordinates of the library, being set from variables that
// we'll setup later
groupId PUBLISH_GROUP_ID
artifactId PUBLISH_ARTIFACT_ID
version PUBLISH_VERSION
// Two artifacts, the `aar` (or `jar`) and the sources
if (project.plugins.findPlugin("com.android.library")) {
from components.release
} else {
from components.java
}
artifact androidSourcesJar
// artifacts javaDocJar
// Mostly self-explanatory metadata
pom {
name = '[라이브러리 이름]'
description = '[라이브러리 설명]'
url = '[오픈소스 Repository Url]'
licenses {
license {
name = '[라이센스명]'
url = '[라이센스 url]'
}
}
developers {
developer {
id = '[사용자 아이디]'
name = '[사용자 이름]'
email = "[사용자 이메일"
url = "[사용자 Url]"
}
}
// Version control info
scm {
connection = 'scm:git:github.com/[Github 사용자명]/[오픈소스 Repository 이름].git'
developerConnection = 'scm:git:ssh://github.com/[Github 사용자명]/[오픈소스 Repository 이름].git'
url = 'https://github.com/[Github 사용자명]/[오픈소스 Repository 이름]/tree/[배포 브랜치명]'
}
}
}
}
}
}
signing {
useInMemoryPgpKeys(
rootProject.ext["signing.keyId"],
rootProject.ext["signing.key"],
rootProject.ext["signing.password"]
)
sign publishing.publications
}
자 이제 gradle 설정이 끝났다.
Gradle Task 실행해 Nexus Repository Manager에 라이브러리 올리기
자 이제 레포지토리의 터미널에서 아래 명령어를 실행해 Nexus에 라이브러리를 올리자.
./gradlew [모듈명]:publishReleasePublicationToSonatypeRepository
BUILD SUCCESSFUL이 나오면 https://s01.oss.sonatype.org/#stagingRepositories 에서 아래와 같이 라이브러리가 올라간 것을 확인할 수 있다.
Nexus에 배포하기
1. Close - Confirm을 누르자.
2. 그러면 한 1분 후에 Release버튼이 활성화된다. 이후 Release를 누르면 배포된다.
3. 30분쯤 지나면 Maven Central에 배포된 것을 확인할 수 있다. 아래 링크에서 자신의 패키지를 찾아보자.
https://central.sonatype.com/?smo=true