Thuta Learning
CI/CD with GitHub Actions
IntermediateDevOps & Toolsbeginner

Conditional Step များနှင့် Job များ

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

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

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

if: သည် step သို့မဟုတ် job တစ်ခု run မရန် ဆုံးဖြတ်ပေးသည်။ Step ပေါ်တွင် ရေးလျှင် ရှေ့က step များ run ပြီးမှ အကဲဖြတ်သဖြင့် သူတို့၏ ရလဒ်များကို ဖတ်နိုင်သည်။ Job ပေါ်တွင် ရေးလျှင် needs: ထဲက job များ အပြီးသတ်မှသာ အကဲဖြတ်သည်။ တန်ဖိုးသည် expression တစ်ခုဖြစ်ပြီး၊ if: အတွင်း ${{ }} အဖုံးသည် ရွေးချယ်စရာဖြစ်သည် — GitHub က string ကို expression အဖြစ် နည်းလမ်းနှစ်မျိုးလုံးတွင် အကဲဖြတ်သည်။ သို့သော် expression သည် YAML က မှားဖတ်နိုင်သော စာလုံးဖြင့် စတင်လျှင်တော့ အဖုံး လိုအပ်သည် — ဒါကြောင့် if: ${{ !cancelled() }} တွင် ပါပြီး if: matrix.coverage တွင် မပါခြင်းဖြစ်သည်။

တကယ် အရေးကြီးသော အပိုင်းက implicit default ဖြစ်သည်။ Step တိုင်းသည် if: success() ဟု ရေးထားသကဲ့သို့ ပြုမူသည် — ဆိုလိုသည်မှာ အဲဒီ job ထဲက ရှေ့က step တစ်ခုမှ မကျရှုံးသေးသရွေ့သာ run သည်။ Step တစ်ခု ကျရှုံးသည်နှင့် condition တိတိကျကျ မပါသော နောက်ပိုင်း step အားလုံး skip ဖြစ်သွားသည်။ ဒီ default သည် build ကိုယ်တိုင်အတွက် မှန်ကန်ပြီး၊ build ပျက်သောအခါ သင် အလိုအရှိဆုံး အရာများအတွက် လုံးဝ မှားနေသည် — test report၊ screenshot bundle၊ log dump၊ ငှားထားသော resource ကို ပြန်လွှတ်ပေးမည့် teardown။ ဒါတွေအတွက် if: always() သို့မဟုတ် if: ${{ !cancelled() }} လိုအပ်ပြီး၊ အဖွဲ့တစ်ဖွဲ့သည် ဒါကို များသောအားဖြင့် flaky end-to-end suite တစ်ခု ကျရှုံးပြီး ကြည့်စရာ report မရှိသည့်နေ့တွင်မှ သိလာတတ်သည် — အကြောင်းက upload step သည် အရေးကြီးဆုံးအချိန်တွင် skip ဖြစ်သွားသောကြောင့်ဖြစ်သည်။

always() သည် run တစ်ခု cancel ခံရသည့်အခါလည်း run သည်၊ ဒါကြောင့် cleanup အတွက် !cancelled() က များသောအားဖြင့် ပိုကောင်းသည် — ကျရှုံးမှုတွင် ဆက်လက် run သော်လည်း cancellation ကို မဆန့်ကျင်ပါ။

continue-on-error ကတော့ တစ်မျိုးဖြစ်သည်။ ၎င်း သတ်မှတ်ထားပြီး ကျရှုံးသော step တစ်ခုသည် outcome: failure ကို မှတ်တမ်းတင်သော်လည်း conclusion: success ဖြစ်သဖြင့် job သည် ဆက်သွားပြီး နောက်ပိုင်းတွင် success() သည် true အဖြစ် ကျန်နေသည်။

