Home Guide Continuous Deployment อัพโหลด Flutter Web App ไปที่ Firebase Hosting อัตโนมัติ

Continuous Deployment อัพโหลด Flutter Web App ไปที่ Firebase Hosting อัตโนมัติ

by khomkrit

รอบที่แล้วเคยเขียนบทความอธิบายเกี่ยวกับ GitHub Action คืออะไร รอบนี้จะมาเขียนตัวอย่างการนำ GitHub Action ไปใช้ง่ายๆ ด้วยการทำ Continuous Deployment โดยการกำหนดว่า ทุกครั้งที่เรา push code ไปที่ GitHub แล้วให้มัน build งานของเราแล้วกำอัพโหลดไปวางไว้ที่ Firebase Hosting พร้อมให้คนอื่นๆ เข้ามาดูผลงานของเราได้ตลอดเวลาที่เราทำงาน

สำหรับใครที่คุ้นเคยกับ Firebase Hosting, Flutter Web Development และ GitHub Action แล้ว ให้

  1. copy โค้ดในไฟล์ main.yml ด้านล่าง แล้วนำไปวางไว้ที่ .github/workflows/main.yml ของ repository เรา
  2. สร้าง Firebase CLI Token ด้วยคำสั่ง firebase login:ci
  3. นำ 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 อย่างต่อไปนี้ ถ้ายังไม่มี ก็ต้องติดตั้งให้พร้อมใช้งานก่อน

  1. Flutter environment ที่ enable beta channel แล้ว
  2. 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

ทีนี้เราต้องการให้ขั้นตอนต่างๆ ที่เราทำมาตั้งแต่ต้น ได้แก่

  1. Enable flutter channel beta
  2. Build flutter project
  3. 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 ที่เราตั้งค่าไว้ ทำงานสำเร็จหรือไม่

GitHub Action result

หลังจากนี้ทุกครั้งที่เรา push โค้ดไปที่ branch master เว็บของเราก็ถูก deploy ไปบน Firebase Hosting ให้โดยอัตโนมัติแล้ว

สรุป

  1. ใช้ subosito/flutter-action เพื่อ setup flutter environment ใน GitHub Runner
  2. ใช้ w9jds/firebase-action ในการรัน firebase cli เพื่อ deploy งานขึ้น Firebase Hosting

เนื้อหาในไฟล์ main.yml นั้นเป็นเพียงแค่ตัวอย่างเร็วๆ เพื่อให้เห็นภาพเท่านั้น ซึ่งเวลาทำจริงๆ อย่างน้อยก็ไม่ควรทำตามในไฟล์ตัวอย่างนี้เป๊ะๆ เนื่องจากเหตุผลทางด้านความปลอดภัย เช่น

action ที่เรานำมาใช้ตอนนี้เรา pin ไปที่ @master ซึ่งสิ่งที่เราควรทำจริงๆ ก็คือการ pin ไปที่ commit SHA หรือ อย่างน้อยก็ควร pin ไปที่แท็กแทนที่จะเป็นชื่อ branch เป็นต้น

อ่านต่อในรายละเอียดเกี่ยวกับความปลอดภัยที่ Using third-party actions

You may also like