Devahoy Logo
PublishedAt

Android

ขั้นตอนการ Upload Android Library ไปที่ Maven Central

ขั้นตอนการ Upload Android Library ไปที่ Maven Central

วันนี้ผมได้ทำการทดสอบ อัพโหลด Library ขึ้นไปไว้ที่ Maven Central ครั้งแรกครับ ก็เลยนำประสบการณ์มาแชร์เผื่อมีใครที่กำลังจะทำ หรืออยากทำ เชื่อว่ามีนะ เห็นมาถามๆผมอยู่ :D Library ที่ผมทำการ Upload ขึ้นไป คือ Shared เป็น Android SharedPreferences แบบง่ายๆครับ

System & Tools

สำหรับเครื่องมือและระบบปฎิบัติการที่ผมใช้ ก็มีดังนี้ หากใครใช้อย่างอื่น ก็ไปประยุกต์ใช้กันเองนะ

  • Ubuntu 14.04
  • Android Studio 1.0 RC1
  • Gradle 2.2
  • Android Gradle Plugin 0.14.4

ส่วนขั้นตอนการ Upload มีอะไรบ้าง ดูด้านล่างเลย

Table of Contents

Step 1 : Register Sonatype Account

ขั้นตอนแรกสุดเลย ต้องทำการสมัครสมาชิกกับทาง Nexus Sonatype ก่อน หลังจากนั้นก็ทำการโพส Issue ขึ้นใหม่ โดยให้ใส่รายละเอียดว่าเรากำลังจะทำโปรเจ็คอะไร

Create new Issue

  • Summary : กรอกชื่อ Library/Project ของเรา
  • Description : ใส่รายละเอียดซักนิด เพื่อให้รู้ว่าคือโปรเจ็คอะไร
  • Group Id : ใส่ groupId ที่ไม่ให้ซ้ำกัน คล้ายๆกับการตั้งชื่อ package name แต่ควรจะใช้ชื่อโดเมนของเราแบบ Reverse ถ้าไม่มีโดเมนก็ใช้โดเมนเดียวกันกับที่เราฝาก repository ไว้ รายละเอียดเพิ่มเติมและตัวอย่างมีเขียนไว้แล้ว
  • Project URL : ใส่ที่อยู่ของ Library ของเรา ในตัวอย่างผมใช้ repository ที่ฝากไว้ที่ Github
  • SCM url : ที่อยู่ของ source control system เหมือนกับด้านบน แค่เพิ่ม .git เข้าไป
  • Already Synced to Central : ถ้ายังไม่เคย Sync ไรเลย ก็เลือก None หรือ No ไป

พร้อมแล้วก็กด Create เลยครับ เมื่อ Issue ของเราถูกสร้าง ก็จะมีทีมงานของ Sonanype มาอนุมัติไม่ใช่ออโต้ ฉะนั้นก็ต้องรอประมาณ 1-2 วัน หรือโชคดีหน่อยก็อาจจะได้เร็ว (ของผมใช้เวลา 10 ชม. ถึงจะมีทีมงานมาตอบ)

Config

โอเค เมื่อทีมงานมาตอบแล้ว เค้าก็จะแนะนำ ว่าเราจะต้องอัพโหลด Snapshot , Release, Staged Artifacts ไปที่ URL ไหนบ้าง แล้วเมื่ออัพโหลดเสร็จแล้ว ก็มาคอมเม้นตอบเค้าด้วย

หากใครทำแล้ว ยังไม่มีคนตอบ ก็ข้ามไปทำ Step อื่นได้นะครับ แต่อย่าเพิ่ง Upload อะไรขึ้นไป (ผมไม่แน่ใจว่าจะเป็นไรไหม เพราะไม่เคยทำก่อน :D )

Step 2 : Create Gradle Script

มาถึงขั้นตอนสร้าง Gradle Script อันนี้ผมอ้างอิงจาก Publish an aar file to Maven Central with Gradle และก็ดูพวกตัวอย่าง Library อื่นๆ ประกอบ เห็นว่าแต่ละอันก็เหมือนกัน ฉะนั้นก็เลยก็อปมาซะเลย