text
WHICH STEPS RUN ON THE SUCCESS AND FAILURE PATHS
------------------------------------------------
  step (in order)              all green        step 3 fails
  ---------------------------  ---------------  ---------------
  1 checkout                   runs             runs
  2 npm ci                     runs             runs
  3 npm run test:e2e           runs, passes     runs, FAILS
  4 npm run lint               runs             SKIPPED
      continue-on-error: true
  5 upload screenshots         SKIPPED          RUNS
      if: failure()
  6 upload junit report        runs             RUNS
      if: always()
  7 teardown                   runs             RUNS
      if: !cancelled()

  job result:                  success          failure

  a step with no if: behaves as if: success()
  a continue-on-error step that fails does NOT flip success()
  on a CANCELLED run: step 6 still runs, step 7 does not

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

reports/ ထဲသို့ JUnit XML နှင့် test-results/ ထဲသို့ ကျရှုံးမှု screenshot များ ရေးသော end-to-end suite တစ်ခုကို ယူကြည့်ပါ။ ရိုးရှင်းလွန်းသော workflow သည် upload step ကို condition မပါဘဲ နောက်ဆုံးတွင် ထားလိုက်သည် — ဆိုလိုသည်မှာ မလိုအပ်သော အစိမ်းရောင် build တိုင်းတွင် run ပြီး၊ တကယ်လိုအပ်သော အနီရောင် build တိုင်းတွင် skip ဖြစ်နေခြင်း ဖြစ်သည်။

နောက်ဆက်တွဲ step တစ်ခုစီကို ၎င်း၏ ရည်ရွယ်ချက်နှင့် ကိုက်ညီသော condition ပေးခြင်းဖြင့် ပြင်ပါ။ Screenshot upload သည် if: failure() ရသည်၊ အကြောင်းက screenshot များသည် တစ်ခုခု မှားသွားမှသာ ရှိလာပြီး အဓိပ္ပာယ်ရှိသောကြောင့်ဖြစ်သည်။ JUnit report upload သည် if: always() ရသည်၊ အကြောင်းက report သည် ဘယ်လမ်းကြောင်းမှာမဆို တန်ဖိုးရှိပြီး test summary tool တစ်ခုက နှစ်မျိုးလုံးတွင် လိုအပ်သောကြောင့်ဖြစ်သည်။ Teardown သည် if: ${{ !cancelled() }} ရသည်၊ ဒါက ငှားထားသော database သို့မဟုတ် deploy လုပ်ထားသော preview environment ကို test အောင်သည်ဖြစ်စေ ရှုံးသည်ဖြစ်စေ ပြန်လွှတ်ပေးပြီး၊ cancel ကိုတော့ လေးစားသည်။

Step တစ်ခုသည် တကယ် conditional ဖြစ်လျှင် condition များကို ပေါင်းစပ်ပါ — if: ${{ failure() && github.event.pull_request.draft == false }} သည် draft မဟုတ်သော PR များ ပျက်သွားလျှင်သာ အဖွဲ့ကို အသိပေးပြီး draft များတွင် တိတ်ဆိတ်နေသည်။

Lint step သည် continue-on-error: true ကို သုံးထားသည်။ ၎င်းသည် step စာရင်းထဲတွင် ကိုယ်ပိုင် အနီရောင် X ကို ပြသဖြင့် သတိပေးချက်များ မရှိဟု ဟန်ဆောင်၍ မရပါ၊ သို့သော် job ကို မကျရှုံးစေပါ — အရေးအကြီးဆုံးမှာ နောက်လာမည့် step များအတွက် success() ကို false သို့ မလှန်ပစ်ခြင်းဖြစ်သည်။

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

yaml
name: End-to-end tests with reports

on:
  pull_request:

jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run end-to-end tests
        id: e2e
        run: npm run test:e2e

      - name: Lint (advisory only)
        continue-on-error: true
        run: npm run lint

      - name: Upload failure screenshots
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: screenshots
          path: test-results/
          if-no-files-found: ignore

      - name: Always upload the test report
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: junit-report
          path: reports/junit.xml
          if-no-files-found: warn

      - name: Notify only on a real failure
        if: ${{ failure() && github.event.pull_request.draft == false }}
        run: ./scripts/notify.sh "e2e failed on ${{ github.head_ref }}"

      - name: Release the leased environment
        if: ${{ !cancelled() }}
        run: ./scripts/teardown.sh
You should see
အားလုံး အောင်မြင်သော run တစ်ခုတွင် failure-only step များမှလွဲ၍ step တိုင်း run သည် — screenshot upload နှင့် notification သည် skip ဖြစ်ပြီး၊ JUnit upload နှင့် teardown သည် run ကာ၊ job ရလဒ်သည် success ဖြစ်သည်။ End-to-end step ကျရှုံးသောအခါ ၎င်းနောက်က condition မပါသော step များ — lint အပါအဝင် — သည် skip ဖြစ်သွားပြီး၊ screenshot upload သည် failure() true ဖြစ်သဖြင့် run ကာ၊ JUnit upload သည် always() ကြောင့် run ကာ၊ notification သည် PR က draft မဟုတ်လျှင် run ကာ၊ teardown သည် run ကို cancel မလုပ်ခဲ့သဖြင့် run ပြီး၊ job ရလဒ်သည် failure ဖြစ်သည်။ Lint ကိုယ်တိုင် ကျရှုံးလျှင် ၎င်း၏ entry ကို ကျရှုံးအဖြစ် အမှတ်အသားပြုသော်လည်း job သည် ဆက်သွားပြီး job ရလဒ်မှာ success အဖြစ် ကျန်နေသည်၊ အကြောင်းက continue-on-error သည် step ရဲ့ conclusion ကို success သို့ ပြောင်းပေးသောကြောင့်ဖြစ်သည်။ တစ်ယောက်ယောက်က run ကို cancel လုပ်လျှင် always() step များသည် ဆက်လက် run သော်လည်း !cancelled() teardown သည် မ run ပါ — ဒါက condition နှစ်ခုကြား ကွာခြားချက်ဖြစ်သည်။

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

နောက်ဆုံးတွင် report-upload step ရှိသော workflow တစ်ခုကို ယူပြီး test step ကို တမင် ကျရှုံးအောင် လုပ်ပါ။ Upload သည် skip ဖြစ်သွားကြောင်း အတည်ပြုပါ။ if: always() ထည့်ပြီး ယခု run ကြောင်း အတည်ပြုပါ။ ထို့နောက် if: ${{ !cancelled() }} ပါသော cleanup step တစ်ခု ထည့်ပြီး UI မှ run တစ်ခုကို cancel လုပ်ကာ၊ condition နှစ်ခုအနက် ဘယ်တစ်ခုက ဆက်လက် အလုပ်လုပ်သလဲ ကြည့်ပါ။ နောက်ဆုံးတွင် lint step တစ်ခုကို continue-on-error: true ဟု အမှတ်အသားပြုပြီး job ရဲ့ conclusion ဘာဖြစ်လာသလဲ စစ်ပါ။

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

Test report သို့မဟုတ် screenshot upload step ကို if: မပါဘဲ ထားခြင်း — implicit success() ကို အမွေဆက်ခံသဖြင့် သင် အလိုအရှိဆုံးသော ကျရှုံးသည့် run များတွင် အတိအကျ skip ဖြစ်သွားသည်။

continue-on-error ကို 'ဒီ step ကို လျစ်လျူရှုပါ' ဟု အဓိပ္ပာယ်ဖွင့်ခြင်း — step သည် UI တွင် ကျရှုံးအဖြစ် ဆက်ပြနေဆဲဖြစ်ပြီး ၎င်း၏ outcome သည် failure ဖြစ်ကာ၊ job ရဲ့ conclusion ကိုသာ မထိခိုက်စေခြင်းဖြစ်သည်။

