จากบทความตอนที่แล้วในเรื่อง ขั้นตอนการ Publish Android Library ไปที่ jCenter ซึ่งเราจะต้อง Publish Library ไปยัง Bintray ก่อน แล้วถึงจะทำการ Link ไปยัง jCenter ได้นั้น นอกจากนี้ เรายังสามารถที่จะ Sync ไปยัง Maven Central ได้ด้วย
ซึ่งการ Sync ไปยัง Maven Central นั้น ต่างจากการนำขึ้นไป Maven Central เอง ถ้าพร้อมแล้ว มาดูกันต่อนะครับ
ถ้าใครยังไม่ได้อ่านบทความตอนแรก ไปอ่านก่อนนะ เพราะบทความนี้เป็นตอนต่อนะ ไม่งั้นเดี๋ยวจะงง
List of contents
Create Sonatype Issue
ก่อนจะนำ Library ขึ้นไปยัง Maven Central ได้นั้น เราจะต้องสมัคร Account ที่เว็บ Sonatype ก่อน แล้ว Login ให้เรียบร้อย หลังจากนั้นก็ทำการ Create Issue ใหม่ขึ้นมา แล้วกรอกแบบฟอร์มลงไป…

โดยจุดสำคัญอยู่ที่ Summary (1), Group Id (2), Project URL (3) และ SCM url (4) ซึ่ง Group Id นั้น ทาง Sonatype ค่อนข้างเข้มงวดในเรื่องของการตั้งชื่อ ซึ่งในตอนแรก ผมใช้ com.ethanf ซึ่งใช้ได้บน jCenter แต่ทาง Sonatype กลับไม่ยอมให้ผ่าน เลยจำเป็นต้องเปลี่ยนเป็น com.artit-k เพื่อให้ถูกต้องตามเงื่อนไข
สำหรับในช่อง Already Synced to Central (5) ให้กำหนดเป็น None ไปก่อน แล้วกดปุ่ม Create (6)
หลังจากนั้น รอทีมงานของ Sonatype มาตรวจ (อาจใช้เวลา 1-2 วัน) ถ้าผ่านเรียบร้อยดี เค้าจะตอบกลับมาเป็นข้อความแบบนี้ ซึ่งจะแนะนำว่าเราต้องทำอะไรเป็นขั้นตอนต่อไป
สำหรับ Issue ที่ผมใช้ Group Id ที่ไม่ถูกต้อง ทำยังไงทีมงานก็ไม่ยอมให้ผ่าน สามารถกดเข้าไปดูได้ที่นี่

แต่เราจะไม่ได้ทำขั้นตอนเหล่านี้หรอกนะ เพราะเราจะ Sync จาก Bintray ไปแทน (ง่ายกว่าเยอะ)
และไม่จำเป็นต้องรอผลการตรวจนะ ทำขั้นตอนต่อไปก่อนได้เลย
GPG Signing
สำหรับ Library ที่จะขึ้นไปยัง Maven Central ได้นั้น จำเป็นจะต้องทำการ Sign ด้วย ซึ่งการ Sign นั้น Bintray จะทำให้อัตโนมัติ เพียงแต่เราต้องเป็นคนสร้าง Key ขึ้นมา แล้วนำ Public Key ไปใส่ไว้ใน Bintray
การจะสร้าง Key บน Windows นั้น เราจะใช้โปรแกรม Gpg4win ให้ Download มา และติดตั้งให้เรียบร้อย หลังจากนั้นก็เปิด Command Prompt (cmd) หรือ DOS ขึ้นมา พิมพ์คำสั่งเพื่อสร้าง Key ดังนี้
1 |
gpg --gen-key |
ระบบก็จะถามเราเป็นข้อ ๆ ตามที่ผมเน้นบรรทัดไว้ โดยส่วนสำคัญอยู่ที่ประเภท Key, ชื่อ, Email และ Passphrase
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
Please select what kind of key you want: (1) RSA and RSA (default) (2) DSA and Elgamal (3) DSA (sign only) (4) RSA (sign only) Your selection? 1 RSA keys may be between 1024 and 4096 bits long. What keysize do you want? (2048) 4096 Requested keysize is 4096 bits 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 Key does not expire at all Is this correct? (y/N) y You need a user ID to identify your key; the software constructs the user ID from the Real Name, Comment and Email Address in this form: "Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>" Real name: Artit Kiuwilai Email address: first087@gmail.com Comment: You selected this USER-ID: "Artit Kiuwilai <first087@gmail.com>" Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O You need a Passphrase to protect your secret key. We need to generate a lot of random bytes. It is a good idea to perform some other action (type on the keyboard, move the mouse, utilize the disks) during the prime generation; this gives the random number generator a better chance to gain enough entropy. |
Passphrase เปรียบเสมือน Password ของ Key ที่เราสร้างเนี่ยแหล่ะ อย่าลืมจำไว้ด้วยนะ
หลังจากป้อน Passphrase แล้ว ระบบจะทำการสร้าง Key ซึ่งจะมีข้อความแนะนำว่าให้เรา กดปุ่มบน Keyboard ขยับ Mouse หรือทำอะไรก็ได้ที่ทำให้มีการใช้งาน Disk เพื่อจะได้สร้าง Key ได้ดีกว่า…
เมื่อสร้าง Key เรียบร้อยแล้ว พิมพ์คำสั่งดังนี้ เพื่อตรวจสอบดู Key ของเรา ว่าสร้างแล้ว
1 |
gpg --list-keys |
ก็จะเห็นประมาณนี้
1 2 3 |
pub 4096R/XXXXXXXX 2015-04-25 uid Artit Kiuwilai <first087@gmail.com> sub 4096R/YYYYYYYY 2015-04-25 |
แล้วทำการ Publish ไปยัง Key Server ด้วยคำสั่งดังนี้ (ไม่จำเป็นต้องทำก็ได้)
1 |
gpg --keyserver hkp://pgp.mit.edu --send-keys XXXXXXXX |
ตรง XXXXXXXX ให้เปลี่ยนเป็นค่าตาม Key ของเรา ที่ได้จากการสั่ง List Keys นะ
แล้วสั่งคำสั่งต่อไปนี้ เพื่อทำการดูในส่วนของ Public Key
1 |
gpg --export -a |
ก็จะเห็นประมาณนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
-----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1.4.13 (MingW32) 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 fw== =eqZD -----END PGP PUBLIC KEY BLOCK----- |
นำ Public Key ที่ได้มา ไปฝากไว้ที่ Bintray โดยเข้าไปที่ Edit Profile ที่เมนู GPG Signing (1) จะมีช่องให้ใส่ Public Key (2) แล้วเซฟให้เรียบร้อย

