GitHub Actions: เขียน CI ที่ไม่พังทุกครั้งที่ merge
· 1 min read
CI ที่ช้าและพังบ่อยส่งผลเสียมากกว่าไม่มี CI เพราะทีมจะเริ่มชิน กับการกด re-run แล้วเชื่อผลลัพธ์น้อยลงเรื่อยๆ บทความนี้รวมสิ่งที่ทำให้ workflow บน GitHub Actions ทั้งเร็วขึ้นและน่าเชื่อถือขึ้น
แยก job ที่ไม่เกี่ยวกันให้รันขนาน
lint, typecheck และ test ไม่ต้องรอกัน แยกเป็นคนละ job แล้วปล่อยให้รันพร้อมกัน เวลารวมของ pipeline จะเท่ากับ job ที่ช้าที่สุด ไม่ใช่ผลรวมทั้งหมด
jobs:
check:
strategy:
fail-fast: false
matrix:
task: [lint, typecheck, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm ${{ matrix.task }}fail-fast: false สำคัญ เพราะเราอยากเห็นว่าพังกี่อย่างในรอบเดียว ไม่ใช่เห็นทีละอย่างแล้วต้อง push ซ้ำ
ยกเลิก run เก่าเมื่อมี push ใหม่
ถ้า push สาม commit ติดกัน ไม่มีเหตุผลที่จะรัน CI ของสอง commit แรกจนจบ
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: trueข้อควรระวัง: อย่าใช้ cancel-in-progress: true กับ workflow ที่ deploy จริง เพราะการยกเลิกกลางคันอาจทิ้ง state ค้างไว้
Cache ให้ถูกชั้น
cache ของ package manager ช่วยเรื่อง download แต่ไม่ช่วยเรื่องเวลา build ถ้าโปรเจกต์ใช้ Next.js หรือ TypeScript ให้ cache ผลลัพธ์ build ด้วย โดยใช้ key ที่ผูกกับ lockfile และ hash ของ source
- uses: actions/cache@v4
with:
path: .next/cache
key: next-${{ hashFiles('pnpm-lock.yaml') }}-${{ hashFiles('src/**') }}
restore-keys: next-${{ hashFiles('pnpm-lock.yaml') }}-restore-keys คือส่วนที่คนมักลืม ถ้าไม่มี cache จะ hit เฉพาะตอน source ไม่เปลี่ยนเลย ซึ่งแทบไม่เคยเกิดขึ้น
จำกัดสิทธิ์ของ token
ค่าเริ่มต้นของ GITHUB_TOKEN กว้างกว่าที่ workflow ส่วนใหญ่ต้องใช้ ประกาศสิทธิ์เท่าที่จำเป็นไว้บนสุดของไฟล์
permissions:
contents: readแล้วค่อยเพิ่มเป็นราย job สำหรับงานที่ต้องเขียนจริง เช่นการสร้าง release