GitHub Docs - ExpressionsCI/CD with GitHub Actions

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

  • Test report သို့မဟုတ် screenshot upload step ကို if: မပါဘဲ ထားခြင်း — implicit success() ကို အမွေဆက်ခံသဖြင့် သင် အလိုအရှိဆုံးသော ကျရှုံးသည့် run များတွင် အတိအကျ skip ဖြစ်သွားသည်။
  • continue-on-error ကို 'ဒီ step ကို လျစ်လျူရှုပါ' ဟု အဓိပ္ပာယ်ဖွင့်ခြင်း — step သည် UI တွင် ကျရှုံးအဖြစ် ဆက်ပြနေဆဲဖြစ်ပြီး ၎င်း၏ outcome သည် failure ဖြစ်ကာ၊ job ရဲ့ conclusion ကိုသာ မထိခိုက်စေခြင်းဖြစ်သည်။
  • Workflow ကို production branch ပေါ် တိုက်ရိုက်မစမ်းဘဲ branch (သို့) test repository တွင် အရင်အတည်ပြုပါ။

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

နောက်ဆုံးတွင် report-upload step ရှိသော workflow တစ်ခုကို ယူပြီး test step ကို တမင် ကျရှုံးအောင် လုပ်ပါ။ Upload သည် skip ဖြစ်သွားကြောင်း အတည်ပြုပါ။ if: always() ထည့်ပြီး ယခု run ကြောင်း အတည်ပြုပါ။ ထို့နောက် if: ${{ !cancelled() }} ပါသော cleanup step တစ်ခု ထည့်ပြီး UI မှ run တစ်ခုကို cancel လုပ်ကာ၊ condition နှစ်ခုအနက် ဘယ်တစ်ခုက ဆက်လက် အလုပ်လုပ်သလဲ ကြည့်ပါ။ နောက်ဆုံးတွင် lint step တစ်ခုကို continue-on-error: true ဟု အမှတ်အသားပြုပြီး job ရဲ့ conclusion ဘာဖြစ်လာသလဲ စစ်ပါ။

You'll know it worked when: အားလုံး အောင်မြင်သော run တစ်ခုတွင် failure-only step များမှလွဲ၍ step တိုင်း run သည် — screenshot upload နှင့် notification သည် skip ဖြစ်ပြီး၊ JUnit upload နှင့် teardown သည် run ကာ၊ job ရလဒ်သည် success ဖြစ်သည်။ End-to-end step ကျရှုံးသောအခါ ၎င်းနောက်က condition မပါသော step များ — lint အပါအဝင် — သည် skip ဖြစ်သွားပြီး၊ screenshot upload သည် failure() true ဖြစ်သဖြင့် run ကာ၊ JUnit upload သည် always() ကြောင့် run ကာ၊ notification သည် PR က draft မဟုတ်လျှင် run ကာ၊ teardown သည် run ကို cancel မလုပ်ခဲ့သဖြင့် run ပြီး၊ job ရလဒ်သည် failure ဖြစ်သည်။ Lint ကိုယ်တိုင် ကျရှုံးလျှင် ၎င်း၏ entry ကို ကျရှုံးအဖြစ် အမှတ်အသားပြုသော်လည်း job သည် ဆက်သွားပြီး job ရလဒ်မှာ success အဖြစ် ကျန်နေသည်၊ အကြောင်းက continue-on-error သည် step ရဲ့ conclusion ကို success သို့ ပြောင်းပေးသောကြောင့်ဖြစ်သည်။ တစ်ယောက်ယောက်က run ကို cancel လုပ်လျှင် always() step များသည် ဆက်လက် run သော်လည်း !cancelled() teardown သည် မ run ပါ — ဒါက condition နှစ်ခုကြား ကွာခြားချက်ဖြစ်သည်။

Conditional Step များနှင့် Job များ | Thuta Learning