ผมทำการสร้างไฟล์ขึ้นมาใหม่ ชื่อว่า maven_push.gradle ใส่ไว้ที่ Root Project เลย

1
apply plugin: 'maven'
2
apply plugin: 'signing'
3
4
def sonatypeRepositoryUrl
5
if (isReleaseBuild()) {
6
println 'RELEASE BUILD'
7
sonatypeRepositoryUrl = hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
8
: "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
9
} else {
10
println 'DEBUG BUILD'
11
sonatypeRepositoryUrl = hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
12
: "https://oss.sonatype.org/content/repositories/snapshots/"
13
}
14
15
def getRepositoryUsername() {
16
return hasProperty('nexusUsername') ? nexusUsername : ""
17
}
18
19
def getRepositoryPassword() {
20
return hasProperty('nexusPassword') ? nexusPassword : ""
21
}
22
23
afterEvaluate { project ->
24
uploadArchives {
25
repositories {
26
mavenDeployer {
27
beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
28
29
pom.artifactId = POM_ARTIFACT_ID
30
31
repository(url: sonatypeRepositoryUrl) {
32
authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
33
}
34
35
pom.project {
36
name POM_NAME
37
packaging POM_PACKAGING
38
description POM_DESCRIPTION
39
url POM_URL
40
41
scm {
42
url POM_SCM_URL
43
connection POM_SCM_CONNECTION
44
developerConnection POM_SCM_DEV_CONNECTION
45
}
46
47
licenses {
48
license {
49
name POM_LICENCE_NAME
50
url POM_LICENCE_URL
51
distribution POM_LICENCE_DIST
52
}
53
}
54
55
developers {
56
developer {
57
id POM_DEVELOPER_ID
58
name POM_DEVELOPER_NAME
59
}
60
}
61
}
62
}
63
}
64
}
65
66
signing {
67
required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
68
sign configurations.archives
69
}
70
71
task androidJavadocs(type: Javadoc) {
72
source = android.sourceSets.main.java.sourceFiles
73
}
74
75
task androidJavadocsJar(type: Jar) {
76
classifier = 'javadoc'
77
//basename = artifact_id
78
from androidJavadocs.destinationDir
79
}
80
81
task androidSourcesJar(type: Jar) {
82
classifier = 'sources'
83
//basename = artifact_id
84
from android.sourceSets.main.java.sourceFiles
85
}
86
87
artifacts {
88
//archives packageReleaseJar
89
archives androidSourcesJar
90
archives androidJavadocsJar
91
}
92
}

จากด้านบน ก็มีในส่วนการใช้ Plugin Maven และ Signing

1
apply plugin: 'maven'
2
apply plugin: 'signing'

และเมื่อสังเกต Task uploadArchives จะเห็น sonatypeRepository ก็คือ URL ที่ทาง Sonatype บอกให้เราทำการ Upload ไว้ในคอมเม้นใน Step 1 แต่ว่าตัว maven_push.gradle ตั้งค่าไว้ให้แล้ว ที่เหลือเราก็แค่ไปเปลี่ยนค่าในไฟล์ gradle.properties เท่านั้นเอง เพราะเห็นว่าค่าบางตัว มันคือตัวแปลที่กำหนดไว้ในไฟล์ gradle.properties

ฉะนั้นเปิดไฟล์ gradle.properties ตัวที่อยู่ที่ Root Project นะไม่ใช่ใน Module

1
VERSION_NAME=0.0.1
2
VERSION_CODE=1
3
GROUP=com.devahoy
4
5
POM_DESCRIPTION=A Simple Android SharedPreferences
6
POM_URL=https://github.com/devahoy/shared
7
POM_SCM_URL=https://github.com/devahoy/shared
8
POM_SCM_CONNECTION=scm:git@github.com:devahoy/shared.git
9
POM_SCM_DEV_CONNECTION=scm:git@github.com:devahoy/shared.git
10
POM_LICENCE_NAME=The Apache Software License, Version 2.0
11
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
12
POM_LICENCE_DIST=repo
13
POM_DEVELOPER_ID=phonbopit
14
POM_DEVELOPER_NAME=Chai Phonbopit

