Thuta Learning
CI/CD with GitHub Actions
IntermediateDevOps & Toolsbeginner

Pull Request Quality Gate များ

ဒီခန်းပြီးရင် ဘာတတ်သွားမလဲ

  • Pull Request Quality Gate များ concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • Diagram ကို ဖတ်ပြီး pipeline ထဲမှာ job တွေ ဘယ်လိုစီးဆင်းပြီး ဘာက gate လုပ်သလဲ ခြေရာခံနိုင်ရန်
  • Workflow file ကို ဖတ်ပြီး ဘယ် job တွေ ဘယ်အစဉ်လိုက် run မလဲ ကြိုတင်ပြောနိုင်ရန်

နားလည်ထားရမယ့် အချက်

Pull request တိုင်းတွင် run ပြီး ရလဒ်တင်ပြသော workflow တစ်ခုသည် အချက်အလက်ပေးသည်။ Gate တစ်ခုတော့ မဟုတ်ပါ။ Reviewer တစ်ယောက်သည် အနီရောင် PR တစ်ခုကို merge လုပ်ခြင်းမှ ဘာမှ မတားဆီးနိုင်ပါ — branch protection က ၎င်းတို့ကို စည်းကမ်းချက်တစ်ခု အဖြစ် မပြောင်းလဲမချင်း check များသည် အကြံပြုချက်သာ ဖြစ်သည်။ Gate သည် target branch အတွက် repository ၏ branch protection rule သို့မဟုတ် ruleset ထဲတွင် တည်ရှိပြီး၊ အဲဒီနေရာတွင် သတ်မှတ်ထားသော check များကို required အဖြစ် ရွေးသည်။ ရွေးပြီးသည်နှင့် အဲဒီ check တစ်ခုစီသည် PR ၏ head commit ပေါ်တွင် success မတင်ပြမချင်း merge ခလုတ်ကို ပိတ်ထားသည်။

လူတွေကို အံ့အားသင့်စေသော ယန္တရားမှာ required check များကို အမည်ဖြင့် တိုက်ဆိုင်စစ်ဆေးခြင်းဖြစ်ပြီး၊ ဘယ်တော့မှ မတင်ပြသော check တစ်ခုနှင့် ဆက်လက် run နေဆဲ check တစ်ခုကို ခွဲခြား၍ မရခြင်းဖြစ်သည်။ Job တစ်ခုကို အမည်ပြောင်းလိုက်၊ matrix dimension ကို ပြောင်းလိုက်၍ job အမည်များ ရွှေ့သွား၊ သို့မဟုတ် documentation-only PR များတွင် workflow ကို ကျော်စေမည့် path filter တစ်ခု ထည့်လိုက်ပါ — required check သည် ဘယ်တော့မှ ရောက်မလာတော့ဘဲ၊ PR သည် ကြည့်စရာ failure မရှိဘဲ အမြဲတမ်း ပိတ်ဆို့ခံနေရမည်။ ပုံမှန် အဖြေမှာ if: always() နှင့် needs: ဖြင့် အားလုံးကို ချိတ်ထားသော သေးငယ်ပြီး စုစည်းပေးသော job တစ်ခုကို ထားခြင်းဖြစ်သည် — ၎င်းသည် အမြဲ ရလဒ်တင်ပြပြီး အမည်လည်း ဘယ်တော့မှ မပြောင်းသဖြင့် အဲဒီ job တစ်ခုတည်းကိုသာ required အဖြစ် သတ်မှတ်ရသည်။

Gate လုပ်သင့်သည်မှာ လူသားတစ်ယောက် review တွင် ကျိုးကြောင်းဆီလျော်စွာ မဖမ်းမိနိုင်သော deterministic အရာများဖြစ်သည် — test၊ typecheck၊ lint၊ မပျက်ရမည့် build။ မလုပ်သင့်သည်မှာ အကြံပြုချက်သဘောသာဖြစ်သော ဆူညံသည့်အရာများ — bundle-size ကွာဟချက်၊ တစ်ရာခိုင်နှုန်း လျော့ကျသွားသော coverage၊ false positive များ သိထားသော security scanner။ ဒါတွေကို comment ပေးစေပါ၊ ပိတ်ဆို့မခိုင်းပါနှင့်။

ဒါတွေ ရှင်သန်မလား မရှင်သန်ဘူးလား ဆုံးဖြတ်သည်မှာ လူသားဆိုင်ရာ အချက်ဖြစ်သည်။ မိနစ်နှစ်ဆယ့်ငါး ကြာသော သို့မဟုတ် ဆယ်ကြိမ်လျှင် တစ်ကြိမ် ကျပန်း ကျရှုံးသော gate တစ်ခုသည် လူများကို ပြန် run ဖို့၊ ပြီးလျှင် ကျော်ဖို့၊ ပြီးလျှင် ဖယ်ရှားဖို့ သင်ပေးလိုက်သည်။ နှေးပြီး မတည်ငြိမ်သော gate သည် ဘာကိုမှ မကာကွယ်ပါ။

