နားလည်ထားရမယ့် အချက်
Crash ဖြစ်တဲ့ pipeline ဟာ တကယ်တော့ pipeline ကောင်းတစ်ခုပါ။ သူက ရပ်သွားတယ်၊ အနီရောင်ပြတယ်၊ ပြီးတော့ code မကောင်းတာ ရှေ့ဆက်မသွားခင် တစ်ယောက်ယောက်က ပြင်လိုက်တယ်။ အန္တရာယ်အရှိဆုံးကတော့ နောက်တစ်မျိုးပါ — workflow က အဆုံးထိ run သွားတယ်၊ လုပ်လို့မဖြစ်တဲ့ အလုပ်တစ်ခုကို လုပ်ပစ်လိုက်တယ်၊ ပြီးတော့ ရှာစရာ error message တစ်ကြောင်းမှ မကျန်ခဲ့ဘူး။ ဒီ exercise က အဲဒီ bug class အကြောင်းပါ — YAML က လုံးဝမှန်တယ်၊ action တိုင်း version pin လုပ်ထားတယ်၊ step တိုင်းက သူ့ဟာသူ အောင်မြင်တယ်၊ ဒါပေမဲ့ pipeline က ပျက်နေတဲ့ code ကို ship လုပ်ပစ်လိုက်တယ်။
ဒီ bug class ဖြစ်ရတဲ့ အကြောင်းရင်းက GitHub Actions မှာ လူအများစု တွဲရောပြီး မြင်တတ်တဲ့ အချက်နှစ်ခုကို သီးခြားစီ ခွဲထားလို့ပါ — ordering နဲ့ gating။ needs: က ordering ကို ထိန်းတယ်၊ job A ပြီးမှ job B စတယ် ဆိုတာမျိုးပါ။ ဒါပေမဲ့ A fail ဖြစ်တဲ့အခါ B ဆက် run သင့်၊ မသင့်ဆိုတာကို ဆုံးဖြတ်တာက if: expression ပါ။ GitHub က implicit success() check ကို ထည့်ပေးတာဟာ သင့် if: ထဲမှာ status function ကိုယ်တိုင် မပါတဲ့အခါမှသာ ဖြစ်တယ်။ always(), failure(), cancelled() တစ်ခုခုကို အဲဒီ expression ထဲ ရေးလိုက်တာနဲ့ implicit success check က ပျောက်သွားပြီး ဆုံးဖြတ်ချက်တစ်ခုလုံး သင့်လက်ထဲ ရောက်သွားတယ်။ ဒါကြောင့် if: always() && github.ref == 'refs/heads/main' ဟာ 'main မှာပဲ run မယ်' လို့ ဖတ်ရပေမယ့် တကယ့်အဓိပ္ပာယ်က 'main မှာ၊ အပေါ်က job တွေ ဘာဖြစ်ဖြစ် run မယ်' ဖြစ်နေတယ်။ လူတွေက fail သွားတဲ့ run ကနေ log ထုတ်ကြည့်ချင်လို့ debug လုပ်ရင်း ရေးထည့်ပြီး ပြန်မဖြုတ်ဖြစ်တာ အများဆုံးပါ။
ဒါကို ဖမ်းနိုင်တဲ့ အလေ့အထကတော့ workflow ကို အပေါ်ကနေအောက် စာဖတ်သလို မဖတ်ဘဲ graph တစ်ခုအဖြစ် ဖတ်ဖို့ပါ။ job တွေကို box နဲ့ဆွဲ၊ needs: မြှားတွေ ဆွဲ၊ ပြီးရင် production ကို ထိတဲ့ job တိုင်းအတွက် မေးခွန်းတစ်ခုတည်းကိုပဲ မေးပါ — ဒီ job skip ဖြစ်သွားဖို့ ဘာက fail ဖြစ်ရမလဲ။ ရိုးသားစွာဖြေရတဲ့ အဖြေက 'ဘာမှ မလိုဘူး' ဆိုရင် သင့်မှာ gate မရှိတော့ဘူး — အချိန်ဇယားတစ်ခုပဲ ကျန်တော့တယ်။
WHAT ACTUALLY GATES THE DEPLOY
------------------------------
------------------------------
push to main
|
+--------+---------+
| |
[ test ] [ build ]
npm test npm run build
MAY FAIL uploads artifact 'dist'
| |
+--------+---------+
|
deploy declares:
needs: [test, build] <-- ordering only
if: always() && ref == main <-- the real gate
|
[ deploy ] <-- RUNS EVEN WHEN test FAILED
|
production
ordering answers WHEN a job starts.
the if: expression answers WHETHER it runs at all.လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
ဒါကို ဘယ်သူ့ကိုမှ ထိခိုက်လို့မရတဲ့ နေရာမှာ စမ်းပါ။ workflow ကို scratch repository တစ်ခုရဲ့ .github/workflows/ci.yml အဖြစ် ကူးထည့်ပြီး၊ ရိုးရှင်းတဲ့ npm test script တစ်ခု ထည့်၊ main ကို တစ်ခါ push လိုက်ပါ — နောက်မှ နှိုင်းယှဉ်ဖို့ green baseline run တစ်ခု ရသွားပါလိမ့်မယ်။ အခု တမင် ဖျက်ကြည့်ပါ — assertion တစ်ခုကို မှားအောင်ပြင်၊ test suite တကယ် fail အောင်လုပ်၊ commit လုပ်ပြီး ထပ် push ပါ။
Actions tab မှာ အဲဒီ run ကို ဖွင့်ပြီး log list ထက် graph view ကို ကြည့်ပါ။ graph က needs: edge တွေကို သင့်အတွက် ဆွဲပြပေးတဲ့အတွက် ဘယ် job က ဘယ်ဟာကို စောင့်နေလဲဆိုတာ တစ်ချက်ကြည့်ရုံနဲ့ မြင်ရမယ်။ node တစ်ခုချင်းစီရဲ့ အရောင်ကို မှတ်ပါ၊ ပြီးတော့ ဘယ် node က ဆက်ပြီး execute ဖြစ်သွားလဲဆိုတာကို မှတ်ပါ။ ပြီးရင် deploy job ကို ဖွင့်ပြီး GitHub က အပေါ်ဆုံးမှာ ပြတဲ့ annotation ကို ဖတ်ပါ — dependency fail ဖြစ်ရဲ့သားနဲ့ job တစ်ခု run သွားတယ်ဆိုရင် သူ့ကို ဖြတ်ခွင့်ပေးလိုက်တာ သူ့ရဲ့ if: expression တစ်ခုတည်းပါပဲ။
Terminal ကနေဆိုရင် gh run list --limit 5 နဲ့ gh run view <id> က shell ထဲကမထွက်ဘဲ တူညီတဲ့ ပုံရိပ်ကို ပေးတယ် — repository အများကြီးကို တစ်ပြိုင်နက် audit လုပ်တဲ့အခါ အသုံးဝင်တယ်။ နောက်ဆုံးမှာ တကယ့်တရားခံကို repository တစ်ခုလုံးမှာ ရှာပါ — grep -rn 'always()' .github/workflows။ ကျန်းမာတဲ့ repository တစ်ခုမှာ ထွက်လာတဲ့ hit တွေဟာ cleanup ဒါမှမဟုတ် notification job တွေထဲမှာသာ ရှိသင့်တယ်။ production ကို ရေးတဲ့ job တစ်ခုပေါ်မှာ always() တွေ့ရင် အဲဒါက သင်ရှာနေတဲ့ အရာပါပဲ။
အတူတူ စမ်းရေးကြည့်မယ်
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test
build:
runs-on: ubuntu-latest
steps:
- name: Check out the repository
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
- name: Install dependencies
run: npm ci
- name: Build the production bundle
run: npm run build
- name: Upload the build output
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
deploy:
runs-on: ubuntu-latest
needs: [test, build]
if: always() && github.ref == 'refs/heads/main'
steps:
- name: Download the build output
uses: actions/download-artifact@v4
with:
name: dist
path: dist
- name: Publish to production
run: ./scripts/deploy.sh dist
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
ဒီအတိုင်း ရေးထားတဲ့အခါ workflow က crash မဖြစ်ဘူး၊ syntax အရလည်း ဘာမှ မှားမနေဘူး။ build မှာ needs: မပါတဲ့အတွက် test နဲ့ build က တစ်ပြိုင်နက် စတယ် — ဒါကြောင့် test suite fail ဖြစ်နေရင်တောင် bundle က ဆက်ပြီး build ဖြစ်ကာ artifact အဖြစ် upload ဖြစ်သွားတယ်။ deploy က needs: [test, build] လို့ ကြေညာထားတဲ့အတွက် နှစ်ခုလုံးပြီးမှ စတယ်၊ ဒါပေမဲ့ သူ့ရဲ့ if: expression ထဲမှာ always() ပါနေတဲ့အတွက် GitHub က ပုံမှန်ဆို ထည့်ပေးမယ့် implicit success() check က ပျောက်သွားတယ်။ ရလဒ်ကတော့ main ကို push တိုင်း၊ အပေါ်က job တွေ ပြီးသွားတာနဲ့ deploy က run တယ် — test က အနီရောင်နဲ့ ပြီးသွားတဲ့ run တွေအပါအဝင်ပါ။ build အောင်မြင်ထားတဲ့အတွက် download လုပ်မယ့် artifact က ရှိနေတယ်၊ ဒါကြောင့် deploy step က ပုံမှန်အတိုင်း အလုပ်လုပ်ပြီး ကိုယ့် test suite ကို မအောင်တဲ့ code ကို ထုတ်ပေးလိုက်တယ်။ Run summary မှာ test job fail ဖြစ်နေတာ ဆက်ပြနေတဲ့အတွက် repository က ပျက်နေသလို မြင်ရပေမယ့် အဲဒါကို လူတစ်ယောက် ဖတ်ပြီးချိန်မှာ production က ပြောင်းသွားပြီးဖြစ်တယ်။ always() ကြောင့် တစ်ဝက်တစ်ပျက် cancel လုပ်လိုက်တဲ့ run တွေမှာတောင် deploy က run ပါသေးတယ်။၅ မိနစ် စမ်းကြည့်
သင့်အဖွဲ့က unit test တစ်ခုနဲ့ ဖမ်းမိထားပြီးသား bug တစ်ခု ပြီးခဲ့တဲ့ သောကြာနေ့က production ရောက်သွားတယ်လို့ တိုင်ကြားလာတယ်။ အဲဒီ commit ရဲ့ Actions run က အနီရောင်ဖြစ်နေတယ် — test job က ထင်ရှားစွာ fail ဖြစ်ထားတယ် — ဒါပေမဲ့ မိနစ်အနည်းငယ်အကြာမှာ production site က ပြောင်းသွားပြီး deploy job ရဲ့ log မှာလည်း ပုံမှန်အတိုင်း ပြီးဆုံးသွားတာကို ပြနေတယ်။
အပေါ်က workflow ကို ဖတ်ပြီး line တစ်ကြောင်းမှ မပြင်ခင် မေးခွန်းသုံးခုကို စာနဲ့ ဖြေကြည့်ပါ။ ပထမ — ဒီ job သုံးခုဟာ တကယ်တော့ ဘယ်အစီအစဉ်နဲ့ run လဲ၊ ဘယ်ဟာက test ကို မစောင့်ဘဲ စနိုင်လဲ။ ဒုတိယ — test fail ဖြစ်တဲ့အခါ ကျန်နှစ်ခုက ဘာလုပ်လဲ၊ ပြီးတော့ deploy job အတွက် download လုပ်စရာ artifact တစ်ခု ဘာကြောင့် ရှိနေသေးလဲ။ တတိယ၊ ဒါက အရေးအကြီးဆုံး — deploy က needs: [test, build] လို့ ရေးထားပါလျက် fail ဖြစ်သွားတဲ့ test က သူ့ကို ဘာကြောင့် မတားနိုင်ခဲ့လဲ။ needs: list ရှိတဲ့ job တစ်ခုကို dependency fail ဖြစ်တဲ့အခါ GitHub က skip လုပ်မလား မလုပ်ဘူးလား ဘယ်လိုဆုံးဖြတ်လဲဆိုတာနဲ့၊ အဲဒီ job ကိုယ်တိုင်က if: condition တစ်ခု ပေးလိုက်တဲ့အခါ အဲဒီဆုံးဖြတ်ချက် ဘာဖြစ်သွားလဲဆိုတာကို သေချာလေ့လာပါ။
ပြီးရင် test suite fail ဖြစ်တာနဲ့ deploy က skip ဖြစ်သွားအောင် ပြင်ပါ — main branch မှာပဲ deploy ဖြစ်ရမယ်ဆိုတဲ့ ကန့်သတ်ချက်ကိုတော့ မပျက်စေရဘူး။ တမင် fail အောင်လုပ်ထားတဲ့ test တစ်ခုကို push ပြီး deploy က success မဟုတ်ဘဲ skipped ဖြစ်သွားတာကို ကြည့်ပြီး အတည်ပြုပါ။
သတိလေးတစ်ချက်
Debug လုပ်ရင်း ထည့်လိုက်တဲ့ always() ကို production ကို ရေးတဲ့ job ပေါ်မှာ မေ့ကျန်ခဲ့ခြင်း — needs: က ordering သက်သက် ဖြစ်သွားပြီး fail နေတဲ့ test job က ဘာကိုမှ မတားနိုင်တော့ဘူး။
အချိန်ကုန်သက်သာဖို့ build ကို test နဲ့ တစ်ပြိုင်နက် run ခိုင်းပြီး၊ test fail တဲ့ commit တွေအတွက်တောင် artifact က ရှိနေမယ်ဆိုတာ မေ့ထားခြင်း — deploy job က အဲဒါကို ပျော်ပျော်ကြီး download လုပ်သွားလိမ့်မယ်။
GitHub Docs: Evaluate expressions in workflows and actions — CI/CD with GitHub Actions