Update Gradle Script
ต่อไป เราจะต้องแก้ไข Gradle Script ซึ่งจากบทความตอนที่แล้วนั้น ก็คือการแก้ไขในส่วนของไฟล์ bintray.gradle ตามนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
apply plugin: 'com.jfrog.bintray' // This is the library version used when deploying the artifact version = libraryVersion task sourcesJar(type: Jar) { from android.sourceSets.main.java.srcDirs classifier = 'sources' } task javadoc(type: Javadoc) { source = android.sourceSets.main.java.srcDirs classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) } task javadocJar(type: Jar, dependsOn: javadoc) { classifier = 'javadoc' from javadoc.destinationDir } artifacts { archives javadocJar archives sourcesJar } // Bintray Properties properties = new Properties() properties.load(project.rootProject.file('local.properties').newDataInputStream()) bintray { user = properties.getProperty("bintray.user") key = properties.getProperty("bintray.apikey") configurations = ['archives'] pkg { repo = bintrayRepo name = bintrayName desc = libraryDescription websiteUrl = siteUrl vcsUrl = gitUrl licenses = licenseLabel publish = true publicDownloadNumbers = true version { desc = libraryDescription gpg { sign = true //Determines whether to GPG sign the files. The default is false passphrase = properties.getProperty("bintray.gpg.password") //Optional. The passphrase for GPG signing' } mavenCentralSync { sync = false //Optional (true by default). Determines whether to sync the version to Maven Central. user = properties.getProperty("bintray.oss.user") //OSS user token password = properties.getProperty("bintray.oss.password") //OSS user password close = '1' //Optional property. By default the staging repository is closed and artifacts are released to Maven Central. You can optionally turn this behaviour off (by puting 0 as value) and release the version manually. } } } } |
ในส่วนของ gpg จะเกี่ยวกับการ Sign Library ของเรา โดยให้กำหนดค่า sign เป็น true เพื่อให้ Bintray นำ Key ที่เราฝากไว้ ทำการ Sign ให้เราเมื่อเรา Publish ขึ้นไป โดยจะต้องกำหนด passphrase ในบรรทัดที่ 47 ด้วย ซึ่งใน Script ข้างบน จะไปอ่านค่า Passphrase จากตัวแปร bintray.gpg.password ในไฟล์ local.properties (เดี๋ยวจะพูดถึงอีกที)
และในส่วนของ mavenCentralSync จะใช้บอก Bintray ว่าจะให้ Sync ไปยัง Maven Central เลยหรือไม่ ถ้าจะให้ Sync เลยก็ให้กำหนดค่า sync เป็น true โดยจะต้องกำหนด user (token), password (token) และ close ด้วย ในตัวอย่างด้านบน ผมจะยังไม่ sync ก่อน แต่เดี๋ยวจะไป sync บนเว็บ Bintray แทน
ซึ่งค่า user และ password ควรที่จะเป็นความลับ ใน Script ข้างบน จึงกำหนดให้ไปอ่านค่าจากไฟล์ local.properties เช่นเดียวกับ Passphrase
สำหรับไฟล์ local.properties ก็เพิ่มตัวแปรทั้ง 3 ลงไป พร้อมกำหนดค่า ดังนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
## This file is automatically generated by Android Studio. # Do not modify this file -- YOUR CHANGES WILL BE ERASED! # # This file must *NOT* be checked into Version Control Systems, # as it contains information specific to your local configuration. # # Location of the SDK. This is only used by Gradle. # For customization when using a Version Control System, please read the # header note. #Thu Apr 23 22:36:00 ICT 2015 sdk.dir=D\:\\adt-bundle\\sdk bintray.user=first087 bintray.apikey=0000000000000000000000000000000000000000 bintray.gpg.password=00000000 bintray.oss.user=XXXXXXXX bintray.oss.password=0000000000000000000000000000000000000000 |
โดยค่า bintray.oss.user และ bintray.oss.password นั้น ให้ Login เข้าไปยัง Sonatype OSS แล้วคลิกที่ User เรา เลือก Profile (1) > เลือก User Token (2) แล้วคลิกปุ่ม Access User Token (3) ระบบจะให้ใส่ Password ของเราเพื่อยืนยันอีกครั้ง

