ทำ CI/CD ใน Android ด้วย Gitlab CI
การเขียนแอปพลิเคชัน ขั้นตอนที่เราต้องทำกันอยู่เสมอคือการ Build, Test และ Deploy เบื่อมั้ยที่ต้องมานั่งกดทีละขั้นตอนทุกครั้งที่แอปมีการอัปเดต? มาใช้ CI/CD ทำเรื่องพวกนี้แทนกันดีกว่า..
ในบทความนี้จะพูดถึงว่า CI/CD คืออะไร? ทำงานร่วมกับ Android ยังไง? แล้วจะสั่งให้มัน Build และ Deploy แอพเองทุกครั้งที่เรา push code ขึ้น gitlab ต้องทำยังไง?
เริ่มต้นกันที่ CI/CD คือ?
CI/CD หรือ Continuous Integration/ Continuous Deployment เป็นเครื่องมือที่ช่วยให้การทำงานต่างๆทำงานเองอัตโนมัติ เช่นในกรณีนี้ เราเขียนแอพ เราอยากจะให้มัน build แล้วก็ deploy เองเลยเวลาที่แอปเรามีการอัปเดต CI ก็จะอ่าน script คำสั่งที่เราเขียนขึ้นมาและทำงานให้เองอัตโนมัติ
Gitlab CI
จริงๆแล้ว CI มี server ให้เลือกใช้อยู่อีกหลายตัวเช่น Jenkins, Travis-CI, Buildbot, Drone และอีกมากมาย แต่เหตุผลที่เลือก Gitlab CI เนื่องจากใช้ Gitlab เป็น version control อยู่แล้ว เลยเลือกใช้ CI ของ Gitlab ซะเลย :)
Flow การทำงาน
ในบทความนี้จะขอข้ามขั้นตอนการทำ Test ไป จะใช้ CI เพื่อทำเพียง automate build และ deploy app เท่านั้น โดยมี Flow การทำงานเบื้องต้นดังรูปภาพด้านล่าง
Gitlab CI จะทำงานโดยการอ่าน script หรือคำสั่งจาก ไฟล์ .gitlab-ci.yml ซึ่งเราจะสร้างไฟล์นี้ไว้ที่โฟลเดอร์ของโปรเจ็ค
ไฟล์ .gitlab-ci.yml
ไฟล์นี้เป็นไฟล์สำคัญ สำหรับการทำ CI ด้วย Gitlab เพราะ Gitlab CI จะเริ่มต้นทำงานโดยอ่านคำสั่งผ่านไฟล์นี้ ซึ่ง Gitlab จะอ่านและทำงานตามคำสั่งในไฟล์นี้เองโดยที่เราไม่ต้องใส่คำสั่งใดๆเพิ่ม แค่ใส่ไฟล์นี้เอาไว้ที่โฟลเดอร์ของโปรเจ็คเท่านั้น
ขั้นตอนหลักๆ จากไฟล์ .gitlab-ci.yml ข้างต้น ประกอบด้วย
- Define image
- Define variables
- Prepare environment
- Define stages
- Build app
- Deploy app
ทำความเข้าใจ(บาง)ขั้นตอนในไฟล์ .gitlab-ci.yml
Define image
image: openjdk:8-jdk
เรา define image เพื่อเป็นการบอก docker ว่าจะใช้ docker image ตัวไหนในการทำงานครั้งนี้
Define stages
stages:
- build
- deploy
stages แต่ละ stages เปรียบเหมือนการจัดกลุ่มให้แต่ละ jobs ซึ่ง jobs ใน stage เดียวกันจะทำงานเป็น parallel โดยแต่ละ jobs ใน stages นั่นๆต้องทำงานเสร็จก่อน stages ถัดไปจะเริ่มทำงาน
Build App + Deploy App
ก่อนจะไปลงรายละเอียดขั้นตอนในการ build กับการ deploy ด้านล่างคือ flow การทำงานของทั้งสองขั้นตอน
Build App
build:
stage: build
script:
# Creating variable
- export APP_VERSION=V.${CI_BUILD_TAG}
- export FILE_NAME=app-$APP_VERSION.apk
# Build apk
- ./gradlew assembleRelease
# Change file name
- mv app/build/outputs/apk/release/app-release.apk app/build/outputs/apk/release/$FILE_NAME
artifacts:
paths:
- app/build/outputs/
only:
- tags
build job เริ่มต้นด้วยการสร้างตัวแปรเพื่อมาเก็บเลข version ซึ่งเอาค่ามากจากชื่อ tags ตอนเรา push ซึ่งคือ ${CI_BUILD_TAG} > คำสั่ง assembleRelease คือให้ build release version ของ app > เปลี่ยนชื่อไฟล์เป็นเลข version > output ที่ได้จากการ build จะสามารถดาวน์โหลดได้ผ่าน artifacts field ตาม path ที่กำหนดเอาไว้ โดย job นี้จะทำงานก็เมื่อเรา push tags ใน Gitlab เท่านั้น
Deploy App
deploy:
stage: deploy
script:
# Installing node and firebase
- curl -sL https://deb.nodesource.com/setup_9.x | bash
- apt-get install -y nodejs
- npm install -g --unsafe firebase-tools
- firebase init database
- firebase use --token $FIREBASE_DEPLOY_KEY production - export APP_VERSION=V.${CI_BUILD_TAG}
- export FILE_NAME=app-$APP_VERSION.apk
- echo $FILE_NAME# Uploading apk to S3
- ./gradlew uploadApk
# Updating value on firebase realtime database
- echo "\"$APK_LINK/production/release/$FILE_NAME\"" | firebase database:set /config/app/test/stable/url --confirm
- echo "\"$APP_VERSION\"" | firebase database:set /config/app/test/stable/version --confirm
dependencies:
- build
only:
- tags
ใน stage deploy นี้ขึ้นอยู่กับแต่ละคนว่าจะเอา output ของ การ build ไปทำอะไร เช่นอัพโหลดขึ้น Google Play เป็นต้น แต่สำหรับในบทความนี้ ตาม code ข้างต้น จะนำ output ที่ได้อัพโหลดขึ้น server ของ S3
จะเริ่มต้นด้วยการติดตั้ง Node และ Firebase > set ค่าตัวแปร > Upload APK ขึ้น S3 server(สามารถดูการเรียกใช้ plugin uploadApk ได้ที่ https://github.com/rambabusaravanan/gradle-android-s3 ) > update Url และ version ล่าสุดของ APK ไฟล์ไว้ที่ Realtime database ของ Firebase เพื่อนำไปใช้งานต่อไป