ด้านบนเป็นตัวอย่างของผม ท่านก็ต้องไปเปลี่ยนเป็นรายละเอียดของท่านเอาเอง

ดูรายละเอียดเปรียบเทียบกับไฟล์ของผมได้ที่นี่ maven_push.gradle และ gradle.properties

Step 3 : Create Module Properties

หลักจากเปลี่ยนแปลงค่าใน Root Project เรียบร้อยแล้ว ต่อมาก็เปลี่ยนแปลงค่าใน Module กันบ้าง ซึ่ง Module มันก็คือ Library Project ของเราใช่มั้ยละ อย่างตอนนี้ File Structure ของผมเป็นแบบนี้

Terminal window
├── build
│   └── intermediates
├── build.gradle
├── gradle
│   └── wrapper
├── gradle.properties
├── gradlew
├── gradlew.bat
├── library
│   ├── build.gradle
│   ├── gradle.properties
│   ├── library.iml
│   ├── libs
│   ├── proguard-rules.pro
│   └── src
├── LICENSE.txt
├── local.properties
├── maven_push.gradle
├── README.md
├── settings.gradle
└── Shared.iml

ทำการเปิดไฟล์ gradle.properties ใน Module ซึ่งของผมคือในโฟลเดอร์ /library/gradle.properties จากนั้นก็เพิ่มรายละเอียดของ POM ลงไป

1
POM_NAME=Shared
2
POM_ARTIFACT_ID=shared
3
POM_PACKAGING=aar

เปลี่ยน POM_NAME และ POM_ARTIFACT_ID เป็นของท่านด้วยนะ เอาแบบ จำง่ายๆก็ได้ เวลาคนอื่นเอาไปใช้จะได้จำง่ายๆ ฟอแมตมันคือ

1
dependencies {
2
compile 'GROUP_ID:POM_ARTIFACT_ID:VERSION_CODE'
3
}

ต่อมา เปิดไฟล์ build.gradle ใน Module ซึ่งของผมก็ยังอยู่ในโฟลเดอร์ /library/build.gradle จากนั้นทำการเพิ่มโค๊ดนี้ลงไปบรรทัดล่างสุดเลย

1
apply from: '../maven_push.gradle'

ดูรายละเอียดเปรียบเทียบกับไฟล์ของผมได้ที่นี่ build.gradle และ gradle.properties

Step 4 : Signing Artifacts with GPG

ขั้นตอนนี้ต้องทำการ Siging ด้วย GPG ก่อน ผมทำบน Ubuntu ส่วน Mac OS X หรือ Windows วิธีการทำไม่รู้ว่าเป็นยังไง ต้องไปหาดูเอาเองนะครับ แต่คิดว่าวิธีคล้ายๆกัน คือต้องการ pub ID และการ Publish Key ไปที่ Server ถ้าท่านไม่ใช่ Ubuntu ก็ข้ามขั้นนี้ไปเลยครับ

ขั้นแรก ติดตั้ง gnugpg

Terminal window
sudo apt-get install gnupg

ทำการ Gen Key

Terminal window
gpg --gen-key

จากนั้นก็ทำการขั้นตอนตาม Command Line เลย

1
Please select what kind of key you want:
2
(1) RSA and RSA (default)
3
(2) DSA and Elgamal
4
(3) DSA (sign only)
5
(4) RSA (sign only)
6
Your selection? 1
7
RSA keys may be between 1024 and 4096 bits long.
8
What keysize do you want? (2048) 4096
9
Requested keysize is 4096 bits
10
Please specify how long the key should be valid.
11
0 = key does not expire
12
<n> = key expires in n days
13
<n>w = key expires in n weeks
14
<n>m = key expires in n months
15
<n>y = key expires in n years
16
Key is valid for? (0) 0
17
Key does not expire at all
18
Is this correct? (y/N) y
19
20
You need a user ID to identify your key; the software constructs the user ID
21
from the Real Name, Comment and Email Address in this form:
22
"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"
23
24
Real name: DevAhoy
25
Email address: hello@devahoy.com
26
Comment:
27
You selected this USER-ID:
28
"DevAhoy <hello@devahoy.com>"
29
30
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
31
You need a Passphrase to protect your secret key.
32
33
We need to generate a lot of random bytes. It is a good idea to perform
34
some other action (type on the keyboard, move the mouse, utilize the
35
disks) during the prime generation; this gives the random number
36
generator a better chance to gain enough entropy.

