รอบที่แล้วเคยเขียนบทความอธิบายเกี่ยวกับ GitHub Action คืออะไร รอบนี้จะมาเขียนตัวอย่างการนำ GitHub Action ไปใช้ง่ายๆ ด้วยการทำ Continuous Deployment โดยการกำหนดว่า ทุกครั้งที่เรา push code ไปที่ GitHub แล้วให้มัน build งานของเราแล้วกำอัพโหลดไปวางไว้ที่ Firebase Hosting พร้อมให้คนอื่นๆ เข้ามาดูผลงานของเราได้ตลอดเวลาที่เราทำงาน
สำหรับใครที่คุ้นเคยกับ Firebase Hosting, Flutter Web Development และ GitHub Action แล้ว ให้
- copy โค้ดในไฟล์
main.yml
ด้านล่าง แล้วนำไปวางไว้ที่.github/workflows/main.yml
ของ repository เรา - สร้าง Firebase CLI Token ด้วยคำสั่ง
firebase login:ci
- นำ Firebase Token ที่ได้ไปเก็บไว้ใน secret ชื่อ
FIREBASE_TOKEN
ใน GitHub repository เดียวกัน
เท่านี้ก็พร้อมใช้งานแล้ว
ต่อไปนี้คือเนื้อหาสำหรับคนที่อยากลองทำ Continuous Deployment โดยใช้ GitHub Action ในการ deploy เว็บไปเก็บไว้ที่ Firebase Hosting ทุกๆ ครั้งที่เรา push code ไปเก็บไว้ที่ master branch ของเราบน GitHub
ติดตั้งเครื่องมือทั้ง 2
โดยทั่วไปแล้วเวลาเราทำเว็บแอปด้วย Flutter และ deploy ขึ้น Firebase Hosting เราก็ต้องมีเครื่องมือ 2 อย่างต่อไปนี้ ถ้ายังไม่มี ก็ต้องติดตั้งให้พร้อมใช้งานก่อน
- Flutter environment ที่ enable beta channel แล้ว
- Firebase CLI
เริ่มจากติดตั้ง Flutter เสร็จแล้ว ก็ทำให้เราสามารถใช้ Flutter build เป็นเว็บได้ โดยการ enable web support ดังนี้
flutter channel beta
flutter upgrade
flutter config --enable-web
และติดตั้ง Firebase CLI ผ่าน npm ดังนี้
sudo npm install -g firebase-tools
ลำดับการพัฒนาแอปโดยทั่วไป
พอมีเครื่องมือ 2 ตัวนี้แล้ว เราก็พัฒนาแอปไปตามปกติ โดยเราจะเริ่มจากการสร้างโปรเจ็ค และลองรันดูก่อนทันที เพื่อเช็คดูว่าทุกอย่างนั้นยังราบรื่น ไปต่อได้
flutter create hello && cd hello && flutter run -d chrome
ถ้าเราเห็น chrome รันแอป Flutter ขึ้นมาแปลว่าทุกอย่างผ่านไปได้ด้วยดี ถ้าไม่ขึ้นให้ลองรันคำสั่ง flutter devices
ว่าเห็น chrome หรือไม่ ต่อไปก็ init firebase โดยการรันคำสั่ง init firebase
ในโปรเจ็คของเรา ถ้ารันแล้วพบข้อความเตือนว่าเรายังไม่ได้ login ก็ให้ login ก่อนด้วยคำสั่ง firebase login
firebase init
ในขั้นตอนการ init firebase project เราต้องเลือกเป็น Hosting และเลือกเป้าหมายว่าเราจะ init project นี้กับโปรเจ็คไหนใน Firebase ของเรา จากนั้นทำตามไปเรื่อยๆ จนกว่าจะเสร็จขั้นตอนการ init
khomkrit@M1 hello % firebase init
######## #### ######## ######## ######## ### ###### ########
## ## ## ## ## ## ## ## ## ## ##
###### ## ######## ###### ######## ######### ###### ######
## ## ## ## ## ## ## ## ## ## ##
## #### ## ## ######## ######## ## ## ###### ########
You're about to initialize a Firebase project in this directory:
? Which Firebase CLI features do you want to set up for this folder? Press Space to select features, then Enter to confirm your choices.
◯ Database: Configure Firebase Realtime Database and deploy rules
◯ Firestore: Deploy rules and create indexes for Firestore
◯ Functions: Configure and deploy Cloud Functions
❯◉ Hosting: Configure and deploy Firebase Hosting sites
◯ Storage: Deploy Cloud Storage security rules
◯ Emulators: Set up local emulators for Firebase features
◯ Remote Config: Get, deploy, and rollback configurations for Remote Config
✔ Firebase initialization complete!
เมื่อเราทำแอปเสร็จถึงจุดหนึ่ง แล้วเราต้องการ deploy ไปไว้ที่ Firebase Hosting เราก็จะ build web และ deploy ขึ้น Firebase ตามปกติ ดังนี้
flutter build web && cp -R build/web/* public && firebase deploy
แล้วเราก็รันคำสั่งนี้วนไปเรื่อยๆ ทุกครั้งที่ต้องการ deploy ไปที่ Firebase Hosting
Deploy โดยใช้ GitHub Action
ทีนี้เราต้องการให้ขั้นตอนต่างๆ ที่เราทำมาตั้งแต่ต้น ได้แก่
- Enable flutter channel beta
- Build flutter project
- Deploy ไปที่ Firebase Hosting
ทำงานทุกครั้งที่เรา push code ไปที่ branch master บน GitHub โดยอัตโนมัติ
ถ้าเราดูจากขั้นตอนที่เราต้องทำ เราก็ต้องการแค่ action ภายนอกเพิ่มอีก 2 ตัวมาใช้ใน workflow ของเรา ก็คือ action เกี่ยวกับ flutter environment และ action เกียวกับ Firebase นั่นเอง
เราจะใช้ action สำหรับจัดการ flutter ตัวนี้ flutter-action และ action สำหรับ firebase ตัวนี้ firebase-action ในการอัพโหลดงานของเราขึ้น Firebase Hosting
เริ่มจากไปสร้างไฟล์ไว้ที่ .github/workflows/main.yml
เพื่อนิยาม workflow ของเราก่อน และ copy โค้ดด้านล่างนี้ไปใช้ได้เลย
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: subosito/flutter-action@v1
with:
channel: 'beta'
- run: flutter config --enable-web
- run: flutter pub get
- run: flutter build web --release
- run: cp -R build/web/* public/
- uses: w9jds/firebase-action@master
with:
args: deploy --only hosting
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
workflow ของเรามี 1 job ชื่อว่า build
และมีขั้นตอนต่างๆ บรรจุอยู่ในนั้น ซึ่งมีเพียง 3 ขั้นตอนเท่านั้นที่มีการเรียกใช้ action (ด้วยคำสั่ง uses
) และที่เหลือก็เป็นการรันคำสั่งตามปกติ
เราจะเห็นว่าในไฟล์ main.yml
มีการเรียกใช้ Secret ตัวหนึ่งชื่อ FIREBASE_TOKEN
ซึ่งเราสามารถหา token ตัวนี้ได้จากการรันคำสั่ง
firebase login:ci
จากนั้นให้ copy token ที่ได้ไปกำหนดค่าไว้ที่ Secret ใน Reposiroty ของโปรเจ็คเราใน GitHub และตั้งชื่อ secret ตัวนี้ว่า FIREBASE_TOKEN
เมื่อเรา push code ขึ้นไปที่ GitHub ให้เราตามเข้าไปดูที่เมนู Actions ใน Repository ของเรา เพื่อดูว่า action ที่เราตั้งค่าไว้ ทำงานสำเร็จหรือไม่
หลังจากนี้ทุกครั้งที่เรา push โค้ดไปที่ branch master เว็บของเราก็ถูก deploy ไปบน Firebase Hosting ให้โดยอัตโนมัติแล้ว
สรุป
- ใช้ subosito/flutter-action เพื่อ setup flutter environment ใน GitHub Runner
- ใช้ w9jds/firebase-action ในการรัน firebase cli เพื่อ deploy งานขึ้น Firebase Hosting
เนื้อหาในไฟล์ main.yml
นั้นเป็นเพียงแค่ตัวอย่างเร็วๆ เพื่อให้เห็นภาพเท่านั้น ซึ่งเวลาทำจริงๆ อย่างน้อยก็ไม่ควรทำตามในไฟล์ตัวอย่างนี้เป๊ะๆ เนื่องจากเหตุผลทางด้านความปลอดภัย เช่น
action ที่เรานำมาใช้ตอนนี้เรา pin ไปที่ @master
ซึ่งสิ่งที่เราควรทำจริงๆ ก็คือการ pin ไปที่ commit SHA หรือ อย่างน้อยก็ควร pin ไปที่แท็กแทนที่จะเป็นชื่อ branch เป็นต้น
อ่านต่อในรายละเอียดเกี่ยวกับความปลอดภัยที่ Using third-party actions