ApexHuasคู่มือช่างเว็บ GitHub repo

บทที่ 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 installci ติดตั้งตาม package-lock.json เป๊ะ ๆ และจะ ล้มทันที ถ้า lock file ไม่ตรงกับ package.json (บทหน้าจะเห็นว่าอันนี้ช่วยชีวิตยังไง)
  • cancel-in-progress — push แก้ซ้ำ ๆ ก็ไม่เปลืองเครื่องรันรอบเก่าที่ไม่มีความหมายแล้ว

4 ด่านที่โค้ดต้องผ่าน

ด่านตรวจใน CI — คลิกดูว่าแต่ละด่านจับอะไร

1. Typecheck

ตรวจว่าชนิดข้อมูลตรงกันหมดไหม เช่นเอาข้อความไปใส่ในช่องที่ต้องการตัวเลข หรือเรียกฟิลด์ที่ไม่มีอยู่จริง จับบั๊กได้เยอะมากโดยไม่ต้องรันเว็บเลย

npx tsc --noEmit
1 / 4

สามด่านแรกรันเองในเครื่องได้ก่อนเปิด 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 แดงแล้วทำยังไง

  1. อ่าน log หาว่าด่านไหนล้มและบรรทัดไหน
  2. รันคำสั่งเดียวกันในเครื่องตัวเอง เพื่อให้เห็นปัญหาซ้ำ (สำคัญมาก — อย่าเดา)
  3. แก้ → commit → push ขึ้น branch เดิม → CI รันใหม่เอง (ไม่ต้องเปิด PR ใหม่)

ห้ามทำ: ปิดด่านที่แดงทิ้ง หรือแก้ config ให้ข้ามการตรวจ เพื่อให้ merge ผ่าน ๆ ไป — เท่ากับถอดสัญญาณเตือนไฟไหม้เพราะรำคาญเสียง

ลองทำเอง: ตั้งใจทำให้ CI แดงดูสักครั้งจะเข้าใจเร็วที่สุด — แตก branch ใหม่ แล้วเพิ่มบรรทัดนี้ในไฟล์ .tsx ไฟล์ไหนก็ได้:

const x: number = "ไม่ใช่ตัวเลข";

push แล้วเปิด PR ดู CI แดง อ่าน log ให้ออกว่ามันบอกอะไร แล้วค่อยลบบรรทัดนั้นทิ้ง push ใหม่ ดูมันกลับมาเขียว

บทหน้าเป็นบทที่สนุกที่สุด: 3 บั๊กจริง ที่ CI จับได้ในโปรเจกต์นี้ ซึ่งถ้าไม่มี CI คงหลุดไปถึงเว็บจริงแล้ว