เมื่อกรอกรายละเอียดเสร็จ ก็ให้ใส่ Passphrase หรือรหัสที่เราจะใช้ จากนั้น ก็รอมัน Generate (แนะนำว่าหาอะไรทำ ขยับเมาท์ นั่งพิมพ์ หรือเปิด Browser อะไรไปก็ได้ ไม่งั้นมันจะ Gen ไม่ได้)

หาก Gen เสร็จแล้ว ให้เราทำการเช็คว่าโอเคไหม

1
gpg --list-keys

ที่เราจะใช้ก็คือรหัส ประมาณนี้

1
pub 4096R/XXXXXXXX 2014-11-25
2
uid DevAhoy <hello@devahoy.com>

ใช้รหัส ที่ได้ Publish ไปที่ Server (ส่วนนี้จะเอาไว้ยืนยันตัวตน เวลาเรา Publish ไปที่ Maven แบบ Released version)

1
gpg --keyserver hkp://keyserver.ubuntu.com --send-keys XXXXXXXX
2
$ gpg --keyserver hkp://pgp.mit.edu --send-keys XXXXXXXX

จำรหัสในตำแหน่ง XXXXXXXX เพราะเราจะเอาไว้ใช้ใน Step 5 อีกที

Step 5 : Upload to Maven Central

มาถึงขั้นตอนสุดท้ายละ คราวนี้เปิดไฟล์ gradle.properties ของ Gradle Home คนละตัวกับในโปรเจ็คและใน Module นะ อันนี้จะเป็นตัว Global เลย ปกติน่าจะอยู่ที่ ~/.gradle/ ถ้าใน Windows น่าจะประมาณ C:\Users\UserName\.gradle ถ้าจำไม่ผิด

จากนั้นก็ใส่รายละเอียดของเราลงไป ส่วนนี้เป็นข้อมูลที่เป็นความลับนะ อย่าเผลอติดมากับ Repository ละ

1
signing.keyId=PUB_ID
2
signing.password=YOUR_PASSPHRASE
3
signing.secretKeyRingFile=/home/UserName/.gnupg/secring.gpg
4
5
nexusUsername=YOUR_NEXUS_USERNAME
6
nexusPassword=YOUR_NEXUS_PASSWORD

เปลี่ยนรายละเอียดด้านบน เป็นของท่านเอง

  • signing.keyId ก็คือ รหัสที่ได้จาก GPG
  • signing.password : Passphrase ที่ตั้งไว้
  • signing.secretKeyRingFile : ที่อยู่ของไฟล์ secretKey อยู่ในโฟลเดอร์ .gnupg ของ User อันนี้น่าจะเหมือนกัน
  • nexusUsername : ชื่อ Username ของ Nexus Sonatype ที่สมัครใน Step 1
  • nexusPassword : password ที่ใช้ของ Nexus Sonatype เช่นกัน ได้จาก Step 1

OK เตรียมพร้อมทุกอย่างเรียบร้อยแล้ว ต่อมาเปิด Terminal ใน Android Studio ก็ได้ แล้วรันคำสั่ง

1
gradle uploadArchives

รออัพโหลดขึ้น Maven ซักพัก ก็จะได้ข้อความด้านล่าง ว่าอัพเสร็จแล้ว (ตื่นเต้นดี :D)