ก็จะได้ Token มาแล้ว เอาไปใส่ local.properties ได้เลย

Sync Bintray to Maven Central
จากหัวข้อที่แล้ว ถ้าเรากำหนด mavenCentralSync ให้ทำการ Sync เลย ก็จบแค่นี้ เปิด Android Studio ขึ้นมา Sync Gradle แล้ว Build Project ตามด้วย Publish to Bintray ก็เสร็จแล้วล่ะ
แต่… จากตัวอย่างด้านบน ผมยังไม่ให้ Sync เพราะฉะนั้น เมื่อ Publish to Bintray แล้ว เราจะต้องเข้าไป Sync บน Bintray ด้วยตัวเอง
แต่ก่อนจะ Sync ลองเข้าไปที่เมนู Files ก่อน จะเห็นว่าไฟล์ที่เรา Publish ขึ้นมานั้น จะมีไฟล์ .asc เพิ่มขึ้นมาด้วย ซึ่งได้จากการที่ Bintray ทำการ Sign ให้เรา ด้วย Key ที่เราฝากไว้ หลังจาก Publish ขึ้นมาแล้วนั่นเอง

ตอนนี้ก็พร้อมที่จะ Sync ไปยัง Maven Central แล้วล่ะ ก็เข้าไปที่เมนู Maven Central แล้ว นำ Token ที่ได้จากหัวข้อที่แล้ว มาใส่ (1), (2) แล้วทำการ Sync (3) ได้เลย

รอสักครู่ ถ้าเรียบร้อยดี จะเห็นในส่วนของ Sync Status ดังนี้ จะเห็นว่า Sync และ Close เรียบร้อย

หลังจากนั้น ให้ตรวจสอบที่ Staging Repositories บน Sonatype OSS ดูอันล่าสุดในตาราง จะเห็นว่า Repository ของเรานั้น มี Status เป็น Released แล้ว

ยังไม่จบ…
เราจำเป็นต้องกลับไปที่ Sonatype Issue ที่สร้างไว้ในขั้นตอนแรก แล้วไปโพสบอกเค้าด้วย ว่าเรา Release เรียบร้อยแล้ว หลังจากนั้นก็รอทีมงานมา Activate ให้ โดยจะแจ้งให้ทราบว่า จะ Publish ไปอยู่บน Central ภายใน 10 นาที และสามารถใช้ Maven Central search ค้นหาเจอ หลังจากนี้อีกประมาณ 2 ชั่วโมง
ลอง Search บน Android Studio ดู ก็เจอแล้วล่ะ

Summary
สรุปกันซะหน่อย เป็นขั้นตอนแบบย่อ ๆ เพื่อที่จะนำ Library ของเรา ไปโลดแล่นอยู่บน jCenter และ Maven Central ก็คือ…
สำหรับการนำ Android Library ขึ้นไปที่ Maven Central ก็จบเพียงเท่านี้…
แต่ก่อนจบ… พอดีระหว่างเขียนบทความ ไปเจอ Blog ของคนไทย ที่เขียนเรื่องเดียวกันไว้มากมายหลายสูตร เลยเอามาแปะไว้ด้วย เผื่อว่าผู้อ่านท่านไหน ชอบสูตรไหนมากกว่า ก็เลือกเอาตามใจชอบได้เลยครับ