Flutter — Automate Semantic versioning.
สวัสดีคับบ :) ห่างหายจากการเขียนบทความไปนานมากๆ
แนะนำตัวกันหน่อย ผม ต้นน้ำ ปัจจุบันเป็นทุกอย่างให้เธอแล้ว Developer คับ หยอกๆ ฮาๆๆ Junior Mobile Developer ที่ Q-Chang นะคร้าบ 🤣
วันนี้ผมอยากจะมาแชร์ประสบการณ์ การยกเครื่อง ระบบ Versioning ของ Flutter ของพวกเรา ให้เป็น Automate หรือ การกำหนด Version แบบอัตโนมัติผ่านการทำงานของ CI นั้นเอง ถ้าพร้อมแล้ว มาลุยกันเลยคร้าบ!
# Flutter Versioning
เริ่มตันกันที่ เรามาทำความรู้จักกับ Versioning ของ Flutter กันก่อนดีกว่า. Flutter เขามีวิธีการกำหนด Version ในรูปแบบ “Semantics Versioning” หรือ “SemVer” นั้นเอง โดยเราจะแบ่งความหมายของแต่ละตำแหน่งออกเป็นดังนี้ครับ
# Automate Versioning
ต่อมา เรามาพูดถึงข้อดีกันก่อน ว่า automate versioning มันดียังงัย
- ลดภาระของคนที่ต้องเข้ามา involve ในการจัดการ Version
- ลดความผิดพลาดที่เกิดจากคน (เช่น การ Bump version ผิดหมวดหมู่)
- เราไม่ต้องจำว่า Version ล่าสุดคืออะไร เพราะระบบจะจัดการให้หมด
- Decoupling version ที่ติดอยู่ใน Code ในแต่ละ Commit ออกจากกัน แล้วเปลี่ยนไป Track ใน Version Control หรือ Git แทนนั่นเอง (คหสต. อันนี้คือดีมากๆ เพราะ การแก้ Version ในโค๊ดนั้นทำให้เกิดปัญหาของ Commit และ Commit ที่ไม่จำเป็นอยู่บ่อยครั้งมากๆครับ)
แจ่มแมวมาก 👍
คราวนี้ ต้องบอกว่า การจะใช้ automate versioning ได้นั้น จะต้องเกิดจาก Git Flow ที่ดี และ Commit Message ที่สามารถบ่งบอกว่า สิ่งที่เราทำอยู่นั้นคืออะไร ถือว่าสิ่งเหล่านี้เป็น Checklist ก่อนเริ่ม Implement กันก็ได้นะครับ :)
- Git Flow
ผมจะยกตัวอย่างที่ Q-Chang นะครับ เราจะมีการแบ่ง Track การ Release ออกเป็น 3 Tracks ประกอบด้วย
ดังนั้น ใน Git ของพวกเรา จะมี 3 Branches นี้เป็น Branch หลักในการทำงาน
- Commit Message
ส่วนตัวผมใช้ Conventional Commit Message ซึ่งเป็นการระบุ Behaviour ของ Commit Message ด้วยข้อความสั้นๆ ที่อยู่หน้าสุดของ Commit Message เช่น
feat: Add remove user feature.
fix: Update the styling of submit button in Job Submisstion page.
chore: Update the project's README.
BREAKING CHANGE: Update app's identity and api
ซึ่งเป็นสิ่งที่จำเป็นมากๆสำหรับการทำ Automate Versioning เพราะ การที่ระบบจะบอกได้ว่าเขาควร Bump Version ไหนขึ้นนั้น จำเป็นต้องรู้ behavior ของ Commit(s) ที่เราทำไปครับ
ต่อมา เราจะมาแบ่งประเภทของ Commit Message ในการ Bump Version กันครับ ซึ่งตรงนี้ ขึ้นอยู่กับเราเลยนะครับ ว่าจะใช้ Key อะไรมาเป็นตัวจับ
- หากใน Commit เรามีการใช้ Key
BREAKING CHANGE
เราจะทำการ Bump Major Version ขึ้น. - หากเรามีการใช้ Key
feat
เราจะทำการ Bump Minor Version ขึ้น - และกรณีอื่นๆ ที่ไม่ได้อยู่ใน 2 cases ด้านบน เราจะ Bump Patch Version ขึ้นแทนครับ
- ทุกๆครั้งที่มีการ Bump Version ขึ้น เราจะ Bump Build Number ขึ้น 1 เสมอ
# Implementing
สรุป Steps คร่าวๆของการทำกันก่อนเริ่มตัน (ทบทวนตัวเอง 555)
- เอา Version ออกจากโค๊ด และใส่ใน Git Tag แทน
- Config CI ให้ระบุ Version ของ Commit ที่เราทำไป และ Tag Commit นั้นด้วย Version ใหม่
- ใน Process การ Release ให้ดึง Tag ล่าสุดของ Branch ที่ CI ทำงาน และมา Render ลงไปที่
pubspec.yaml
1. เอา Version ออกจากโค๊ด และใส่ใน Git Tag แทน
อันดับแรก เราจะเริ่มต้นด้วยการลบ Version ออกจาก Code ของเราก่อน ด้วยการไปที่ pubspec.yaml
และแก้ Version ให้เป็น 0.0.0+0 และ เราจะทำการ Tag Commit ปัจจุบันของเราด้วย Version นั้นแทน
$ git tag "2.0.0+10"
name: app
description: My Application
version: 0.0.0+0
จากนั้น เราจะไป Config CI ของเรากัน โดยที่ Q-Chang จะใช้ CircleCI เป็นหลัก ดังนั้น ในบทความนี้ ผมจะยกตัวอย่าง Config ของ CircleCI มาแทนนะครับ
2. Config CI
CI จะต้องบอกได้ว่า Version ต่อไปที่มันทำงานคืออะไร
เนื่องจากสิ่งนี้ มีหลายขึ้นตอนที่ต้องทำ ผมจึงตัดสินใจเขียนเป็น Go Application ขึ้นมา 1 ตัวเพื่อดึง Git Commit ขึ้นมาดู และเป็นตัว Bump Version ให้กับเรานั้นเอง ชื่อว่า flutter-semver
Note: PR is always welcome :)
หลักการทำงานคร่าวๆ
- มันจะดึง Tag ล่าสุดขึ้นมาดู และทำการ Parse เข้าสู่ SemVer Syntax
- ดึง Commit Message จาก Tag ที่มันเจอล่าสุด มาจนถึง HEAD หรือ Commit ล่าสุดใน Branch ที่ทำงานอยู่
- ตรวจสอบ Commit Message ตาม Condition ที่ระบุไปด้านบน
- ทำการ Bump Version ตาม Condition ที่ตั้งไว้
- Set Environment & สร้าง File ที่เก็บ Result ของการ Bump Semver ขึ้นมา.
ทางผมได้ Build ตัว flutter-semver ไว้ให้เบื้องต้น 4 Builds. Support 2 os และ 2 Architectures นะครับ
- darwin-amd64
- darwin-arm64
- linux-amd64
- linux-arm64
ซึ่งด้านล่างนี้เป็นตัวอย่าง Commands ที่ใช้ใน CircleCI ในการ Tag Commit ด้วย flutter-semver นะครับ
commands:
setup:
description: Setup the flutter-semver
steps:
- run: curl -fsSLo flutter-semver https://github.com/sirateek/flutter-semver/releases/download/v0.4.6/flutter-semver-darwin-amd64
- run: chmod 777 flutter-semver
run-flutter-semver:
description: Start Semver
steps:
- run: eval $(DEBUG=true ./flutter-semver)
parse-semver-result:
description: Read semver-result file and export to env var
steps:
- run: |
while read p; do
echo "export $p" >> "$BASH_ENV"
done < semver-result.txt
- run: source "$BASH_ENV"
tag-commit:
description: Tag a commit
steps:
- run: git tag $SEMVER_FULL
- run: git push origin $SEMVER_FULL
สังเกตว่า จากในโค๊ด CI ด้านบนนี้ จะมี 3 ส่วน คือ
- Download Binary ของ flutter-semver
- Run flutter-semver
- Parse Result & tag commit ด้วยผลลัพท์จาก flutter-semver
ซึ่งสามารถนำไปประยุคใช้กับ CI ของแต่ละคนได้เลยนะครับ
3. Render Version
ขั้นตอนสุดท้าย เราก็จะต้อง render version ของเราลงไปใน pubspec.yaml
ซึ่ง Version เราสามารถดึงได้จาก Latest Tag ในแต่ละ Branch นะครับ
render-version-and-release-log:
description: For rendering the latest tag version and commit message
steps:
- run:
name: Fetch from git
command: git fetch
- run:
name: Copy pubspec template
command: mv pubspec.yaml pubspec-template.yaml
- run:
name: Render the version.
command: sed "s/0.0.0+0/$(git describe --abbrev=0 --tags)/g" pubspec-template.yaml > pubspec.yaml
Note อีกเช่นเคย อันนี้เป็นตัวอย่าง command ใน CircleCI นะครับ :)
จาก Config ด้านบนจะแบ่งออกเป็น
- Fetch ข้อมูลล่าสุดจาก Git
- แยก pubspec.yaml ออกมาเป็น Template (เผื่อเรามีอะไรต้องใช้ Original)
- ทำการ Find & Replace ด้วย
sec
command ซึ่งเป็น Command ของทางฝั่ง linux นะคับ
$ sed "s/0.0.0+0/$(git describe --abbrev=0 --tags)/g" pubspec-template.yaml > pubspec.yaml
โดยคำสั่งที่ดึง Tag ล่าสุดของ Branch นั้นๆใน Git คือ
$ git describe --abbrev=0 --tags
ความหมายโดยรวมของคำสั่ง sed ด้านบนคือ
หาจุดที่มีข้อความ 0.0.0+0
และ replace ด้วยผลลัพท์จากการหา tag ล่าสุดจาก git ในไฟล์ชื่อ pubspec-template.yaml
และทำการ Save ผลลัพท์ไปที่ไฟล์ชื่อ pubspec.yaml
จบปิ๊งๆๆๆ 🌟✨
จบไปแล้วนะครับ สำหรับการแชร์ประสบการณ์การแปลงระบบ Version เป็นระบบ Automate ที่ Q-Chang ของผม
จริงๆ การทำ Automate Versioning ที่ผมเสนอไปนั้น เป็นเพียงแค่ 1 วิธี ยังมีอีกหลายวิธีที่สามารถทำได้ และ แต่ละวิธีก็มีข้อดีข้อเสีย ความเหมาะสมกับแต่ละที่ก็ไม่เหมือนกัน ดันนั้นผมอยากให้ทุกคนนึกถึงสิ่งที่เป็น Condition, Git flow ของเราออกมาก่อน แล้วค่อยมาออกแบบการทำ Automation เหล่านี้ เพื่อให้เหมาะกับการทำงานของเรามากที่สุดครับ มิเช่นนั้น จากที่ระบบจะมาช่วยเรา อาจจะกลายเป็นเราต้องไปช่วยระบบทำงานก็เป็นไปได้ 😂😂
สุดท้ายนี้ หากใครมีคำแนะนำตรงส่วนไหน สามารถแนะนำเข้ามาได้นะครับ ผมยินดีรับทุกความคิดเห็น/คำถาม
จนกว่าจะพบกันใหม่ในบทความถัดไปครับ :)
Tonnam
Follow me on