1
Uploading: com/devahoy/library/0.0.1/library-0.0.1-20141125.161932-1.aar to repository remote at https://oss.sonatype.org/content/repositories/snapshots/
2
Transferring 52K from remote
3
Uploaded 52K
4
Uploading: com/devahoy/library/0.0.1/library-0.0.1-20141125.161932-1-sources.jar.asc to repository remote at https://oss.sonatype.org/content/repositories/snapshots/
5
Transferring 0K from remote
6
Uploaded 0K
7
Uploading: com/devahoy/library/0.0.1/library-0.0.1-20141125.161932-1.aar.asc to repository remote at https://oss.sonatype.org/content/repositories/snapshots/
8
Transferring 0K from remote
9
Uploaded 0K
10
Uploading: com/devahoy/library/0.0.1/library-0.0.1-20141125.161932-1-javadoc.jar.asc to repository remote at https://oss.sonatype.org/content/repositories/snapshots/
11
Transferring 0K from remote
12
Uploaded 0K
13
Uploading: com/devahoy/library/0.0.1/library-0.0.1-20141125.161932-1-javadoc.jar to repository remote at https://oss.sonatype.org/content/repositories/snapshots/
14
Transferring 0K from remote
15
Uploaded 0K
16
Uploading: com/devahoy/library/0.0.1/library-0.0.1-20141125.161932-1-sources.jar to repository remote at https://oss.sonatype.org/content/repositories/snapshots/
17
Transferring 1K from remote
18
Uploaded 1K
19
Uploading: com/devahoy/library/0.0.1/library-0.0.1-20141125.161932-1.pom.asc to repository remote at https://oss.sonatype.org/content/repositories/snapshots/
20
Transferring 0K from remote
21
Uploaded 0K
22
23
BUILD SUCCESSFUL

เมื่ออัพโหลด เสร็จเรียบร้อยแล้ว ตรวจเช็ค Library ของเราในเว็บ OSSRH web UI ด้วย จะอยู่ List ล่างๆสุดเลย ดูเวลาเปรียบเทียบเอาครับ ล่าสุดจะอยู่ล่างสุด

Close

ทำการเลือก Library ของเรา แล้วกดปุ่ม Close ครับ เพื่อพร้อมสำหรับ Release Version หากมีข้อผิดพลาดมันก็จะบอก ว่าเพราะอะไร เช่น ครั้งแรกผมลืม Publish Pub ID ไปที่ Server ทำให้ไม่มี Permission ในการ Release

แต่หากใครที่ Close เรียบร้อยไม่มีปัญหา ก็จะมีอีกปุ่มขึ้นมา คือ Release กดเลย เป็นอันเรียบร้อย Library เราลืมตาดูโลกแล้ว

ขั้นตอนสุดท้าย กลับไปคอมเม้น Issue ที่เราสร้างไว้ใน Step 1 ด้วยนะครับ อย่าลืม!

ตอนนี้ผมสามารถเรียกใช้ Library ของผม ได้เก๋ไก๋ แบบคนอื่นเค้าแล้ว แบบนี้

1
dependencies {
2
compile 'com.devahoy:shared:0.0.1'
3
}

แล้วก็ ถ้าหากใครอยากให้ค้นหาเจอใน Maven Search ก็รอทางทีมงานดำเนินการอีกราวๆ 3-4 ชม. Library เราก็ไปอยู่ใน Maven Search แล้ว ใน Gradle Please ก็อัพเดทเช่นเดียวกัน :)

สุดท้ายดู บันทึก Issue ของผม ได้ครับ ว่าทางทีมงานเค้าแนะนำ และบอกขั้นตอน จนเราอัพ Library ได้แหละครับ

หวังว่าคงมีประโยชน์นะครับ

Referenes

Authors
avatar

Chai Phonbopit

เป็น Web Dev ในบริษัทแห่งหนึ่ง ทำงานมา 10 ปีกว่าๆ ด้วยภาษาและเทคโนโลยี เช่น JavaScript, Node.js, React, Vue และปัจจุบันกำลังสนใจในเรื่องของ Blockchain และ Crypto กำลังหัดเรียนภาษา Rust

Related Posts