บทที่ 7 — CI: ผู้ตรวจงานอัตโนมัติที่ไม่เคยหลับ
CI คืออะไร
CI (Continuous Integration) คือระบบที่รันคำสั่งตรวจโค้ดให้อัตโนมัติทุกครั้งที่มีคนเปิด PR ถ้าตรวจไม่ผ่าน PR จะขึ้นสีแดงและ merge ไม่ควรเกิดขึ้น
คิดง่าย ๆ ว่ามันคือ "เพื่อนร่วมทีมที่ขยันมาก" ซึ่งจะรันคำสั่งชุดเดิมทุกครั้ง ไม่มีวันลืม ไม่มีวันขี้เกียจ ไม่มีวันบอกว่า "อันนี้แก้นิดเดียวไม่ต้องเช็คหรอก"
ทำไมต้องมีทั้งที่เราเช็คเองได้: เพราะคนลืม เครื่องแต่ละคนไม่เหมือนกัน (ของคุณมีไฟล์ค้างอยู่แต่เครื่องคนอื่นไม่มี) และตอนรีบ ๆ คนมักข้ามขั้นตอน CI รันบนเครื่องสะอาดเสมอ — ซึ่งเหมือนกับ server จริงมากกว่าเครื่องคุณ
GitHub Actions ทำงานยังไง
GitHub Actions คือ CI ที่มาพร้อม GitHub เขียนคำสั่งไว้ในไฟล์ YAML ใน .github/workflows/ แล้วมันจะรันให้ตามเงื่อนไขที่กำหนด
โปรเจกต์นี้มี 2 ไฟล์:
| ไฟล์ | ทำงานเมื่อ | ทำอะไร |
|---|---|---|
ci.yml |
เปิด PR เข้า develop / uat / main |
ตรวจคุณภาพโค้ด |
deploy.yml |
โค้ดเข้า uat หรือ main |
build + deploy ขึ้น server |
อ่านไฟล์ CI ของจริง
นี่คือ ci.yml ของโปรเจกต์นี้ (ตัดมาบางส่วน) อ่านทีละบล็อก:
name: CI
on: # "รันเมื่อไหร่"
pull_request:
branches: [develop, uat, main]
concurrency: # ถ้า push ซ้ำ ให้ยกเลิกรอบเก่าที่ยังค้าง
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: # "ทำงานอะไรบ้าง" — แต่ละ job รันขนานกัน
checks:
name: Typecheck, lint, build
runs-on: ubuntu-latest # ขอเครื่อง Linux สะอาด ๆ หนึ่งเครื่อง
steps:
- uses: actions/checkout@v6 # ดึงโค้ดจาก PR ลงมา
- uses: actions/setup-node@v4 # ติดตั้ง Node
with:
node-version-file: .nvmrc # ใช้เวอร์ชันเดียวกับที่ทีมใช้
cache: npm
- run: npm ci # ติดตั้ง dependency แบบเป๊ะตาม lock file
- run: npx next typegen # สร้าง type ของ route
- run: npx tsc --noEmit # ตรวจ type
- run: npm run lint # ตรวจสไตล์โค้ด
- run: npm run build # ลอง build จริงจุดที่ควรสังเกต:
runs-on: ubuntu-latest— เครื่องใหม่เอี่ยมทุกครั้ง ไม่มีของค้างจากรอบก่อน นี่คือเหตุผลที่ CI จับบั๊กที่เครื่องคุณไม่เจอได้npm ciไม่ใช่npm install—ciติดตั้งตามpackage-lock.jsonเป๊ะ ๆ และจะ ล้มทันที ถ้า lock file ไม่ตรงกับpackage.json(บทหน้าจะเห็นว่าอันนี้ช่วยชีวิตยังไง)cancel-in-progress— push แก้ซ้ำ ๆ ก็ไม่เปลืองเครื่องรันรอบเก่าที่ไม่มีความหมายแล้ว
4 ด่านที่โค้ดต้องผ่าน
ด่านตรวจใน CI — คลิกดูว่าแต่ละด่านจับอะไร
1. Typecheck
ตรวจว่าชนิดข้อมูลตรงกันหมดไหม เช่นเอาข้อความไปใส่ในช่องที่ต้องการตัวเลข หรือเรียกฟิลด์ที่ไม่มีอยู่จริง จับบั๊กได้เยอะมากโดยไม่ต้องรันเว็บเลย
npx tsc --noEmit
2. Lint
ตรวจแพทเทิร์นการเขียนที่เป็นอันตราย หลายกฎมาจากบั๊กที่คนทั้งโลกเคยเจอ เช่นกฎของ React ที่จับปัญหาตะกร้าสินค้าในโปรเจกต์นี้ได้
npm run lint
3. Build
ลองสร้างเว็บเวอร์ชันจริงดู ถ้าโค้ดพังตอนรวมร่าง (เช่น import ไฟล์ที่ไม่มี) จะเจอตรงนี้
npm run build
4. Docker build
ลองห่อทั้งเว็บเป็น image ที่จะเอาไปรันบน server จริง จับปัญหาที่เกิดเฉพาะตอนติดตั้งบนเครื่อง Linux สะอาด ๆ เช่น lock file ไม่ตรง
docker build .
สามด่านแรกรันเองในเครื่องได้ก่อนเปิด PR: npx tsc --noEmit && npm run lint && npm run build
อ่านผล CI
บนหน้า PR จะมีแถบสถานะอยู่ล่างสุด:
- 🟡 เหลือง/หมุน = กำลังรัน (ปกติ 1–3 นาที)
- ✅ เขียว = ผ่านหมด พร้อม merge
- ❌ แดง = มีด่านที่ไม่ผ่าน ห้าม merge ให้กดเข้าไปอ่าน log
ดูผ่าน terminal ก็ได้:
gh pr checks --watch # ดูสถานะแบบเรียลไทม์
gh run view <run id> --log-failed # ดู log เฉพาะส่วนที่ล้มเคล็ดลับอ่าน log: เลื่อนหาบรรทัดที่ขึ้นต้นด้วย Error: หรือ ##[error] — ข้างบนนั้นคือขั้นตอนที่พัง ข้างล่างมักเป็นแค่ผลพลอยได้
CI แดงแล้วทำยังไง
- อ่าน log หาว่าด่านไหนล้มและบรรทัดไหน
- รันคำสั่งเดียวกันในเครื่องตัวเอง เพื่อให้เห็นปัญหาซ้ำ (สำคัญมาก — อย่าเดา)
- แก้ → commit → push ขึ้น branch เดิม → CI รันใหม่เอง (ไม่ต้องเปิด PR ใหม่)
ห้ามทำ: ปิดด่านที่แดงทิ้ง หรือแก้ config ให้ข้ามการตรวจ เพื่อให้ merge ผ่าน ๆ ไป — เท่ากับถอดสัญญาณเตือนไฟไหม้เพราะรำคาญเสียง
ลองทำเอง: ตั้งใจทำให้ CI แดงดูสักครั้งจะเข้าใจเร็วที่สุด — แตก branch ใหม่ แล้วเพิ่มบรรทัดนี้ในไฟล์
.tsxไฟล์ไหนก็ได้:const x: number = "ไม่ใช่ตัวเลข";push แล้วเปิด PR ดู CI แดง อ่าน log ให้ออกว่ามันบอกอะไร แล้วค่อยลบบรรทัดนั้นทิ้ง push ใหม่ ดูมันกลับมาเขียว
บทหน้าเป็นบทที่สนุกที่สุด: 3 บั๊กจริง ที่ CI จับได้ในโปรเจกต์นี้ ซึ่งถ้าไม่มี CI คงหลุดไปถึงเว็บจริงแล้ว