text
PR OPENED, CHECKS RUN, MERGE UNLOCKED OR BLOCKED
------------------------------------------------
  PR opened, or a new commit pushed to it
        |
        v
  +---------------------------------------+
  | workflow triggers on: pull_request     |
  | concurrency cancels the previous run   |
  +---------------------------------------+
        |
        +--> lint        (required) --+
        +--> typecheck   (required) --+
        +--> test        (required) --+--> required-gate
        +--> build       (required) --+    if: always()
        +--> bundle-size (advisory) ..     needs: [...]
                                                |
                                                v
                          +-------------------------------+
                          | branch protection on main     |
                          | is required-gate a success?   |
                          +-------------------------------+
                               | yes             | no
                               v                 v
                         MERGE ENABLED      MERGE BLOCKED

  advisory checks report but never block the merge

လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်

ပထမဦးစွာ pipeline ကို gate ပုံစံ ဖြစ်အောင် လုပ်ပါ။ fast-checks job သည် lint, typecheck, test ကို matrix သေးသေးလေးအဖြစ် run သဖြင့် ပြိုင်တူ အလုပ်လုပ်ပြီး တစ်ခုစီ သီးခြား ရလဒ်တင်ပြသည်၊ fail-fast: false ထားသဖြင့် ကျရှုံးသော check တစ်ခုက ကျန်နှစ်ခုကို မဖုံးကွယ်ပါ။ build သည် ၎င်းတို့နောက်မှ run သည်၊ အကြောင်းက typecheck မအောင်သော branch တစ်ခုကို compile လုပ်ခြင်းသည် အချည်းနှီးဖြစ်သောကြောင့်ဖြစ်သည်။ နှစ်ခုစလုံးတွင် timeout-minutes ထားခြင်းက ရပ်တန့်နေသော job တစ်ခုက PR ကို ခြောက်နာရီ ပိတ်ဆို့မထားစေရန် ဖြစ်သည်။

cancel-in-progress: true ပါသော concurrency သည် PR သို့ push အသစ်တစ်ခုက အဲဒီ branch ရဲ့ ယခင် run ကို ဖျက်သိမ်းစေသည်။ ဒါက အလုပ်များသော repo တစ်ခုတွင် အကြီးမားဆုံး ချွေတာမှုဖြစ်ပြီး၊ ဟောင်းနွမ်းသော run တစ်ခုက commit အဟောင်းအတွက် ရလဒ် တင်ပြခြင်းကိုလည်း တားဆီးပေးသည်။

ထို့နောက် gate job ကို ထည့်ပါ။ ၎င်းသည် needs: [fast-checks, build] နှင့် if: always() ကို ကြေညာသဖြင့် အထက် job တစ်ခု ကျရှုံးသည့်အခါတွင်ပင် run ပြီး၊ needs.<job>.result ကို စစ်ဆေးကာ ကိုယ်ပိုင် exit code ကို ဆုံးဖြတ်သည်။ Branch protection တွင် required-gate ဟူသော check တစ်ခုတည်းကိုသာ required အဖြစ် သတ်မှတ်ပါ။ ယခုအခါ pipeline သို့ job အသစ်တစ်ခု ထည့်ခြင်းသည် needs: သို့ ထည့်ရုံသာဖြစ်ပြီး protection rule ကို မထိရတော့ပါ၊ ထို့အပြင် job တစ်ခုစီကို အမည်ဖြင့် required လုပ်ထားလျှင် ဖြစ်တတ်သလို skip ဖြစ်သွားသော အထက် job တစ်ခုက gate ကို တိတ်တဆိတ် ကျေနပ်စေခြင်း မဖြစ်နိုင်တော့ပါ။

အတူတူ စမ်းရေးကြည့်မယ်

yaml
name: PR quality gate

on:
  pull_request:
    branches: [main]

