บทที่ 5 CI ด้วย GitHub Actions: workflow, required check และการอ่าน log
แผนบริหารการสอนประจำบทที่ 5
หัวข้อเนื้อหา
- 5.1 Continuous Integration และ Quality Gate
- 5.2 YAML เบื้องต้นสำหรับอ่านและแก้ workflow
- 5.3 องค์ประกอบของ GitHub Actions: workflow, event, job, step, action และ runner
- 5.4 CI ตัดสินผลจาก exit status
- 5.5 required status check: ให้ CI เป็นด่านก่อน merge
- 5.6 การอ่าน log ของ workflow run
- 5.7 ออกแบบประตูตรวจจากสิ่งที่ต้องปฏิเสธ
- 5.8 job ที่มีชื่อเรียงกันไม่ได้ทำงานเรียงกันเสมอ
- 5.9 อ่าน log โดยแยกอาการ ต้นเหตุ และสภาพแวดล้อม
- 5.10 shell ใน CI กับคำสั่งที่ดูเหมือนสำเร็จ
- ปฏิบัติการที่ 5 CI ด้วย GitHub Actions (Lab A–E)
วัตถุประสงค์เชิงพฤติกรรม
เมื่อศึกษาบทนี้และทำปฏิบัติการครบถ้วนแล้ว ผู้เรียนสามารถปฏิบัติได้ตามผลลัพธ์การเรียนรู้ต่อไปนี้ โดยแต่ละข้อมีงานที่ใช้วัดผลกำกับไว้
| รหัส | ผลลัพธ์การเรียนรู้ | วัดจาก | CLO ของรายวิชา |
|---|---|---|---|
| 5.1 | กำหนดค่า workflow ที่ให้มาให้รัน npm ci และ npm test เมื่อ push และ pull request | งานข้อ 1 | CLO2 |
| 5.2 | ทำให้ pipeline ล้มด้วย test ที่ผิดโดยตั้งใจ แล้วแก้ให้ผ่าน | งานข้อ 2 | CLO2 |
| 5.3 | อธิบายลำดับ event → job → step → ผลของ pipeline ตนเองจาก log และบอกว่าขั้นใดทำให้ run ล้ม | งานข้อ 3 | CLO1 |
CLO ที่เกี่ยวข้อง (ข้อความจากหลักสูตร):
- CLO1 สามารถนำความต้องการและสถาปัตยกรรมการส่งมอบซอฟต์แวร์ที่กำหนดมาใช้ โดยอธิบาย Git workflow, CI/CD pipeline flow และการใช้ Containerization ได้อย่างเหมาะสมกับบริบทของงาน
- CLO2 สามารถกำหนดค่าและใช้งาน DevOps workflow ที่กำหนดมา เพื่อให้กระบวนการพัฒนา–ทดสอบ–ส่งมอบซอฟต์แวร์เป็นไปอย่างถูกต้องและมีประสิทธิภาพ
บทนี้เป็นบทแรกที่ผู้เรียนกำหนดค่า DevOps workflow ที่ทำงานอัตโนมัติจริง จึงเริ่มเก็บหลักฐาน CLO2 จากไฟล์ค่าตั้งและผลการรัน ส่วนใบงานแผนภาพ pipeline เป็นหลักฐาน CLO1 ชิ้นที่ 2 ซึ่งเขียนเป็นรายบุคคล
วิธีสอนและกิจกรรมการเรียนการสอน
- บรรยายแนวคิดตามหัวข้อ 5.1–5.6 ประกอบตัวอย่างที่ซับซ้อนขึ้นตามลำดับ และใช้คำถามตรวจความเข้าใจท้ายหัวข้อเป็นกิจกรรมอภิปรายเป็นคู่
- สาธิต workflow run ที่ผ่านและที่ล้มบน repository ตัวอย่างของผู้สอน แล้วเปิด log ทีละ step ให้ทั้งห้องอ่านพร้อมกัน
- ให้ผู้เรียนหาจุดผิดใน workflow ตัวอย่างด้วยเครื่องมือ actionlint หรือด้วยการอ่าน
- ปฏิบัติการ Lab A–E บน repository ทีม แต่ละ Lab ประกอบด้วยส่วนฝึกตามตัวอย่างและส่วนที่ผู้เรียนต้องตัดสินใจเอง
- มอบหมายใบงานแผนภาพ pipeline แบบฝึกหัดท้ายบท Learning Log ครั้งที่ 5 และการศึกษาค้นคว้าเพิ่มเติม
สื่อการเรียนการสอน
- เอกสารประกอบการสอนบทที่ 5 (บทความนี้) และหน้ารวมรายวิชา 10306343
- repository ทีม
devops-appที่ผ่าน M3 จากบทที่ 4 - บทความเดิมบนเว็บไซต์ เริ่มต้น CI ด้วย GitHub Actions: คู่มือลงมือทำจริงใน 6 ขั้นตอน สำหรับทบทวนนอกเวลา
- เอกสารทางการ GitHub Docs หัวข้อ Understanding GitHub Actions, Building and testing Node.js และ Triggering a workflow
- (ไม่บังคับ) เครื่องมือ actionlint สำหรับตรวจไฟล์ workflow ในเครื่อง
การวัดผลและการประเมินผล
กำหนดส่ง: ก่อนเริ่มเรียนบทที่ 7 เพื่อเว้นระยะหนึ่งบท
| ข้อ | งาน | หลักฐาน | ผลลัพธ์ | CLO |
|---|---|---|---|---|
| 1 | M4 workflow CI ของทีม | ลิงก์ไฟล์ .github/workflows/ci.yml บน main ลิงก์ run สีเขียวบน main และภาพหน้าตั้ง branch protection ที่เปิด required status check | 5.1 | CLO2 |
| 2 | pull request ที่ถูกกั้นแล้วแก้ผ่าน | ลิงก์ pull request ของตนเอง (Lab D) ที่มี run สีแดงก่อน แล้วสีเขียวถัดมา และ merge ได้หลังสีเขียวเท่านั้น | 5.2 | CLO2 |
| 3 | ใบงานแผนภาพ pipeline (CLO1 ชิ้นที่ 2) | แผนภาพ event → job → step → ผล ของ workflow ทีม พร้อมคำอธิบายรายบุคคลไม่เกิน 1 หน้า โดยอ้างบรรทัดจาก log ของ run สีแดงของตนเอง | 5.3 | CLO1 |
| 4 | Learning Log ครั้งที่ 5 | ตอบ 4 หัวข้อ: สิ่งที่เรียนในบทนี้ · ข้อผิดพลาดใน workflow ที่พบและวิธีหาสาเหตุ · แหล่งที่ใช้ค้นหาคำตอบ (ระบุลิงก์เอกสารทางการอย่างน้อย 1 แหล่ง) · ขั้นใดของ pipeline ที่ยังไม่เข้าใจ | — | บันทึกสะท้อนการเรียนรู้ ไม่มีหมวดคะแนน portfolio แยก |
เกณฑ์ให้คะแนน
| ข้อ | ผ่าน | ยังไม่ผ่าน |
|---|---|---|
| 1 | ci.yml รัน npm ci และ npm test เมื่อ push เข้า main และเมื่อเปิด pull request เข้า main · run ล่าสุดบน main เป็นสีเขียว · required status check เปิดอยู่ | ไม่มี run บน main หรือ run ล่าสุดล้ม หรือไม่ได้ตั้ง required status check |
| 2 | ประวัติ run ของ pull request มีสีแดงตามด้วยสีเขียว และ merge หลังสีเขียว | มีเฉพาะสีเขียว หรือ merge ขณะยังเป็นสีแดง |
| 4 | ตอบครบ 4 หัวข้อ และมีลิงก์เอกสารทางการ | ขาดหัวข้อ หรือไม่มีแหล่งอ้างอิง |
ข้อ 3 ใช้ rubric CLO1 3 ระดับ
| ระดับ | ลักษณะของคำตอบ |
|---|---|
| 3 ดี | อธิบายลำดับและเหตุผลถูกต้อง ชี้ step ที่ล้มจาก log พร้อมเหตุผล และเปรียบเทียบทางเลือกการจัด pipeline อย่างน้อย 2 ทางพร้อมข้อแลกเปลี่ยน เช่น รวมทุก step ใน job เดียวเทียบกับแยก job |
| 2 ผ่าน | แผนภาพเรียงลำดับ event → job → step ถูกต้องตาม workflow ทีม อธิบายว่าแต่ละขั้นทำอะไรและเหตุใดจึงอยู่ในลำดับนั้น และชี้ step ที่ทำให้ run ล้มได้ถูกต้องโดยอ้างบรรทัดจาก log |
| 1 ยังไม่ผ่าน | ท่องนิยามคำศัพท์โดยไม่โยงกับ workflow ของทีม หรือชี้ step ที่ล้มไม่ได้ |
เฉลยจะเปิดในชั้นเรียนหลังทุกคนทำเสร็จ
การคิดคะแนนฉบับปัจจุบัน
มติผู้สอน 28 ต.ค. 2569: เข้าเรียน 10% · ใบงานบทที่ 1–13 รวม 40% · FP 20% · FW 20% · โครงงาน 10% ส่วนบทที่ 14–15 ฝึกเพื่อรับข้อเสนอแนะ ไม่นับคะแนน
ใบงาน C05-WS01 มีคะแนนดิบเต็ม 30 คะแนน แยกตามผลลัพธ์ด้านล่าง ระบบนำไปรวมในหมวดใบงาน 40% โดยไม่เพิ่มคะแนน milestone, portfolio หรือจุดตรวจเป็นหมวดแยก
| ผลลัพธ์ | หลักฐานตามงานส่ง | คะแนนดิบเต็ม |
|---|---|---|
| LLO5.1 | งานข้อ 1 | 10 |
| LLO5.2 | งานข้อ 2 | 10 |
| LLO5.3 | งานข้อ 3 | 10 |
ประเมินงานแต่ละข้อจากหลักฐานของข้อนั้นโดยตรง ไม่ตัดสิทธิ์รับคะแนนของอีกข้อเพราะ milestone หรือจุดตรวจอื่นยังไม่ผ่าน งานที่ยังทำไม่ได้ให้บันทึกปัญหาและหลักฐานที่มีตามจริง
ใช้เกณฑ์ความถูกต้อง เหตุผล และหลักฐานที่ระบุในงานแต่ละข้อประกอบการประเมินราย LLO งานเตรียมพื้นและ Learning Log ที่ไม่มีรหัส LLO ใช้ให้ข้อเสนอแนะ ไม่เพิ่มคะแนนดิบให้ใบงาน
บทนำ
บทนี้นำคำสั่ง npm ci และ npm test ที่ทำให้ทำซ้ำได้ในบทที่ 4 มาให้ระบบรันโดยอัตโนมัติทุกครั้งที่มีการ push หรือเปิด pull request ด้วย GitHub Actions เนื้อหาเริ่มจากความหมายของ Continuous Integration (CI) และแนวคิด Quality Gate ไวยากรณ์ YAML ที่จำเป็นต่อการอ่านไฟล์ workflow องค์ประกอบของ GitHub Actions การตัดสินผลด้วย exit status การตั้ง required status check ให้ CI เป็นด่านก่อน merge และการอ่าน log เพื่อหาสาเหตุเมื่อ run ล้ม
ความสำคัญ: ในบทที่ 3 การตรวจทาน pull request อาศัยสมาชิกทีมเพียงอย่างเดียว ผู้ตรวจทานอาจลืมรัน test หรือรันบนเครื่องที่มี dependency ต่างจากผู้เปิด CI ทำให้ทุก pull request ถูกตรวจด้วยขั้นตอนเดียวกันบนเครื่องใหม่ทุกครั้ง และเมื่อตั้งเป็น required status check งานที่ test ไม่ผ่านจะ merge เข้า main ไม่ได้ ซึ่งเป็นเงื่อนไขให้บทที่ 8 deploy จาก main ได้อย่างมั่นใจ
ความเชื่อมโยงกับบทอื่น: บทนี้ใช้ exit status และสคริปต์ check-errors.sh จากบทที่ 1 GitHub flow และ branch protection จากบทที่ 3 และ lock file กับ npm script test จากบทที่ 4 ผลผลิตของบทนี้ถูกใช้ต่อตามตารางต่อไปนี้
| สิ่งที่ได้จากบทนี้ | ใช้ต่อในบท |
|---|---|
.github/workflows/ci.yml | 8 (เพิ่ม workflow CD ที่ทำงานต่อจาก CI) · 10 (เพิ่มขั้นสแกนช่องโหว่) · 12 (pipeline ครบสาย) |
required status check ของ main | ทุกบทหลังจากนี้ pull request ต้องผ่าน CI ก่อน merge |
| ทักษะอ่าน log ของ run | 8, 10, 11 และ 12 ใช้หาสาเหตุเมื่อ deploy หรือสแกนล้ม |
| ใบงานแผนภาพ pipeline | ต่อยอดเป็นแผนภาพ CI/CD ครบสายในใบงานบทที่ 8 |
ความรู้พื้นฐาน เครื่องมือ และรุ่นซอฟต์แวร์
ความรู้พื้นฐาน: exit status และ $? จากบทที่ 1 การทำงานผ่าน pull request จากบทที่ 3 คำสั่ง npm ci และ npm test จากบทที่ 4 ไวยากรณ์ YAML สอนในหัวข้อ 5.2 ระดับอ่านออกและแก้ได้
เครื่องมือ: repository ทีม devops-app ที่ผ่าน M3 เครื่อง Ubuntu ที่ติดตั้ง Git และ Node.js แล้ว และเว็บเบราว์เซอร์สำหรับเปิดแท็บ Actions ของ GitHub เครื่องมือ actionlint เป็นทางเลือกสำหรับตรวจไฟล์ workflow ในเครื่องก่อน push
รุ่นซอฟต์แวร์ที่ใช้ทดสอบ:
| ซอฟต์แวร์ | รุ่นที่ทดสอบ |
|---|---|
| actionlint | 1.7.12 (ตรวจไฟล์ workflow ทุกไฟล์ในบทนี้ ทดสอบเมื่อ 19 ต.ค. 2569) |
| Node.js และ npm | 22.22.0 และ 10.9.4 (จำลองคำสั่งใน step ของ workflow บนเครื่องผู้เขียน) |
actions/checkout และ actions/setup-node | v6 และ v7 ตามตัวอย่างใน GitHub Docs: Building and testing Node.js ณ วันที่เข้าถึง |
| GitHub Actions (บริการจริง) | ยังไม่ได้ทดสอบรัน workflow บน GitHub ในรอบนี้ |
ไฟล์ workflow ทุกไฟล์ในบทนี้ผ่านการตรวจไวยากรณ์ด้วย actionlint และคำสั่งใน step ทุกคำสั่งผ่านการรันจำลองด้วย bash -e บนเครื่องผู้เขียน แต่ยังไม่ได้รันบน GitHub-hosted runner จริง ผลลัพธ์บนหน้าเว็บที่บทความบรรยายไว้ (เช่น สีของ run และข้อความใน log) อ้างอิงจากเอกสารของ GitHub และลักษณะการทำงานทั่วไปของบริการ ซึ่งชื่อเมนูและรูปแบบหน้าเว็บอาจเปลี่ยนตามรุ่นของบริการ
ค่าใช้จ่ายและข้อมูลลับ: GitHub-hosted runner มาตรฐานใช้กับ repository แบบ public ได้โดยไม่มีค่าใช้จ่าย ส่วน repository แบบ private มีโควตานาทีต่อเดือนตามแผนบริการ ซึ่งเป็นอีกเหตุผลหนึ่งที่รายวิชาใช้ repository แบบ public workflow ในบทนี้ไม่ใช้ข้อมูลลับใด ๆ ห้ามพิมพ์ token หรือรหัสผ่านลงในไฟล์ workflow โดยตรง เพราะไฟล์อยู่ใน repository ที่เปิดเผย การเก็บความลับสำหรับ workflow จะสอนในบทที่ 8 และ 10
5.1 Continuous Integration และ Quality Gate
Martin Fowler นิยาม Continuous Integration ว่าเป็นแนวปฏิบัติที่สมาชิกทุกคนของทีม merge งานเข้า codebase ร่วมกันอย่างน้อยวันละครั้ง และทุกครั้งที่ merge จะถูกตรวจด้วย automated build ที่มีการทดสอบอยู่ในตัว (Fowler: Continuous Integration) แนวปฏิบัติจากบทความเดียวกันที่รายวิชาเลือกใช้มีดังนี้
| แนวปฏิบัติของ Fowler | ในรายวิชานี้ |
|---|---|
| build อัตโนมัติด้วยคำสั่งเดียว | npm ci แล้วตามด้วย npm test จากบทที่ 4 |
| build ที่ทดสอบตัวเองได้ (self-testing build) | npm test อยู่ใน workflow ทุก run |
| ทุก push เข้า mainline ต้องถูก build | workflow ทำงานเมื่อ push เข้า main และเมื่อเปิด pull request เข้า main |
| แก้ build ที่พังทันที | run สีแดงบน pull request ต้องแก้ก่อนทำงานอื่น |
| build ต้องเร็ว (commit build ประมาณ 10 นาที) | workflow ของรายวิชามีขั้นตอนน้อยและควรเสร็จในไม่กี่นาที |
Quality Gate คือเงื่อนไขที่งานต้องผ่านก่อนเข้าสู่ขั้นถัดไป แผนเดิมของรายวิชากล่าวถึง Quality Gate ในบริบทของ Jenkins และ SonarQube รายวิชานี้ใช้แนวคิดเดียวกันในรูปแบบที่เรียบง่ายกว่า คือ test ทุกรายการต้องผ่าน และ required status check ต้องเป็นสีเขียวก่อน merge
ระดับที่ 1: CI ไม่ใช่ชื่อเครื่องมือ แต่เป็นแนวปฏิบัติของทีม GitHub Actions เป็นเครื่องมือที่ช่วยให้แนวปฏิบัตินี้เกิดขึ้นอัตโนมัติ ทีมที่มี GitHub Actions แต่ merge งานเดือนละครั้งยังไม่ได้ทำ CI
ระดับที่ 2: เหตุผลของการ merge บ่อยคือการทำให้ความต่างระหว่าง branch เล็กเสมอ ความต่างเล็กทำให้ conflict น้อยและหาสาเหตุเมื่อ test ล้มได้ง่าย เพราะมีการเปลี่ยนแปลงไม่กี่บรรทัดที่น่าสงสัย ข้อนี้สอดคล้องกับ branch อายุสั้นของ GitHub flow ในบทที่ 3
ระดับที่ 3: CI ให้ผลเพียงเท่าที่ test ครอบคลุม แอปของรายวิชามี test 3 รายการซึ่งตรวจเพียงบาง endpoint run สีเขียวจึงหมายถึง "test ที่มีอยู่ผ่าน" ไม่ใช่ "แอปไม่มี bug" การเพิ่ม test ของสมาชิกแต่ละคนใน Lab D จึงเพิ่มคุณภาพของด่านนี้โดยตรง
คำถามตรวจความเข้าใจ 5.1: ทีมหนึ่งตั้ง GitHub Actions ครบทุกขั้น แต่สมาชิกแต่ละคนทำงานบน branch ของตนเองสามสัปดาห์ก่อนเปิด pull request ทีมนี้ปฏิบัติตามนิยาม CI ของ Fowler หรือไม่ เพราะเหตุใด
5.2 YAML เบื้องต้นสำหรับอ่านและแก้ workflow
ไฟล์ workflow ของ GitHub Actions เขียนด้วย YAML ซึ่งเป็นรูปแบบข้อมูลที่ใช้การย่อหน้าแสดงโครงสร้าง รายวิชาใช้ไวยากรณ์ YAML เพียงสี่แบบ
| ไวยากรณ์ | ตัวอย่าง | ความหมาย |
|---|---|---|
| คู่ key: value | runs-on: ubuntu-latest | ค่าเดี่ยว |
| การย่อหน้า | jobs: แล้วย่อหน้า test: ด้านล่าง | test อยู่ภายใต้ jobs |
| รายการ (list) | บรรทัดขึ้นต้นด้วย - | สมาชิกของลำดับ เช่น step แต่ละ step |
| ข้อความหลายบรรทัด | run: | แล้วย่อหน้าคำสั่งด้านล่าง | คำสั่ง shell หลายบรรทัดใน step เดียว |
ระดับที่ 1: การย่อหน้าต้องใช้ช่องว่าง (space) เท่านั้น ห้ามใช้ tab ตัวอย่างผลตรวจด้วย actionlint เมื่อไฟล์มี tab
.github/workflows/b.yml:7:11: could not parse as YAML: found a tab character that violates indentation [syntax-check]ระดับที่ 2: ระดับการย่อหน้าบอกความเป็นเจ้าของ step ที่ย่อหน้าผิดระดับจะกลายเป็นส่วนของ key อื่นหรือทำให้ไฟล์อ่านไม่ได้ทั้งไฟล์ รายวิชาใช้การย่อหน้าครั้งละ 2 ช่องว่างตามตัวอย่างใน GitHub Docs
ระดับที่ 3: YAML รุ่น 1.1 ตีความคำบางคำเป็นค่าตรรกะ การทดสอบอ่านไฟล์ ci.yml ของรายวิชาด้วยไลบรารี PyYAML พบว่า key on ถูกอ่านเป็นค่า true แทนข้อความ "on" GitHub Actions อ่าน on ได้ถูกต้อง แต่เครื่องมืออื่นที่อ่านไฟล์ workflow อาจไม่เป็นเช่นนั้น ข้อนี้อธิบายว่าเหตุใดตัวเลขเวอร์ชันอย่าง '22.x' จึงควรใส่เครื่องหมายคำพูดให้เป็นข้อความชัดเจน
คำถามตรวจความเข้าใจ 5.2: ในไฟล์ต่อไปนี้ npm test เป็น step ของ job ใด หรือไฟล์นี้ผิดไวยากรณ์ อธิบายจากการย่อหน้า
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: npm ci
- run: npm test5.3 องค์ประกอบของ GitHub Actions: workflow, event, job, step, action และ runner
GitHub Docs นิยามองค์ประกอบหลักไว้ดังนี้ (GitHub Docs: Understanding GitHub Actions)
| องค์ประกอบ | ความหมาย | ใน ci.yml ของรายวิชา |
|---|---|---|
| workflow | กระบวนการอัตโนมัติที่รัน job อย่างน้อยหนึ่ง job กำหนดด้วยไฟล์ YAML ใน .github/workflows | ไฟล์ ci.yml ชื่อ CI |
| event | กิจกรรมใน repository ที่ทำให้ workflow ทำงาน | push และ pull_request เข้า main และ workflow_dispatch |
| job | ชุดของ step ที่รันบน runner เดียวกัน job หลาย job รันขนานกันโดยปริยาย | job test |
| step | shell script ที่รัน หรือ action ที่เรียกใช้ ทำงานตามลำดับภายใน job | Checkout, Setup Node.js, Install dependencies, Run tests |
| action | ชุดงานที่นำกลับมาใช้ซ้ำได้ | actions/checkout, actions/setup-node |
| runner | เครื่องที่รัน workflow GitHub-hosted runner ได้ VM ใหม่ทุกครั้ง | ubuntu-latest |
workflow ที่ใช้ในรายวิชาดัดแปลงจากตัวอย่างใน GitHub Docs: Building and testing Node.js ซึ่งมีลำดับ actions/checkout → actions/setup-node → npm ci → npm run build --if-present → npm test (GitHub Docs: Building and testing Node.js) รายวิชาตัดขั้น build ออกเพราะแอปไม่มีขั้น compile ตามที่อธิบายในบทที่ 4 และเปลี่ยนรุ่น Node.js เป็น 22.x ให้ตรงกับรุ่นที่ทดสอบแอป
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '22.x'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm testระดับที่ 1 event: on: push เพียงอย่างเดียวทำให้ workflow ทำงานเมื่อ push เข้าทุก branch ส่วน pull_request ที่ระบุ branches: [main] ทำงานเฉพาะ pull request ที่ส่งเข้า main และ workflow_dispatch เพิ่มปุ่มสั่งรันด้วยมือในแท็บ Actions (GitHub Docs: Triggering a workflow) รายวิชาจำกัด push ไว้ที่ main เพื่อไม่ให้ branch งานรันซ้ำสองครั้ง (ครั้งหนึ่งจาก push และอีกครั้งจาก pull request)
ระดับที่ 2 runner ใหม่ทุกครั้ง: runner ของแต่ละ run เริ่มจากเครื่องว่าง ไม่มีโค้ดของ repository ไม่มี node_modules และไม่มีผลจาก run ก่อนหน้า step แรกจึงต้องเป็น actions/checkout เพื่อดึงโค้ดลงเครื่อง และ step ติดตั้ง dependency ต้องทำทุกครั้ง นี่คือเหตุผลที่บทที่ 4 ทำให้ npm ci บน clone ใหม่ใช้ได้ก่อน เพราะ runner ทำสิ่งเดียวกับการ clone ใหม่ทุกครั้ง
ระดับที่ 3 job แยกกันไม่ใช้เครื่องร่วมกัน: ถ้าแยก npm test ไปไว้ใน job ที่สอง job นั้นจะรันบน runner อีกเครื่องที่ไม่มีโค้ดและไม่มี dependency และโดยปริยายจะรันขนานกับ job แรก การแยก job จึงต้องทำ checkout และติดตั้ง dependency ซ้ำ และต้องกำหนด needs: หากต้องการให้รอ job ก่อนหน้า รายวิชาจึงใช้ job เดียวเพื่อความเรียบง่าย ข้อแลกเปลี่ยนนี้เป็นประเด็นของ rubric ระดับ 3 ในใบงาน CLO1
คำถามตรวจความเข้าใจ 5.3: ระบุ event, job, step และ action ใน workflow ข้างต้น และอธิบายว่าถ้าลบ step Checkout ออก step Install dependencies จะเกิดอะไรขึ้น
5.4 CI ตัดสินผลจาก exit status
ระบบ CI ไม่ได้อ่านข้อความที่คำสั่งพิมพ์ออกมาเพื่อตัดสินว่าผ่านหรือไม่ แต่ดู exit status ของคำสั่งตามที่เรียนในบทที่ 1 step แบบ run จะล้มเมื่อคำสั่งจบด้วย exit status ที่ไม่ใช่ 0 และเมื่อ step หนึ่งล้ม step ที่เหลือใน job จะถูกข้าม job และ run จะแสดงเป็นสีแดง
| คำสั่งใน step | exit status เมื่อปกติ | exit status เมื่อผิด (ทดสอบแล้ว) |
|---|---|---|
npm ci | 0 | 1 (เช่น ไม่มี lock file หรือ lock file ไม่ตรง) |
npm test | 0 | 1 (มี test ล้มอย่างน้อย 1 รายการ หรือไม่มี script test) |
curl -fsS URL | 0 | 22 เมื่อได้ HTTP 400 ขึ้นไป และ 7 เมื่อเชื่อมต่อไม่ได้ |
bash scripts/check-errors.sh app.log | 0 (ไม่พบ ERROR) | 1 (พบ ERROR) และ 2 (ไม่พบไฟล์) |
ระดับที่ 1: สคริปต์ check-errors.sh จากบทที่ 1 ถูกออกแบบให้จบด้วย exit status 1 เมื่อพบ ERROR จึงใช้เป็นขั้นตรวจใน CI ได้ทันทีโดยไม่ต้องแก้
ระดับที่ 2: ตัวเลือก -f ของ curl ทำให้ curl จบด้วย exit status ที่ไม่ใช่ 0 เมื่อได้รหัส HTTP 400 ขึ้นไป หากไม่ใส่ -f คำสั่ง curl ที่ได้ HTTP 404 จะจบด้วย 0 และ CI จะถือว่าผ่าน ผลทดสอบเมื่อใส่ -f
curl: (22) The requested URL returned error: 404ระดับที่ 3: คำสั่ง shell หลายบรรทัดใน run: | รันด้วย bash -e บน runner Linux โดยปริยาย ตัวเลือก -e ทำให้ shell หยุดทันทีเมื่อคำสั่งใดล้ม แต่คำสั่งที่ต่อกันด้วยท่อ (|) จะใช้ exit status ของคำสั่งสุดท้ายในท่อ เช่น npm test | tee test.log จะผ่านเสมอเพราะ tee จบด้วย 0 แม้ npm test ล้ม (ทดสอบพฤติกรรมของ bash -e กับท่อแล้วในเครื่อง) การใช้ท่อใน step จึงต้องระวัง ข้อที่ต้องยืนยัน: shell ปริยายของ GitHub Actions ตามเอกสาร workflow syntax ยังไม่อยู่ในรายการแหล่งอ้างอิงของบท
คำถามตรวจความเข้าใจ 5.4: step หนึ่งรัน curl http://localhost:3000/health (ไม่มี -f) ขณะที่แอปตอบ HTTP 500 step นี้ผ่านหรือล้ม และควรแก้อย่างไร
5.5 required status check: ให้ CI เป็นด่านก่อน merge
ผลของ workflow ที่รันบน pull request ปรากฏในแท็บ Checks และท้ายแท็บ Conversation ของ pull request แต่ถ้าไม่ได้ตั้งค่าเพิ่ม สมาชิกยังกด merge ได้แม้ run เป็นสีแดง กฎ Require status checks to pass before merging ของ protected branch ที่กล่าวไว้ในบทที่ 3 ทำให้ GitHub กั้นปุ่ม merge จนกว่า check ที่เลือกจะผ่าน
| สถานะของ check | ผลต่อปุ่ม merge เมื่อตั้ง required status check |
|---|---|
| กำลังรัน (สีเหลือง) | merge ไม่ได้ |
| ล้ม (สีแดง) | merge ไม่ได้ |
| ผ่าน (สีเขียว) และมี approval ครบ | merge ได้ |
ระดับที่ 1: check ที่จะเลือกได้ในหน้าตั้งค่าต้องเคยรันใน repository มาก่อนอย่างน้อยหนึ่งครั้ง Lab D จึงอยู่หลัง Lab B ซึ่งทำให้ workflow รันแล้ว
ระดับที่ 2: เมื่อ push commit ใหม่เข้า branch ของ pull request workflow จะรันใหม่โดยอัตโนมัติ ผู้เปิดจึงแก้ test ที่ล้มด้วยการ commit เพิ่มบน branch เดิม ไม่ต้องเปิด pull request ใหม่ เช่นเดียวกับการแก้ตามความเห็น review ในบทที่ 3
ระดับที่ 3: required status check เป็นการเปลี่ยน "ข้อตกลง" ของทีมให้เป็น "กฎของระบบ" ในลักษณะเดียวกับที่ branch protection เปลี่ยนข้อตกลงเรื่อง review เป็นกฎ ข้อดีคือไม่ขึ้นกับความจำหรือความเกรงใจของสมาชิก ข้อแลกเปลี่ยนคือเมื่อ CI ล้มด้วยเหตุที่ไม่เกี่ยวกับโค้ด เช่น เครือข่ายของ runner มีปัญหา ทีมต้องสั่งรันใหม่ (Re-run) ก่อนจึงจะ merge ได้
คำถามตรวจความเข้าใจ 5.5: ผู้ดูแล repository ต้องการให้ pull request merge ได้เมื่อมี approval 1 ครั้งและ CI ผ่าน ต้องเปิดกฎใดของ protected branch บ้าง และกฎใดต้องตั้งหลังจาก workflow รันแล้วอย่างน้อยหนึ่งครั้ง
5.6 การอ่าน log ของ workflow run
หน้า run แต่ละครั้งในแท็บ Actions แสดง job ทางซ้ายและ step ของ job ทางขวา step ที่ผ่านมีเครื่องหมายถูก step ที่ล้มมีเครื่องหมายกากบาท และ step หลังจากนั้นแสดงว่าถูกข้าม การคลิกที่ชื่อ step เปิด log ของ step นั้นซึ่งเป็นผลที่คำสั่งพิมพ์ออกมาเหมือนกับที่เห็นใน terminal
วิธีอ่าน log เมื่อ run ล้มมีลำดับดังนี้
- หา step แรกที่ล้ม step ถัดไปที่ถูกข้ามไม่ใช่สาเหตุ
- เลื่อนไปท้าย log ของ step นั้น บรรทัดท้ายมักระบุ exit status ของคำสั่ง
- ไล่ขึ้นหาข้อความที่บอกสาเหตุ สำหรับ
npm testคือบรรทัดnot okพร้อมlocation,expectedและactual - ทำซ้ำความผิดพลาดในเครื่องด้วยคำสั่งเดียวกัน แก้ แล้ว push ใหม่
ระดับที่ 1: log ของ step Run tests ที่ล้มมีรูปแบบเดียวกับผลของ npm test ในเครื่องที่ฝึกอ่านใน Lab C ของบทที่ 4 ทักษะเดิมจึงใช้ได้ทันที
ระดับที่ 2: step แบบ uses เช่น actions/setup-node พิมพ์ข้อความของ action เอง ซึ่งช่วยตรวจว่า runner ได้ Node.js รุ่นใด ถ้า test ผ่านในเครื่องแต่ล้มบน runner ให้เทียบรุ่น Node.js ใน log กับรุ่นในเครื่องก่อน
ระดับที่ 3: ข้อความใน log ที่ไม่ได้เป็นสาเหตุอาจดูน่ากลัวกว่าสาเหตุจริง เช่น คำเตือนเรื่อง package ที่ขอรับการสนับสนุน (npm fund) ซึ่งไม่ทำให้ step ล้ม การตัดสินว่าบรรทัดใดเป็นสาเหตุต้องโยงกับ exit status ของ step ไม่ใช่ความยาวหรือสีของข้อความ
คำถามตรวจความเข้าใจ 5.6: run หนึ่งแสดง step Checkout และ Setup Node.js ผ่าน step Install dependencies ล้ม และ step Run tests ถูกข้าม สมาชิกคนหนึ่งเปิด log ของ Run tests เพื่อหาสาเหตุ อธิบายว่าวิธีนี้ผิดอย่างไร และสาเหตุที่น่าจะเป็นของ step ที่ล้มคืออะไร (อ้างบทที่ 4)
5.7 ออกแบบประตูตรวจจากสิ่งที่ต้องปฏิเสธ
CI ที่มีประโยชน์ต้องตอบได้ว่างานประเภทใดควรถูกปฏิเสธ ตัวอย่างสมมติของ devops-app กำหนดว่า dependency ต้องติดตั้งตาม lock file, test ต้องตรวจสัญญาของ /health และสคริปต์ต้องคืนสถานะล้มเหลวเมื่อปลายทางไม่ตอบ หาก workflow ทำเพียง checkout แล้วพิมพ์ done ผลสีเขียวพิสูจน์ได้เพียงว่าขั้นเหล่านั้นสำเร็จ ไม่ได้พิสูจน์ข้อกำหนดของแอป
การสร้าง quality gate ควรเริ่มจากจับคู่ความเสี่ยงกับหลักฐาน เช่น manifest ไม่ตรง lock file ตรวจด้วย npm ci พฤติกรรม endpoint เปลี่ยนตรวจด้วย test และแอปเปิดบริการไม่ได้ตรวจด้วย smoke test จากนั้นสร้างตัวอย่างเสียหนึ่งกรณีต่อความเสี่ยงเพื่อยืนยันว่าประตูหยุดได้จริง ถ้าทำให้โค้ดผิดแล้ว CI ยังผ่าน ควรตรวจว่า test ถูกเรียกจริงหรือไม่ เลือกไฟล์ครบหรือไม่ และมีคำสั่งใดกลบสถานะหรือไม่
ตัวอย่างที่ไม่ถูกต้องคือใส่ continue-on-error: true ให้ขั้น test แล้วถือว่า required check ป้องกันงานเสียได้ การตั้งค่านี้มีประโยชน์กับการเก็บข้อมูลจากงานที่ยังไม่บังคับ แต่ไม่เหมาะกับขั้นที่ต้องหยุดการรวมงานตามนโยบาย การตัดสินต้องแยก “ต้องการเก็บรายงานแม้ทดสอบล้ม” ออกจาก “ยอมให้ผล test ไม่มีผลต่อ gate” ซึ่งเป็นคนละเรื่องกัน
การทดสอบเชิงลบควรจบด้วยหลักฐานสองด้าน ได้แก่ snapshot ที่ผิดทำให้ check ล้ม และ snapshot ที่แก้แล้วทำให้ check ผ่าน การแสดงเฉพาะ run ที่ผ่านไม่บอกว่าระบบตรวจพบข้อผิดพลาดได้ และการแสดงเฉพาะ run ที่ล้มไม่บอกว่าวิธีแก้ถูกต้องแล้ว
5.8 job ที่มีชื่อเรียงกันไม่ได้ทำงานเรียงกันเสมอ
ตามเอกสาร GitHub Actions jobs ทำงานขนานได้โดยปริยาย หากต้องการให้ job หนึ่งรออีก job ต้องกำหนดความสัมพันธ์ด้วย needs การวาง test ไว้เหนือ build ใน YAML จึงไม่ใช่การสร้างลำดับการพึ่งพา (GitHub Docs: Workflow syntax)
ตัวอย่างต่อไปนี้เป็นส่วนตัดของกราฟ job เพื่ออธิบายความสัมพันธ์ ยังไม่ใช่ workflow ที่นำไปใช้ได้ครบถ้วน เพราะไม่แสดง checkout และคำสั่งสร้าง image
# ตัวอย่างที่ไม่ถูกต้อง หากนโยบายกำหนดว่าต้อง test ผ่านก่อน build
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo "test placeholder"
build:
runs-on: ubuntu-latest
steps:
- run: echo "build placeholder"สอง job ด้านบนไม่มีขอบเชื่อม จึงอาจเริ่มพร้อมกันได้ การแก้ความสัมพันธ์ต้องเพิ่ม needs: test ใน build เมื่อใช้งานจริงต้องแทน placeholder ด้วยคำสั่งตรวจที่มีความหมายด้วย มิฉะนั้นกราฟถูกแต่ gate ยังไม่มีหลักฐาน การตรวจไวยากรณ์จึงต้องทำร่วมกับการตรวจเจตนา
ความผิดอีกแบบคือ job แรกสร้างไฟล์ไว้ใน working directory แล้ว job ที่สองคาดว่าจะเห็นไฟล์นั้นโดยอัตโนมัติ แต่ job บน runner คนละสภาพแวดล้อมต้องส่งผลผลิตผ่านกลไกที่ออกแบบไว้ เช่น artifact หรือ registry ไม่ควรอาศัยไฟล์ค้างบน self-hosted runner เป็นช่องทางส่งต่อ เพราะจะทำให้ผลขึ้นกับประวัติของเครื่อง และอาจนำผลผลิตจาก run เก่ามาใช้
ตัวอย่างการแยกงานที่เหมาะสมคือ lint กับ unit test สามารถทำขนานเมื่อไม่พึ่งกัน ส่วน build ที่นโยบายกำหนดให้เกิดหลังสองงานผ่านต้อง needs ทั้งคู่ การเพิ่มขนานมีเป้าหมายลดเวลารอโดยรักษาเงื่อนไข ไม่ใช่ทำให้ทุกขั้นเริ่มพร้อมกันโดยไม่สนใจความถูกต้อง
5.9 อ่าน log โดยแยกอาการ ต้นเหตุ และสภาพแวดล้อม
เมื่อ run ล้ม ควรเริ่มจาก job และ step แรกที่ไม่สำเร็จ อ่านคำสั่งจริง แล้วค้นข้อความผิดพลาดแรกที่เกี่ยวข้อง ข้อความท้ายสุด เช่น Process completed with exit code 1 เป็นผลสรุปของสถานะ ไม่ใช่คำอธิบายต้นเหตุเสมอไป การคัดเฉพาะบรรทัดท้ายไปค้นอาจทำให้ได้คำแนะนำทั่วไปที่ไม่ตรงปัญหา
ข้อมูลจำลองชุดแรกมี npm ci แจ้งว่า lock file ไม่สอดคล้องกับ manifest จุดแก้คือชุด dependency ที่ commit มา ไม่ใช่เพิ่มเวลารอ ชุดที่สองมี npm test พบ expected 200 แต่ได้ 500 จุดตรวจคือพฤติกรรมแอปและ fixture ชุดที่สามยังไม่เริ่ม step ใดเพราะ runner ไม่พร้อม ต้องตรวจการจัดสรร runner หรือข้อจำกัดบริการ การเรียกทั้งสามอย่างว่า “test ไม่ผ่าน” ทำให้เลือกการแก้ผิดชั้น
ตัวอย่างที่ไม่ถูกต้องคือ rerun ซ้ำจนผ่านแล้วปิดเรื่องโดยไม่บันทึกสาเหตุ ถ้าปัญหาเป็น flaky test ผลผ่านครั้งหลังยังไม่อธิบายความไม่แน่นอน ควรเก็บเงื่อนไขของครั้งที่ล้ม เช่น เวลา seed ลำดับ test และทรัพยากรที่ใช้ แล้วลดสาเหตุของความไม่คงที่ การเพิ่ม retry อาจเหมาะกับเครือข่ายบางขั้น แต่ต้องแยกจากการซ่อน bug ที่เกิดเป็นครั้งคราว
อีกตัวอย่างคือเอาค่าลับทั้งหมดพิมพ์ลง log เพื่อค้นปัญหา การตรวจควรใช้ชื่อ key การมีหรือไม่มีค่า ความยาวที่ไม่เปิดเผยเนื้อหา หรือรหัสอ้างอิงที่ไม่ใช่ความลับ แทนการแสดง token จริง แนวทาง GitHub ระบุให้จำกัดสิทธิ์ของ token และระวังข้อมูลที่ไม่น่าเชื่อถือใน workflow (GitHub Docs: Security hardening)
5.10 shell ใน CI กับคำสั่งที่ดูเหมือนสำเร็จ
พฤติกรรม shell ต้องระบุให้ชัด เอกสาร GitHub แยกค่าเริ่มต้นของ shell ที่ไม่ได้ระบุบน Linux ออกจากกรณีเขียน shell: bash ซึ่งเรียก Bash พร้อมตัวเลือกหยุดเมื่อผิดและ pipefail ตามรูปแบบที่เอกสารกำหนด จึงไม่ควรเหมารวมว่าทุก terminal และทุก workflow จัดการ pipeline เหมือนกัน
ตัวอย่างที่ผิดใน Bash ซึ่งไม่ได้เปิด pipefail คือ false | tee report.txt คำสั่งแรกจงใจล้ม แต่คำสั่งสุดท้ายอาจสำเร็จและทำให้สถานะรวมเป็น 0 การเปิด pipefail ช่วยให้สถานะสะท้อนคำสั่งที่ล้มใน pipeline อย่างไรก็ตามการใส่ || true ต่อท้ายยังเป็นการเลือกยอมรับความล้มเหลวอยู่ดี จึงต้องอ่านทั้งบรรทัดและบริบท ไม่ใช่เห็นตัวเลือกตัวเดียวแล้วถือว่าปลอดภัย
ตัวอย่างที่ถูกต้องสำหรับขั้นบังคับคือเรียกคำสั่งตรวจโดยไม่กลบสถานะ และเก็บรายงานด้วยขั้นที่ออกแบบให้ทำหลังการตรวจตามเงื่อนไขที่เหมาะสม เมื่อเพิ่มขั้นเก็บ artifact ต้องยืนยันว่าการเก็บรายงานไม่เปลี่ยนผลตัดสินของ test ส่วนการตั้ง timeout ช่วยกำหนดขอบเขตเวลาที่รอได้ แต่ timeout ไม่ได้บอกเองว่าสาเหตุอยู่ที่แอปหรือเครือข่าย
ท้ายที่สุด required check ต้องผูกกับชื่อและเงื่อนไขที่เกิดจริงบน pull request ตัวอย่างชื่อ check เปลี่ยนตาม matrix หรือ workflow ถูกข้ามเพราะ path filter อาจทำให้กติกาไม่ตรงความตั้งใจ จึงควรทดลอง PR ที่ผ่าน PR ที่ test ล้ม และ PR ที่ไม่เข้า trigger ก่อนใช้เป็นประตูของทีม หลักฐานสามกรณีนี้ให้ภาพรอบด้านกว่าการดู run ปกติเพียงรายการเดียว
กรณีอธิบายต่อเนื่อง: workflow เขียวแต่ PR ที่ผิดยัง merge ได้
กำหนดโจทย์ให้ test ต้องปฏิเสธ /health ที่คืน 500 สมาชิกเปลี่ยนโค้ดให้ผิดโดยตั้งใจบน branch ทดลอง แต่ pull request ยังแสดง check ผ่าน มีสมมติฐานอย่างน้อยสี่ข้อ: ไม่ได้เรียก test, เรียก test คนละไฟล์, ใช้ source คนละ snapshot หรือผล test ถูกยอมรับแม้ล้ม การแก้ต้องตรวจสมมติฐานเหล่านี้ทีละข้อ ไม่เริ่มจากเพิ่ม test ที่ซ้ำโดยยังไม่รู้ว่าของเดิมถูกรันหรือไม่
ขั้นแรกอ่านคำสั่งใน log และจำนวน test ที่ค้นพบ ถ้ามีเพียงข้อความ No tests found แต่คำสั่งถูกกำหนดให้สำเร็จได้ ความเขียวไม่ครอบคลุมข้อกำหนด ขั้นที่สองตรวจ commit ที่ checkout เทียบกับ PR หากทดสอบ branch หลักแทนการเปลี่ยนที่กำลังเสนอ ผลผ่านของโค้ดเดิมไม่ใช่หลักฐานของโค้ดใหม่
ขั้นที่สามตรวจ continue-on-error, เงื่อนไข if และคำสั่งกลบสถานะ ถ้า test คืน 1 แต่ขั้นรวมยังผ่าน ต้องแก้เส้นทางส่งผลตัดสิน ขั้นที่สี่ตรวจว่า branch rule บังคับ check เดียวกับที่รันจริงหรือไม่ เพราะการมี test ถูกต้องแต่ไม่ถูกกำหนดให้เป็นเงื่อนไขก็ยังไม่ป้องกัน merge ตามนโยบายที่ตั้งใจ
หลังแก้ให้ทดสอบ PR ที่ผิดเดิมจนเห็นการปฏิเสธ แล้วเปลี่ยนกลับเป็นพฤติกรรมที่ถูกและยืนยันว่าผ่านทั้ง test และ gate การทดลองสองทิศทางช่วยจับกรณีตั้ง rule เข้มผิดจนปฏิเสธทุกงานด้วย ประตูที่ไม่ให้สิ่งใดผ่านเลยไม่ได้พิสูจน์ว่าตรวจคุณภาพได้ถูกต้อง
ผลสรุปควรระบุว่าข้อผิดพลาดอยู่ที่การค้น test การเลือก snapshot การส่งสถานะ หรือกติกา merge พร้อม run ที่รองรับ การแยกชั้นทำให้เพื่อนร่วมทีมใช้หลักฐานนี้ตรวจ workflow อื่นได้ ไม่จำกัดอยู่กับการแก้ไฟล์เดียว
ปฏิบัติการที่ 5 CI ด้วย GitHub Actions
ปฏิบัติการนี้ประกอบด้วย Lab A–E ทำบน repository ทีม devops-app ผู้รับผิดชอบแต่ละส่วนเป็นดังตาราง เพื่อให้ทุกคนมี pull request ของตนเองใน milestone M4
| ส่วน | ผู้รับผิดชอบ | ผู้ตรวจทาน |
|---|---|---|
Lab A–B pull request เพิ่ม ci.yml | สมาชิกคนที่ 1 | สมาชิกคนที่ 2 |
| Lab C pull request เพิ่ม smoke test และขั้นตรวจ log | สมาชิกคนที่ 2 | สมาชิกคนที่ 3 |
| Lab D.1 ตั้ง required status check | หัวหน้าทีม | — |
| Lab D.2–D.4 pull request ที่ test พังแล้วแก้ | สมาชิกทุกคน คนละหนึ่งรายการ | สมาชิกคนถัดไปในทีม |
| Lab E อ่าน log ของ run | สมาชิกทุกคน | — |
แต่ละ Lab แบ่งเป็นส่วน ฝึกตามตัวอย่าง และส่วน ตัดสินใจเอง (A.4, B.3, C.3, D.4 และ E.2)
ไฟล์ workflow ทุกไฟล์ในบทนี้ผ่านการตรวจด้วย actionlint 1.7.12 และคำสั่งในทุก step ผ่านการรันจำลองบนเครื่องผู้เขียน แต่ยังไม่ได้รันบน GitHub-hosted runner บทความจึงไม่แสดง log จริงจากหน้าเว็บ ผลบนหน้าเว็บที่บรรยายไว้มาจากเอกสารของ GitHub หากหน้าเว็บแสดงผลต่างจากที่บรรยาย ให้บันทึกไว้ใน Learning Log และแจ้งผู้สอน
Lab A: workflow แรก
สถานการณ์: repository ทีมยังไม่มีระบบตรวจอัตโนมัติ ทีมต้องการเห็นก่อนว่า GitHub Actions ทำงานอย่างไรเมื่อมี pull request
เป้าหมาย: สร้าง workflow ที่มี event และ job ตามที่รายวิชากำหนด และเห็น run แรกบน pull request (ส่วนต้นของ LLO 5.1)
ฝึกตามตัวอย่าง
A.1 สร้าง branch และไฟล์ workflow (สมาชิกคนที่ 1)
cd ~/devops-app
git switch main
git pull
git switch -c ci/add-workflow
mkdir -p .github/workflows
nano .github/workflows/ci.ymlพิมพ์เนื้อหาต่อไปนี้ โดยย่อหน้าด้วยช่องว่างครั้งละ 2 ช่อง ห้ามใช้ tab
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Show environment
run: |
echo "Repository: $GITHUB_REPOSITORY"
ls
node --version$GITHUB_REPOSITORY เป็นตัวแปรสภาพแวดล้อมที่ GitHub กำหนดให้บน runner มีค่าเป็นชื่อบัญชีและชื่อ repository step นี้ใช้ตรวจว่า checkout ได้ไฟล์ของ repository มาจริง (ls) และ runner มี Node.js ติดตั้งมาก่อนรุ่นใด
A.2 ตรวจไฟล์ก่อน push (ไม่บังคับ)
หากติดตั้ง actionlint ไว้ในเครื่อง ให้ตรวจไฟล์ก่อน push เพื่อหาข้อผิดพลาดของไวยากรณ์ ไม่พบข้อผิดพลาดคือไม่มีข้อความใดพิมพ์ออกมา
actionlint
echo $?0A.3 push และเปิด pull request
git add .github/workflows/ci.yml
git commit -m "เพิ่ม workflow CI เริ่มต้น"
git push -u origin ci/add-workflowเปิด pull request เข้า main แล้วเลื่อนลงท้ายแท็บ Conversation จะเห็นส่วนของ check ชื่อ CI / test ซึ่งเริ่มจากสถานะกำลังรันแล้วเปลี่ยนเป็นผ่าน คลิก Details เพื่อเปิดหน้า run และคลิก step Show environment เพื่อดู log ยังไม่ต้อง merge เพราะ Lab B จะเพิ่ม commit ใน pull request เดียวกัน
ตัดสินใจเอง A.4: ไฟล์ workflow ต่อไปนี้มีข้อผิดพลาด 3 จุด หาและแก้ทั้งหมดโดยใช้ actionlint หรืออ่านจากเอกสาร Understanding GitHub Actions และ Triggering a workflow ทำในโฟลเดอร์ทดลองที่ไม่ใช่ repository ทีม
name: CI
on:
pushh:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
run-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Show environment
run: echo "Repository: $GITHUB_REPOSITORY"ผลลัพธ์ที่คาดหวัง A.4: ไฟล์ที่แก้แล้วผ่าน actionlint โดยไม่มีข้อความ และคำอธิบายว่าแต่ละจุดผิดเพราะเหตุใด
จุดตรวจ A: pull request จาก ci/add-workflow มี check CI / test ที่รันแล้วอย่างน้อยหนึ่งครั้ง และ log ของ step Show environment แสดงชื่อ repository และรายการไฟล์
ปัญหาที่พบบ่อย
| อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|
| เปิด pull request แล้วไม่มี check ปรากฏ | ไฟล์ไม่ได้อยู่ใน .github/workflows หรือสะกดชื่อโฟลเดอร์ผิด | ตรวจด้วย ls .github/workflows ต้องพบ ci.yml |
| แท็บ Actions แจ้งว่าไฟล์ workflow ไม่ถูกต้อง | ไวยากรณ์ YAML ผิด เช่น tab หรือย่อหน้าไม่ตรง | ตรวจด้วย actionlint หรือเทียบการย่อหน้ากับตัวอย่างทีละบรรทัด |
node --version แสดงรุ่นที่ไม่ใช่ 22 | ยังไม่ได้ติดตั้ง Node.js รุ่นที่ต้องการด้วย actions/setup-node | เป็นสิ่งที่คาดหวังใน Lab A Lab B จะแก้ |
| ไม่เห็นปุ่ม Run workflow ในแท็บ Actions | workflow_dispatch ใช้ได้เมื่อไฟล์ workflow อยู่บน branch หลักแล้ว | รอจน merge ใน Lab B |
เฉลยจะเปิดในชั้นเรียนหลังทุกคนทำเสร็จ
Lab B: เพิ่ม npm ci และ npm test
สถานการณ์: workflow รันได้แล้วแต่ยังไม่ได้ตรวจอะไร ทีมต้องให้ CI ติดตั้ง dependency และรัน test ด้วยคำสั่งเดียวกับที่ตรวจจาก clone ใหม่ในบทที่ 4
เป้าหมาย: กำหนดค่า workflow ให้รัน npm ci และ npm test แล้ว merge เข้า main (LLO 5.1)
ฝึกตามตัวอย่าง
B.1 แทน step Show environment ด้วย step ของ Node.js (สมาชิกคนที่ 1 บน branch เดิม)
แก้ส่วน steps: ของ .github/workflows/ci.yml ให้เป็นดังนี้
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: '22.x'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm testก่อน push ให้รันคำสั่งเดียวกับ step บนเครื่องของตนจากโฟลเดอร์ที่ clone ใหม่ (ทำแบบเดียวกับ Lab D.3 ของบทที่ 4) เพื่อยืนยันว่าคำสั่งผ่าน
bash -e -c 'npm ci && npm test' > /tmp/ci-local.log 2>&1
echo $?
tail -8 /tmp/ci-local.log0
# tests 3
# suites 0
# pass 3
# fail 0
# cancelled 0
# skipped 0
# todo 0B.2 push แล้วดู run ใหม่บน pull request เดิม
git add .github/workflows/ci.yml
git commit -m "ให้ CI ติดตั้ง dependency และรัน test"
git pushpull request จะแสดง run ใหม่ของ commit ล่าสุด เปิด log ของ step Run tests แล้วเทียบกับผลในเครื่องจาก B.1 เมื่อ run ผ่าน สมาชิกคนที่ 2 ตรวจทานไฟล์ ci.yml ในแท็บ Files changed แล้วกด Approve จากนั้น merge ด้วย Create a merge commit การ merge เข้า main จะทำให้ workflow รันอีกครั้งจาก event push ตรวจในแท็บ Actions ว่า run ของ main เป็นสีเขียว
ตัดสินใจเอง B.3: ตัวอย่างใน GitHub Docs: Building and testing Node.js ใช้ node-version: '20.x' ส่วนรายวิชาใช้ '22.x' ค้นจากเอกสารทางการของ Node.js เรื่องรอบการสนับสนุนของแต่ละรุ่น แล้วตัดสินใจว่าทีมควรใช้รุ่นใด เขียนเหตุผล 2–3 บรรทัดในคำอธิบายของ pull request
ผลลัพธ์ที่คาดหวัง B.3: เหตุผลที่โยงกับ (1) รุ่นที่ใช้พัฒนาและทดสอบแอป และ (2) สถานะการสนับสนุนของรุ่นนั้น พร้อมลิงก์แหล่งข้อมูล
จุดตรวจ B: main มี .github/workflows/ci.yml ที่รัน npm ci และ npm test และ run ล่าสุดของ main เป็นสีเขียว
ปัญหาที่พบบ่อย
| อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|
step Install dependencies ล้มด้วย EUSAGE | main ยังไม่มี package-lock.json (ยังไม่ผ่าน M3) | ทำบทที่ 4 Lab A ให้เสร็จก่อน หรือ merge branch เฉลยตามที่ผู้สอนกำหนด |
step Run tests ล้มด้วย Missing script: "test" | main ยังไม่มี script test | ทำบทที่ 4 Lab B ให้เสร็จก่อน |
| ผ่านในเครื่องแต่ล้มบน runner | รุ่น Node.js ต่างกัน หรือรันในเครื่องจากโฟลเดอร์ที่มี node_modules เดิม | ตรวจรุ่นใน log ของ Setup Node.js และรันในเครื่องจาก clone ใหม่ |
| run ไม่เกิดหลัง merge | event push จำกัดเฉพาะ main แต่ไฟล์ workflow บน main มีไวยากรณ์ผิด | เปิดแท็บ Actions ดูข้อความแจ้งความผิดพลาดของไฟล์ |
เฉลยจะเปิดในชั้นเรียนหลังทุกคนทำเสร็จ
Lab C: smoke test และขั้นตรวจ log ด้วย check-errors.sh
สถานการณ์: test ตรวจ endpoint ผ่านโค้ดใน test โดยตรง แต่ยังไม่มีขั้นใดยืนยันว่าแอปเริ่มทำงานด้วย npm start ได้จริง และ log ของแอปไม่มี ERROR
เป้าหมาย: เพิ่ม step ที่รันแอปจริงแล้วเรียก /health (smoke test) และใช้ check-errors.sh จากบทที่ 1 ตรวจ log ของแอป
ฝึกตามตัวอย่าง
C.1 เพิ่มสอง step ท้าย job (สมาชิกคนที่ 2)
cd ~/devops-app
git switch main
git pull
git switch -c ci/add-smoke-test
nano .github/workflows/ci.ymlเพิ่มต่อจาก step Run tests
- name: Smoke test
run: |
npm start > app.log 2>&1 &
sleep 2
curl -fsS http://localhost:3000/health
- name: Check log for ERROR
run: bash scripts/check-errors.sh app.log| บรรทัด | หน้าที่ |
|---|---|
npm start > app.log 2>&1 & | รันแอปเบื้องหลัง และเก็บทั้ง output และ error ของแอปลง app.log |
sleep 2 | รอให้แอปเริ่มฟังพอร์ต |
curl -fsS http://localhost:3000/health | เรียก /health และล้มถ้าเชื่อมต่อไม่ได้หรือได้ HTTP 400 ขึ้นไป |
bash scripts/check-errors.sh app.log | ล้มถ้า app.log มีบรรทัดที่มีคำว่า ERROR หรือไม่พบไฟล์ |
ใช้ bash scripts/... แทน ./scripts/... เพื่อไม่ต้องพึ่งสิทธิ์ execute ของไฟล์ใน repository
C.2 จำลองในเครื่องแล้ว push
npm ci
bash -e -c '
npm start > app.log 2>&1 &
sleep 2
curl -fsS http://localhost:3000/health'
echo
bash scripts/check-errors.sh app.log
echo $?{"status":"ok","version":"0.2.0"}
พบ ERROR 0 บรรทัดใน app.log
0หยุดแอปที่ยังรันเบื้องหลังด้วย kill %1 หรือ pkill -f "node src/server.js" แล้วลบ app.log ที่สร้างในเครื่อง (rm app.log) เพราะไฟล์ .log ถูกกันด้วย .gitignore อยู่แล้วแต่ไม่ควรค้างในโฟลเดอร์ทำงาน จากนั้น commit และเปิด pull request
git add .github/workflows/ci.yml
git commit -m "เพิ่ม smoke test และขั้นตรวจ log"
git push -u origin ci/add-smoke-testตัดสินใจเอง C.3: แอปปัจจุบันไม่ได้เขียนบรรทัดที่มีคำว่า ERROR ลง log ขั้น Check log for ERROR จึงผ่านเสมอ ออกแบบวิธีพิสูจน์บน pull request นี้ว่าขั้นตรวจทำงานจริง คือทำให้ run ล้มที่ step Check log for ERROR โดยไม่แก้โค้ดของแอป แล้วย้อนการเปลี่ยนแปลงให้ run กลับเป็นสีเขียวก่อน merge
ผลลัพธ์ที่คาดหวัง C.3: ประวัติของ pull request มี run ที่ล้มที่ step Check log for ERROR (ไม่ใช่ step อื่น) ตามด้วย run สีเขียว
จุดตรวจ C: main มี step Smoke test และ Check log for ERROR และ run ล่าสุดของ main เป็นสีเขียว
ปัญหาที่พบบ่อย
| อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|
Smoke test ล้มด้วย curl: (7) Failed to connect | แอปยังเริ่มไม่ทัน หรือแอปล้มตอนเริ่ม | เปิด log ของ step แล้วดูว่า app.log มีอะไร อาจเพิ่ม cat app.log ชั่วคราว หรือเพิ่มเวลา sleep |
Check log for ERROR ล้มด้วย ไม่พบไฟล์ app.log | step Smoke test ไม่ได้สร้างไฟล์ เช่น พิมพ์ชื่อไฟล์ต่างกัน | ตรวจชื่อไฟล์ทั้งสอง step ให้ตรงกัน |
จำลองในเครื่องแล้ว EADDRINUSE | แอปจากการจำลองครั้งก่อนยังรันอยู่ | หยุดด้วย pkill -f "node src/server.js" |
Permission denied เมื่อเรียกสคริปต์ | ใช้ ./scripts/check-errors.sh และไฟล์ไม่มีสิทธิ์ execute | ใช้ bash scripts/check-errors.sh ตามตัวอย่าง |
เฉลยจะเปิดในชั้นเรียนหลังทุกคนทำเสร็จ
Lab D: required status check และ pull request ที่ test พัง
สถานการณ์: CI รันบนทุก pull request แล้ว แต่สมาชิกยังกด merge ได้แม้ run เป็นสีแดง
เป้าหมาย: ตั้ง required status check แล้วให้สมาชิกทุกคนเห็นด้วยตนเองว่า pull request ที่ test ล้มถูกกั้น และแก้จนผ่าน (LLO 5.2)
ฝึกตามตัวอย่าง
D.1 ตั้ง required status check (หัวหน้าทีม)
- เปิด Settings → Branches ของ repository แล้วแก้กฎของ
mainที่สร้างในบทที่ 3 (หรือ Settings → Rules → Rulesets หาก repository ใช้ ruleset) - เปิด Require status checks to pass before merging
- ค้นหาและเลือก check ของ job
testจาก workflow CI - บันทึก แล้วถ่ายภาพหน้านี้เป็นหลักฐานงานข้อ 1
ชื่อเมนูอ้างอิงจาก GitHub Docs: About protected branches และอาจเปลี่ยนตามรุ่นของบริการ
D.2 เพิ่ม test ของตนเองโดยตั้งใจให้ผิด (สมาชิกทุกคน)
แต่ละคนรับผิดชอบ endpoint คนละรายการเพื่อไม่ให้ไฟล์ชนกัน: สมาชิกคนที่ 1 /api/items/1 (Linux) สมาชิกคนที่ 2 /api/items/2 (Git) สมาชิกคนที่ 3 /api/items/3 (GitHub Actions) ตัวอย่างต่อไปนี้เป็นของสมาชิกคนที่ 1 ชื่อ somchai
cd ~/devops-app
git switch main
git pull
npm ci
git switch -c test/add-somchai
nano test/items-somchai.test.jsconst test = require('node:test');
const assert = require('node:assert');
const app = require('../src/app');
let server;
let base;
test.before(async () => {
server = app.listen(0);
await new Promise((resolve) => server.once('listening', resolve));
base = `http://127.0.0.1:${server.address().port}`;
});
test.after(() => server.close());
test('GET /api/items/1 returns Linux', async () => {
const res = await fetch(`${base}/api/items/1`);
const body = await res.json();
assert.strictEqual(body.name, 'linux');
});ค่าที่คาดหวัง 'linux' (ตัวพิมพ์เล็ก) ผิดโดยตั้งใจ เพราะแอปส่งกลับ 'Linux' ไฟล์นี้ใช้โครงเดียวกับ test/app.test.js คือเปิดแอปบนพอร์ตว่าง (listen(0)) ก่อน test และปิดหลัง test push โดยไม่แก้
git add test/items-somchai.test.js
git commit -m "เพิ่ม test ของ /api/items/1"
git push -u origin test/add-somchaiเปิด pull request เข้า main ภายในไม่กี่นาที check จะเป็นสีแดงและปุ่ม merge ถูกกั้น ผลเดียวกันเมื่อรันในเครื่อง (ทดสอบแล้ว)
not ok 4 - GET /api/items/1 returns Linux
location: '/home/ชื่อผู้ใช้/devops-app/test/items-somchai.test.js:16:1'
expected: 'linux'
actual: 'Linux'
...
# pass 3
# fail 1D.3 แก้ test แล้ว push ซ้ำบน branch เดิม
sed -i "s/'linux'/'Linux'/" test/items-somchai.test.js
npm test
git commit -am "แก้ค่าที่คาดหวังของ test ให้ตรงกับข้อมูลจริง"
git push# tests 4
# suites 0
# pass 4
# fail 0run ใหม่จะเป็นสีเขียว สมาชิกคนถัดไปในทีมตรวจทานแล้วกด Approve จากนั้นจึง merge ได้ เมื่อสมาชิกทุกคน merge ครบ main จะมี test อย่างน้อย 6 รายการ
ตัดสินใจเอง D.4: ก่อน merge ให้เพิ่ม test อีก 1 รายการในไฟล์ของตนเอง สำหรับพฤติกรรมของแอปที่ยังไม่มี test ใดครอบคลุม โดยเลือกเองจากการอ่าน src/app.js สมาชิกในทีมต้องเลือกพฤติกรรมไม่ซ้ำกัน และ test ต้องผ่านบน CI
ผลลัพธ์ที่คาดหวัง D.4: test ที่เพิ่มผ่านในเครื่องและบน CI และชื่อ test บอกพฤติกรรมที่ตรวจได้ชัดเจน
จุดตรวจ D: branch protection ของ main มี required status check · pull request ของสมาชิกทุกคนมี run สีแดงตามด้วยสีเขียว และ merge แล้ว
ปัญหาที่พบบ่อย
| อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|
หน้าตั้งค่าค้นหา check test ไม่พบ | workflow ยังไม่เคยรันใน repository | ทำ Lab B ให้ merge และมี run บน main ก่อน |
| run เป็นสีแดงแต่ยังกด merge ได้ | ไม่ได้บันทึกกฎ หรือผู้กดเป็นผู้ดูแลที่ได้รับยกเว้นจากกฎ | ตรวจกฎอีกครั้ง และตรวจตัวเลือกที่ให้ผู้ดูแลข้ามกฎได้ |
test ใหม่ล้มด้วย EADDRINUSE | เขียน app.listen(3000) แทน app.listen(0) | ใช้ listen(0) ให้ระบบเลือกพอร์ตว่างตามแม่แบบ |
| conflict ที่ไฟล์ test | สมาชิกสองคนใช้ชื่อไฟล์เดียวกัน | ใช้ชื่อไฟล์ items-ชื่อของตน.test.js |
เฉลยจะเปิดในชั้นเรียนหลังทุกคนทำเสร็จ
Lab E: อ่าน log ของ run
สถานการณ์: สมาชิกทุกคนมี run สีแดงของตนเองจาก Lab D ซึ่งเป็นวัตถุดิบของใบงานแผนภาพ pipeline
เป้าหมาย: อ่าน log ของ run ที่ล้มอย่างเป็นลำดับ และบันทึกข้อมูลที่ใช้ในใบงาน CLO1 (LLO 5.3)
ฝึกตามตัวอย่าง
E.1 อ่าน run สีแดงของตนเองตามลำดับในหัวข้อ 5.6 (สมาชิกทุกคน)
- เปิด pull request ของตนเอง แท็บ Checks หรือแท็บ Actions แล้วเลือก run ที่ล้ม
- ระบุ event ที่ทำให้ run นี้เกิด (แสดงที่หัวหน้า run) และ commit ที่ถูกตรวจ
- ระบุ step แรกที่ล้มและ step ที่ถูกข้าม
- เปิด log ของ step ที่ล้ม หาบรรทัด
not ok,location,expected,actualและบรรทัดท้ายที่บอก exit status - เทียบกับผลเมื่อรันในเครื่องจาก D.2
ตัดสินใจเอง E.2 (LLO 5.3): เติมตารางต่อไปนี้จาก run สีแดงของตนเอง โดยคัดลอกบรรทัดจาก log จริงเป็นหลักฐาน แล้วใช้ตารางนี้วาดแผนภาพในใบงานงานข้อ 3
| ข้อมูล | ค่าจาก run ของตนเอง | บรรทัดใน log ที่ใช้ยืนยัน |
|---|---|---|
| event | ||
| workflow และ job | ||
| step ที่ผ่าน | ||
| step แรกที่ล้ม | ||
| step ที่ถูกข้าม | ||
| สาเหตุ | ||
| exit status |
ผลลัพธ์ที่คาดหวัง E.2: ตารางครบทุกแถว และทุกแถวที่อ้าง log มีบรรทัดที่คัดลอกมาจริง
จุดตรวจ E: สมาชิกทุกคนมีตาราง E.2 ของตนเอง
ปัญหาที่พบบ่อย
| อาการ | สาเหตุ | วิธีแก้ |
|---|---|---|
| หา run สีแดงไม่พบ | push ครั้งแรกก็ผ่านแล้ว (แก้ test ก่อน push) | ทำ D.2 ใหม่บน branch ใหม่ หรือใช้ run สีแดงจาก Lab C.3 |
| log ยาวมากหาสาเหตุไม่เจอ | อ่านจากบนลงล่าง | เริ่มจากท้าย log ของ step ที่ล้มแล้วไล่ขึ้น หรือใช้ช่องค้นหาของหน้า log |
| ตอบว่าสาเหตุคือ step ที่ถูกข้าม | สับสนระหว่างล้มกับถูกข้าม | step ที่ถูกข้ามไม่ได้รัน สาเหตุอยู่ที่ step แรกที่ล้มเสมอ |
เฉลยจะเปิดในชั้นเรียนหลังทุกคนทำเสร็จ
เก็บกวาดเมื่อจบปฏิบัติการ
- หยุดแอปที่ยังรันเบื้องหลังจากการจำลองในเครื่องด้วย
pkill -f "node src/server.js"และลบapp.logกับ/tmp/ci-local.log - ลบ branch ที่ merge แล้วในเครื่องด้วย
git branch -d ชื่อbranchและสั่งgit fetch --prune - ตรวจในแท็บ Actions ว่าไม่มี run ค้างสถานะกำลังรัน และ run ล่าสุดของ
mainเป็นสีเขียว - ลบโฟลเดอร์ทดลองของ Lab A.4
สรุป
Continuous Integration คือแนวปฏิบัติที่สมาชิกทุกคน merge งานเข้า codebase ร่วมกันบ่อยครั้ง และทุกครั้งถูกตรวจด้วย build อัตโนมัติที่มี test อยู่ในตัว GitHub Actions เป็นเครื่องมือที่ทำให้แนวปฏิบัตินี้เกิดขึ้นเองเมื่อมี event เช่น push หรือ pull request โดย workflow ประกอบด้วย job ที่รันบน runner และ job ประกอบด้วย step ที่ทำงานตามลำดับ
runner ของแต่ละ run เป็นเครื่องใหม่ที่ไม่มีโค้ดและไม่มี dependency workflow จึงต้องเริ่มจาก checkout ตามด้วยการติดตั้งด้วย npm ci ซึ่งเป็นเหตุผลที่บทที่ 4 ทำให้การติดตั้งจาก clone ใหม่ทำซ้ำได้ก่อน CI ตัดสินผลจาก exit status ของคำสั่ง step ที่จบด้วยค่าที่ไม่ใช่ 0 ทำให้ job ล้มและ step ที่เหลือถูกข้าม
required status check เปลี่ยนผลของ CI ให้เป็นด่านก่อน merge pull request ที่ test ไม่ผ่านจึงเข้า main ไม่ได้ การอ่าน log ต้องเริ่มจาก step แรกที่ล้มและบรรทัดที่บอกสาเหตุ แล้วทำซ้ำในเครื่องก่อนแก้
สิ่งที่ส่งต่อไปยังโครงงานและ Capstone
| สิ่งที่ได้ | นำไปใช้ที่ |
|---|---|
.github/workflows/ci.yml และ M4 | บทที่ 8 เพิ่ม workflow CD · บทที่ 10 เพิ่มขั้นสแกนช่องโหว่ · บทที่ 12 ใช้เป็นส่วนต้นของ pipeline ครบสาย |
| required status check | ทุก pull request หลังจากนี้ต้องผ่าน CI · วันนำเสนอ Capstone ทีมต้อง push แก้งานผ่าน CI ต่อหน้าผู้สอน |
| test ของสมาชิกแต่ละคน | เพิ่มความครอบคลุมของด่าน CI ตลอดภาคการศึกษา |
| ใบงานแผนภาพ pipeline CLO1 ชิ้นที่ 2 | ต่อยอดเป็นแผนภาพ CI/CD ครบสายในใบงานบทที่ 8 และใช้เตรียมสอบปากเปล่าบทที่ 12 |
| Learning Log ครั้งที่ 5 | portfolio ที่ส่งในบทที่ 13 |
สรุปคำสั่งและไวยากรณ์ในบทนี้
| คำสั่งหรือไวยากรณ์ | หน้าที่ |
|---|---|
.github/workflows/ชื่อ.yml | ตำแหน่งไฟล์ workflow |
on: push / pull_request / workflow_dispatch | กำหนด event ที่ทำให้ workflow ทำงาน |
branches: [main] | จำกัด event เฉพาะ branch ที่ระบุ |
runs-on: ubuntu-latest | เลือก runner |
uses: actions/checkout@v6 | ดึงโค้ดของ repository ลง runner |
uses: actions/setup-node@v7 พร้อม node-version | ติดตั้ง Node.js รุ่นที่ระบุ |
run: คำสั่ง และ run: | | รันคำสั่ง shell หนึ่งบรรทัดหรือหลายบรรทัด |
actionlint | ตรวจไวยากรณ์ไฟล์ workflow ในเครื่อง |
curl -fsS URL | เรียก URL และจบด้วย exit status ที่ไม่ใช่ 0 เมื่อได้ HTTP 400 ขึ้นไป |
npm start > app.log 2>&1 & | รันแอปเบื้องหลังและเก็บ log ลงไฟล์ |
bash -e -c '...' | จำลองการรัน step ในเครื่องโดยหยุดเมื่อคำสั่งใดล้ม |
pkill -f "node src/server.js" | หยุดแอปที่รันเบื้องหลัง |
แบบฝึกหัดท้ายบท
ตอนที่ 1 ฝึกทบทวน
ชุด FP10306343-P05 จำนวน 12 ข้อ ข้อละ 1 คะแนน รวม 12 คะแนน ครอบคลุม YAML ของ workflow การเลือก event ลำดับ step การตัดสินผลจาก exit status required status check และการอ่าน log ของ run ที่ล้ม
เข้าสู่ระบบห้องเรียนด้วยบัญชีที่ลงทะเบียนในรายวิชา 10306343 แล้วตอบคำถามให้ครบ ระบบตรวจคำตอบและแสดงคำอธิบายเพื่อทบทวน เมื่อทำครบและส่งผล ระบบจะบันทึกคะแนนไว้กับบัญชีของผู้เรียน สามารถทำซ้ำเพื่อทบทวนได้ ส่วนคะแนนที่นำไปรวมในรายวิชาเป็นไปตามเกณฑ์และกำหนดส่งในสมุดคะแนน
ตอนที่ 2 แบบฝึกหัดเขียนตอบ (5 ข้อ)
ชุด FW10306343-P05 จำนวน 5 ข้อ ข้อละ 4 คะแนน รวม 20 คะแนน เป็นงานเขียนตอบที่ดัดแปลงจากแบบฝึกหัดเดิมด้วยสถานการณ์ใหม่ ครอบคลุมตัวกรอง branch ของ event push และ pull_request การตัดสินผลของ step จาก exit status การเขียน step ตรวจความพร้อมของบริการ การอ่าน log ของ run ที่ล้ม และการวิเคราะห์คำสั่งที่ต่อด้วยท่อซึ่งกลบผลของ test
เข้าสู่ระบบห้องเรียนด้วยบัญชีที่ลงทะเบียนในรายวิชา 10306343 แล้วเปิดรอบส่งงานจากลิงก์ด้านล่าง แต่ละข้อบอกสิ่งที่ต้องส่งและเกณฑ์ที่ใช้ตรวจ พิมพ์คำตอบหรือแนบไฟล์ได้สูงสุด 8 ไฟล์ ผู้สอนตรวจตามเกณฑ์และแจ้งผลรายเกณฑ์ คะแนนที่นำไปรวมคือครั้งที่ดีที่สุดที่ตรวจแล้วและส่งก่อนกำหนดในสมุดคะแนน
ใช้ประกอบการตรวจตามรูบริก ไม่เปิดให้นักศึกษา
แบบฝึกหัดเพิ่มเติมสำหรับอภิปรายและเขียนตอบ
แบบฝึกหัดแบ่งเป็นสามกลุ่ม ได้แก่ ความเข้าใจ การประยุกต์ และการวิเคราะห์ปัญหา แต่ละข้อระบุผลลัพธ์การเรียนรู้ที่ฝึก
ก. ความเข้าใจ
- ก1 (LLO 5.3) วาดแผนภาพ event → workflow → job → step ของ
ci.ymlฉบับสุดท้ายของทีม และระบุว่าส่วนใดรันบนเครื่องเดียวกัน - ก2 (LLO 5.1) อธิบายความต่างของ event
pushที่ระบุbranches: [main]กับpull_requestที่ระบุbranches: [main]ว่าแต่ละแบบทำงานเมื่อใด - ก3 (LLO 5.2) อธิบายว่า CI รู้ได้อย่างไรว่า step
Run testsล้ม โดยโยงกับเรื่อง exit status ในบทที่ 1
ข. การประยุกต์
- ข1 (LLO 5.1) เขียน step เพิ่มต่อท้าย job ที่เรียก
/api/itemsแล้วล้มเมื่อแอปตอบรหัส HTTP 400 ขึ้นไป - ข2 (LLO 5.1) ทีมต้องการกดรัน workflow ด้วยมือจากแท็บ Actions ได้ด้วย ระบุสิ่งที่ต้องเพิ่มในส่วน
on:และเงื่อนไขที่ทำให้ปุ่มปรากฏ - ข3 (LLO 5.3) จาก log ต่อไปนี้ ระบุ step ที่ล้ม สาเหตุ และไฟล์กับบรรทัดที่ต้องเปิดดู
not ok 5 - GET /api/items/2 returns Git
location: '/home/runner/work/devops-app/devops-app/test/items-somsri.test.js:16:1'
expected: 'git'
actual: 'Git'ค. การวิเคราะห์ปัญหา
- ค1 (LLO 5.2) สมาชิกคนหนึ่งเปลี่ยน step เป็น
run: npm test | tee test.logเพื่อเก็บผลไว้ หลังจากนั้นพบว่า pull request ที่มี test ล้มกลับได้ run สีเขียว วิเคราะห์สาเหตุและเสนอวิธีแก้ - ค2 (LLO 5.1) workflow รันผ่านบน pull request แต่หลัง merge แล้วไม่มี run ของ
mainเกิดขึ้นเลย ส่วนon:ของไฟล์เป็นpush: branches: [master]วิเคราะห์สาเหตุ - ค3 (LLO 5.3) ทีมเสนอแยก workflow เป็นสอง job คือ
install(รันnpm ci) และtest(รันnpm test) เพื่อให้แผนภาพดูเป็นระเบียบ วิเคราะห์ว่า jobtestจะทำงานได้หรือไม่ เพราะเหตุใด และถ้าต้องการแยกจริงต้องทำอย่างไร
เฉลยจะเปิดในชั้นเรียนหลังทุกคนทำเสร็จ
การศึกษาค้นคว้าเพิ่มเติม
- อ่านบทความ Continuous Integration ของ Martin Fowler หัวข้อ Practices of Continuous Integration แล้วเลือกแนวปฏิบัติ 1 ข้อที่ทีมยังไม่ได้ทำ บันทึกใน Learning Log
- อ่าน GitHub Docs: Understanding GitHub Actions และ Building and testing Node.js เพื่อเขียนใบงานแผนภาพ pipeline (งานข้อ 3)
- ทบทวนด้วยบทความเดิม เริ่มต้น CI ด้วย GitHub Actions: คู่มือลงมือทำจริงใน 6 ขั้นตอน
- ทำแบบฝึกหัดท้ายบทให้ครบ และเขียน Learning Log ครั้งที่ 5
- (ไม่บังคับ) ศึกษา Jenkins Declarative Pipeline จาก Jenkins: Pipeline Syntax แล้วลองเขียน pipeline เดียวกับ
ci.ymlเป็น Jenkinsfile เพื่อเทียบแนวคิด ซึ่งเป็นหัวข้อหนึ่งที่เลือกทำเป็นงานต่อยอดได้ตั้งแต่บทที่ 6
เอกสารอ้างอิง
แหล่งข้อมูลเพิ่มเติมสำหรับตัวอย่างและกรณีอธิบาย
ตรวจเอกสารออนไลน์เมื่อ 23 ตุลาคม 2569 ตัวเลขและเหตุการณ์ที่ระบุว่าสมมติใช้เพื่ออธิบายกลไก ไม่ใช่ผลทดลองหรือสถิติขององค์กรจริง
เอกสารอ้างอิงของเนื้อหาเดิม
- Fowler, M. (2024, January 18). Continuous integration (original work published 2000). https://martinfowler.com/articles/continuousIntegration.html
- GitHub. (n.d.). About protected branches. GitHub Docs. https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches
- GitHub. (n.d.). Building and testing Node.js. GitHub Docs. https://docs.github.com/en/actions/tutorials/build-and-test-code/nodejs
- GitHub. (n.d.). Triggering a workflow. GitHub Docs. https://docs.github.com/en/actions/how-tos/write-workflows/choose-when-workflows-run/trigger-a-workflow
- GitHub. (n.d.). Understanding GitHub Actions. GitHub Docs. https://docs.github.com/en/actions/get-started/understand-github-actions
- Jenkins project. (n.d.). Pipeline syntax. Jenkins User Documentation. https://www.jenkins.io/doc/book/pipeline/syntax/
- sarachai.com. (n.d.). เริ่มต้น CI ด้วย GitHub Actions: คู่มือลงมือทำจริงใน 6 ขั้นตอน. /blog/github-actions-ci-6-steps