concurrency:
  group: gate-${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

permissions:
  contents: read

jobs:
  fast-checks:
    name: fast-checks
    runs-on: ubuntu-latest
    timeout-minutes: 10
    strategy:
      fail-fast: false
      matrix:
        check: [lint, typecheck, test]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: npm
      - run: npm ci
      - name: Run ${{ matrix.check }}
        run: npm run ${{ matrix.check }}

  build:
    name: build
    needs: fast-checks
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: npm
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: build-${{ github.sha }}
          path: dist/
          retention-days: 3

  required-gate:
    name: required-gate
    if: always()
    needs: [fast-checks, build]
    runs-on: ubuntu-latest
    steps:
      - name: Fail unless every upstream job succeeded
        env:
          CHECKS_RESULT: ${{ needs.fast-checks.result }}
          BUILD_RESULT: ${{ needs.build.result }}
        run: |
          echo "fast-checks: $CHECKS_RESULT"
          echo "build: $BUILD_RESULT"
          if [ "$CHECKS_RESULT" != "success" ] || [ "$BUILD_RESULT" != "success" ]; then
            exit 1
          fi
You should see
main ကို ဦးတည်သော pull request တိုင်းတွင် fast-checks job သုံးခု — lint, typecheck, test တစ်ခုစီ — ပြိုင်တူ run ပြီး၊ fail-fast သည် false ဖြစ်သဖြင့် တစ်ခု ကျရှုံးသည့်တိုင် သုံးခုစလုံး ရလဒ်တင်ပြသည်။ build သည် သုံးခုစလုံး အောင်မြင်မှသာ run ပြီး၊ တစ်ခုခု ကျရှုံးလျှင် build ကို skip လုပ်၍ ၎င်း၏ result သည် success မဟုတ်ဘဲ skipped ဖြစ်သည်။ required-gate job သည် if: always() ကြောင့် အခြေအနေတိုင်းတွင် run ပြီး၊ အထက် job နှစ်ခုလုံး၏ result ကို ဖတ်ကာ၊ နှစ်ခုစလုံး success အတိအကျ မဟုတ်လျှင် exit code သုည မဟုတ်သည်ကို ပြန်ပေးသည် — ဒါကပင် skip ဖြစ်သွားသော build တစ်ခုက gate ကို တိတ်တဆိတ် ဖြတ်သွားမည့်အစား merge ကို ပိတ်ဆို့စေခြင်း ဖြစ်သည်။ PR သို့ commit အသစ် push လုပ်လျှင် concurrency group မှတစ်ဆင့် အဲဒီ branch ရဲ့ လက်ရှိ run ကို ဖျက်သိမ်းသဖြင့် commit အသစ်ဆုံး၏ ရလဒ်သာ ကျန်ရှိသည်။ Branch protection ကို workflow file အပြင်ဘက်တွင် သတ်မှတ်ရသည် — required-gate ကို required status check အဖြစ် စာရင်းသွင်းထားလျှင် အဲဒီ job သည် PR ၏ head commit ပေါ်တွင် success မတင်ပြမချင်း GitHub သည် merge ခလုတ်ကို ပိတ်ထားမည် ဖြစ်သည်။

၅ မိနစ် စမ်းကြည့်

သင့် pipeline များထဲမှ တစ်ခုသို့ စုစည်းပေးသော gate job တစ်ခုတည်း ထည့်ပြီး၊ branch protection တွင် ၎င်းကိုသာ required status check အဖြစ် သတ်မှတ်ပါ။ ထို့နောက် အထက် job တစ်ခု၏ အမည်ကို ပြောင်းပြီး PR သည် မှန်ကန်စွာ gate ခံနေဆဲဖြစ်ကြောင်း အတည်ပြုပါ — job တစ်ခုစီကို အမည်ဖြင့် required လုပ်ထားလျှင် အဲဒီ အမည်ပြောင်းမှုက PR ကို ဘယ်တော့မှ မတင်ပြသော check တစ်ခုပေါ်တွင် ပိတ်ဆို့ထားခဲ့မည်ဖြစ်သည်။ သီးခြားအားဖြင့် နောက်ဆုံး run သုံးဆယ်အတွင်း သင့် pipeline ၏ median PR wall-clock time နှင့် မပြောင်းလဲသော code ပေါ်တွင် ကျရှုံးနှုန်းကို တိုင်းတာပါ — နှေးလျှင် သို့မဟုတ် မတည်ငြိမ်လျှင် required check ထပ်မထည့်ခင် အဲဒါကို အရင်ပြင်ပါ။

သတိလေးတစ်ချက်

Branch protection တွင် job တစ်ခုစီကို အမည်ဖြင့် required လုပ်ပြီး၊ နောက်မှ job တစ်ခုကို အမည်ပြောင်းခြင်း သို့မဟုတ် matrix တန်ဖိုးတစ်ခု ပြောင်းခြင်း — အမည်ဟောင်းသည် ဘယ်တော့မှ ပြန်မတင်ပြတော့ဘဲ PR တိုင်းသည် စုံစမ်းစရာ failure မရှိဘဲ ပိတ်ဆို့ခံနေရသည်။

စုစည်းပေးသော gate job တွင် if: always() မေ့ကျန်ခြင်း — အထက် job တစ်ခု ကျရှုံးသောအခါ gate သည် skip ဖြစ်ပြီး ဘာမှ မတင်ပြသဖြင့် PR သည် ဖတ်ရလွယ်သော failure အစား အမြဲတမ်း pending အဖြစ် ပေါ်နေသည်။

GitHub Docs - About protected branchesCI/CD with GitHub Actions

ဒီနေရာမှာ လူအများမှားတတ်တယ်

  • Branch protection တွင် job တစ်ခုစီကို အမည်ဖြင့် required လုပ်ပြီး၊ နောက်မှ job တစ်ခုကို အမည်ပြောင်းခြင်း သို့မဟုတ် matrix တန်ဖိုးတစ်ခု ပြောင်းခြင်း — အမည်ဟောင်းသည် ဘယ်တော့မှ ပြန်မတင်ပြတော့ဘဲ PR တိုင်းသည် စုံစမ်းစရာ failure မရှိဘဲ ပိတ်ဆို့ခံနေရသည်။
  • စုစည်းပေးသော gate job တွင် if: always() မေ့ကျန်ခြင်း — အထက် job တစ်ခု ကျရှုံးသောအခါ gate သည် skip ဖြစ်ပြီး ဘာမှ မတင်ပြသဖြင့် PR သည် ဖတ်ရလွယ်သော failure အစား အမြဲတမ်း pending အဖြစ် ပေါ်နေသည်။
  • Workflow ကို production branch ပေါ် တိုက်ရိုက်မစမ်းဘဲ branch (သို့) test repository တွင် အရင်အတည်ပြုပါ။

လေ့ကျင့်ခန်း

သင့် pipeline များထဲမှ တစ်ခုသို့ စုစည်းပေးသော gate job တစ်ခုတည်း ထည့်ပြီး၊ branch protection တွင် ၎င်းကိုသာ required status check အဖြစ် သတ်မှတ်ပါ။ ထို့နောက် အထက် job တစ်ခု၏ အမည်ကို ပြောင်းပြီး PR သည် မှန်ကန်စွာ gate ခံနေဆဲဖြစ်ကြောင်း အတည်ပြုပါ — job တစ်ခုစီကို အမည်ဖြင့် required လုပ်ထားလျှင် အဲဒီ အမည်ပြောင်းမှုက PR ကို ဘယ်တော့မှ မတင်ပြသော check တစ်ခုပေါ်တွင် ပိတ်ဆို့ထားခဲ့မည်ဖြစ်သည်။ သီးခြားအားဖြင့် နောက်ဆုံး run သုံးဆယ်အတွင်း သင့် pipeline ၏ median PR wall-clock time နှင့် မပြောင်းလဲသော code ပေါ်တွင် ကျရှုံးနှုန်းကို တိုင်းတာပါ — နှေးလျှင် သို့မဟုတ် မတည်ငြိမ်လျှင် required check ထပ်မထည့်ခင် အဲဒါကို အရင်ပြင်ပါ။

You'll know it worked when: main ကို ဦးတည်သော pull request တိုင်းတွင် fast-checks job သုံးခု — lint, typecheck, test တစ်ခုစီ — ပြိုင်တူ run ပြီး၊ fail-fast သည် false ဖြစ်သဖြင့် တစ်ခု ကျရှုံးသည့်တိုင် သုံးခုစလုံး ရလဒ်တင်ပြသည်။ build သည် သုံးခုစလုံး အောင်မြင်မှသာ run ပြီး၊ တစ်ခုခု ကျရှုံးလျှင် build ကို skip လုပ်၍ ၎င်း၏ result သည် success မဟုတ်ဘဲ skipped ဖြစ်သည်။ required-gate job သည် if: always() ကြောင့် အခြေအနေတိုင်းတွင် run ပြီး၊ အထက် job နှစ်ခုလုံး၏ result ကို ဖတ်ကာ၊ နှစ်ခုစလုံး success အတိအကျ မဟုတ်လျှင် exit code သုည မဟုတ်သည်ကို ပြန်ပေးသည် — ဒါကပင် skip ဖြစ်သွားသော build တစ်ခုက gate ကို တိတ်တဆိတ် ဖြတ်သွားမည့်အစား merge ကို ပိတ်ဆို့စေခြင်း ဖြစ်သည်။ PR သို့ commit အသစ် push လုပ်လျှင် concurrency group မှတစ်ဆင့် အဲဒီ branch ရဲ့ လက်ရှိ run ကို ဖျက်သိမ်းသဖြင့် commit အသစ်ဆုံး၏ ရလဒ်သာ ကျန်ရှိသည်။ Branch protection ကို workflow file အပြင်ဘက်တွင် သတ်မှတ်ရသည် — required-gate ကို required status check အဖြစ် စာရင်းသွင်းထားလျှင် အဲဒီ job သည် PR ၏ head commit ပေါ်တွင် success မတင်ပြမချင်း GitHub သည် merge ခလုတ်ကို ပိတ်ထားမည် ဖြစ်သည်။

Pull Request Quality Gate များ | Thuta Learning