Thuta Learning
CI/CD with GitHub Actions
IntermediateDevOps & Toolsbeginner

Reusable Workflow များနှင့် Composite Action များ

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

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

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

Pipeline များသည် copy ကူးခံရသည်။ Repository တစ်ခုတွင် ကောင်းမွန်သော Node CI workflow တစ်ခု ရလာပြီး နောက်အဖွဲ့က ကူးထည့်ကာ၊ ဆယ့်ရှစ်လကြာသောအခါ repository ငါးခုသည် အတွေးအခေါ်တစ်ခုတည်း၏ အနည်းငယ်စီ ကွဲပြားသော version ငါးမျိုးကို run နေသည် — Node version မတူ၊ cache key မတူ၊ တစ်ခုကတော့ deprecated action တစ်ခုပေါ်တွင် ကျန်နေဆဲ။ ဘယ်သူမှ ကွဲထွက်ရန် ဆုံးဖြတ်ခဲ့ခြင်း မဟုတ်ပါ၊ တစ်ကြိမ်တည်း ပြင်နိုင်မည့် နေရာတစ်ခု မရှိခဲ့ခြင်းသာ ဖြစ်သည်။

GitHub သည် မျှဝေရန် နည်းလမ်းနှစ်မျိုး ပေးထားပြီး၊ ကွဲပြားချက်မှာ granularity ဖြစ်သည်။ Reusable workflow ဆိုသည်မှာ trigger သည် on: workflow_call ဖြစ်သော workflow file တစ်ခုလုံးဖြစ်သည်။ ၎င်းသည် type ပါသော inputs များ၊ လိုအပ်လျှင် secrets များ၊ outputs များကို ကြေညာသည်။ Caller တစ်ခုသည် jobs.<id>.uses: owner/repo/.github/workflows/file.yml@ref ဟု ကိုးကားပြီး၊ အဲဒီ caller job တွင် ကိုယ်ပိုင် runs-on သို့မဟုတ် steps မရှိပါ — ခေါ်လိုက်သော workflow က ကိုယ်ပိုင် job များနှင့် ကိုယ်ပိုင် runner များကို ယူဆောင်လာသည်။ သင် job တစ်ခုလုံးကို မျှဝေနေခြင်းဖြစ်သည်။

Composite action ဆိုသည်မှာ runs.using: composite ပါသော action.yml တစ်ခုအတွင်း ထုပ်ပိုးထားသော step အစဉ်လိုက်တစ်ခုဖြစ်သည်။ Caller သည် ၎င်းကို job တစ်ခုအတွင်း၊ တခြား step များကြားရှိ step တစ်ခုအဖြစ် သုံးပြီး၊ အဲဒီ job ရဲ့ runner ပေါ်တွင် အဲဒီ job ရဲ့ filesystem ကို မျှဝေကာ အလုပ်လုပ်သည်။ သင် job ရဲ့ အစိတ်အပိုင်းတစ်ခုကို မျှဝေနေခြင်းဖြစ်သည်။

ရွေးချယ်မှုသည် အဲဒီအချက်မှ ပေါက်ဖွားလာသည်။ 'Node install၊ cache restore၊ npm ci run' ဆိုသည်မှာ တူညီသော job ထဲက တခြား step များ မှီခိုနေသော step အစဉ်တစ်ခုဖြစ်သည် — composite action။ 'Lint၊ typecheck၊ test၊ build၊ ဒီ matrix ပေါ်တွင်၊ ဒီအစဉ်အတိုင်း' ဆိုသည်မှာ pipeline တစ်ခုလုံးဖြစ်သည် — reusable workflow။

ကိုးကားချက်ကို branch မဟုတ်ဘဲ tag သို့မဟုတ် SHA ဖြင့် pin လုပ်ပါ။ @main ဆိုသည်မှာ shared file ကို တစ်ယောက်ယောက် ပြင်လိုက်သည့်နေ့တွင် caller တိုင်း၏ အပြုအမူ တိတ်တဆိတ် ပြောင်းသွားခြင်း ဖြစ်သည်။

text
THREE REPOS CALLING ONE SHARED WORKFLOW
---------------------------------------
  repo: api-service        repo: web-app          repo: worker
  .github/workflows/       .github/workflows/     .github/workflows/
    ci.yml                   ci.yml                 ci.yml
  jobs.test.uses: --+      jobs.test.uses: --+    jobs.test.uses: --+
                    |                        |                      |
                    v                        v                      v
  +-----------------------------------------------------------+
  | my-org/ci-workflows                                        |
  | .github/workflows/node-ci.yml@v1                           |
  |   on: workflow_call                                        |
  |   inputs:  node-version, run-lint                          |
  |   secrets: npm-token                                       |
  |   jobs:    test   <- runs-on and steps live HERE           |
  +-----------------------------------------------------------+

  one fix here reaches all three repos when you retag v1

  composite action  = a reusable SEQUENCE OF STEPS inside a job
  reusable workflow = one or more whole JOBS, each with a runner

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

my-org/ci-workflows ဟူသော သီးသန့် repository တစ်ခု ဖန်တီးပြီး၊ ၎င်း၏ .github/workflows/ directory ထဲတွင် node-ci.yml ကို on: workflow_call ဖြင့် ထားပါ။ node-version ကို default ပါသော string input အဖြစ်၊ run-lint ကို boolean input အဖြစ်၊ npm-token ကို optional secret အဖြစ် ကြေညာပါ။ Job များအတွင်းတွင် ၎င်းတို့ကို inputs.node-version နှင့် secrets.npm-token အဖြစ် ဖတ်သည် — တခြား context တစ်ခုကို ဖတ်သကဲ့သို့ပင်။

အသုံးပြုသော repository တစ်ခုသည် ၎င်း၏ CI file တစ်ခုလုံးကို လိုင်းအနည်းငယ်အဖြစ် လျှော့ချလိုက်နိုင်သည် — jobs.test အောက်တွင် uses: my-org/ci-workflows/.github/workflows/node-ci.yml@v1 ရေးပြီး၊ with: အောက်တွင် node-version ပေးကာ၊ secrets: အောက်တွင် npm-token ကို ${{ secrets.NPM_TOKEN }} ဖြင့် လွှဲပေးလိုက်ရုံပင်။

Caller တွင် ဘာမပါသလဲ သတိပြုပါ — runs-on မပါ၊ steps မပါ။ တစ်ခုခု ထည့်လိုက်လျှင် schema error ဖြစ်ပြီး၊ ရှိပြီးသား job တစ်ခုကို ပြောင်းလဲသောအခါ အဖြစ်များဆုံး အမှားဖြစ်သည်။

လုပ်ငန်းပိုင်းဆိုင်ရာ အချက်နှစ်ခု။ Secret များကို အလိုအလျောက် အမွေမဆက်ခံပါ — တိတိကျကျ လွှဲပေးရမည်၊ သို့မဟုတ် ခေါ်ခံရသူသည် caller ရှိသမျှ လက်ခံရမည်ဆိုလျှင် secrets: inherit ဟု ရေးရမည်။ ထို့အပြင် @v1 သည် သင် တမင်ရွှေ့သော tag ဖြစ်သဖြင့် ပြင်ဆင်ချက်တစ်ခုသည် သင် push လုပ်သည့်အခိုက်တွင် မဟုတ်ဘဲ retag လုပ်သည့်အခါမှသာ caller အားလုံးသို့ ရောက်သည်။ Shared repo သည် private ဖြစ်လျှင် organisation အတွင်း တခြား repository များ ခေါ်ခွင့်ရှိအောင် visibility setting ကို ဖွင့်ပေးထားရမည်။

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

yaml
name: Reusable Node CI

on:
  workflow_call:
    inputs:
      node-version:
        description: Node major version to test against
        required: false
        default: '20'
        type: string
      run-lint:
        description: Whether to run the lint step
        required: false
        default: true
        type: boolean
    secrets:
      npm-token:
        description: Token for the private registry
        required: false
    outputs:
      test-result:
        description: Outcome of the test step
        value: ${{ jobs.test.outputs.result }}

jobs:
  test:
    runs-on: ubuntu-latest
    outputs:
      result: ${{ steps.run-tests.outputs.outcome }}
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
          cache: npm

      - name: Install dependencies
        run: npm ci
        env:
          NODE_AUTH_TOKEN: ${{ secrets.npm-token }}

      - name: Lint
        if: ${{ inputs.run-lint }}
        run: npm run lint

      - name: Test
        id: run-tests
        run: |
          npm test
          echo "outcome=passed" >> "$GITHUB_OUTPUT"
You should see
ဒီ file သည် သူ့ဘာသာသူ ဘယ်တော့မှ မ run ပါ — push သို့မဟုတ် pull_request trigger မရှိဘဲ workflow_call သာ ရှိသဖြင့် တခြား workflow တစ်ခုက ကိုးကားမှသာ အလုပ်လုပ်သည်။ Caller တစ်ခုက ကိုးကားလိုက်သောအခါ test job သည် caller ရဲ့ runner မဟုတ်ဘဲ ဒီ workflow ပိုင်ဆိုင်သော runner ပေါ်တွင် run သည်။ inputs.node-version သည် caller က with: အောက်တွင် ပေးလိုက်သည့် တန်ဖိုးဖြစ်လာပြီး၊ ဘာမှ မပေးလျှင် input က default ကြေညာထားသဖြင့် '20' ဖြစ်သည်။ Lint step သည် caller က run-lint ကို true အဖြစ် ပေးမှသာ run ပြီး၊ ဒါကလည်း ကြေညာထားသော default ဖြစ်သည်။ secrets.npm-token သည် caller က တိတိကျကျ မလွှဲပေးလျှင် empty string ဖြစ်နေမည်ဖြစ်၍၊ မေ့ကျန်ခဲ့သော caller တစ်ခုသည် parse ချိန်တွင်မဟုတ်ဘဲ private registry ကို install လုပ်ချိန်တွင် ကျရှုံးမည်။ Workflow ကြေညာထားသော output test-result သည် test step ရဲ့ ရလဒ်ကို ပြန်သယ်ဆောင်ပေးသဖြင့် ခေါ်ထားသော workflow သည် အောက်ဘက် job တစ်ခုတွင် needs မှတစ်ဆင့် ဖတ်နိုင်သည်။

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

တစ်ခုနှင့်တစ်ခု copy အဖြစ် စတင်ခဲ့သော repository နှစ်ခု၏ CI file များကို ရွေးပြီး diff ကြည့်ပါ — အဲဒီ ကွာခြားချက်များသည် ဒီ lesson ပြောနေသော drift ဖြစ်သည်။ တူညီသော အပိုင်းကို shared repository တစ်ခုရှိ reusable workflow အဖြစ် ရွှေ့ပြီး v1 tag တပ်ကာ၊ repo နှစ်ခုလုံးကို ခေါ်သုံးအောင် ပြောင်းပါ။ ထို့နောက် တခြား step များနှင့် filesystem မျှဝေဖို့ လိုအပ်သော step အစဉ်တစ်ခု — setup၊ cache၊ install — ကိုတော့ composite action အဖြစ် ထုပ်ပိုးပါ။ ဒါဆိုလျှင် နှစ်မျိုးလုံးကို တစ်ခုစီ တည်ဆောက်ပြီးဖြစ်၍ နယ်နိမိတ်ကို ခံစားမိလာလိမ့်မည်။

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

Reusable workflow ကို ညွှန်းသော uses: ပါသော job တစ်ခုသို့ runs-on သို့မဟုတ် steps ထည့်ခြင်း — caller job သည် နှစ်ခုလုံးကို မပေးရဘဲ၊ ထည့်လိုက်လျှင် workflow သည် parse မဖြစ်တော့ပါ။

Shared workflow ကို @main ဖြင့် ကိုးကားခြင်း — shared file ကို တစ်ယောက်ယောက် ပြင်လိုက်သည့်အခိုက် အသုံးပြုနေသော repository တိုင်း၏ အပြုအမူ ပြောင်းသွားပြီး၊ ပြန်လည်သုံးသပ်ရန် သို့မဟုတ် ပြန်ဆုတ်ရန် release တစ်ခုမှ မရှိပါ။

GitHub Docs - Reusing workflowsCI/CD with GitHub Actions

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

  • Reusable workflow ကို ညွှန်းသော uses: ပါသော job တစ်ခုသို့ runs-on သို့မဟုတ် steps ထည့်ခြင်း — caller job သည် နှစ်ခုလုံးကို မပေးရဘဲ၊ ထည့်လိုက်လျှင် workflow သည် parse မဖြစ်တော့ပါ။
  • Shared workflow ကို @main ဖြင့် ကိုးကားခြင်း — shared file ကို တစ်ယောက်ယောက် ပြင်လိုက်သည့်အခိုက် အသုံးပြုနေသော repository တိုင်း၏ အပြုအမူ ပြောင်းသွားပြီး၊ ပြန်လည်သုံးသပ်ရန် သို့မဟုတ် ပြန်ဆုတ်ရန် release တစ်ခုမှ မရှိပါ။
  • Workflow ကို production branch ပေါ် တိုက်ရိုက်မစမ်းဘဲ branch (သို့) test repository တွင် အရင်အတည်ပြုပါ။

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

တစ်ခုနှင့်တစ်ခု copy အဖြစ် စတင်ခဲ့သော repository နှစ်ခု၏ CI file များကို ရွေးပြီး diff ကြည့်ပါ — အဲဒီ ကွာခြားချက်များသည် ဒီ lesson ပြောနေသော drift ဖြစ်သည်။ တူညီသော အပိုင်းကို shared repository တစ်ခုရှိ reusable workflow အဖြစ် ရွှေ့ပြီး v1 tag တပ်ကာ၊ repo နှစ်ခုလုံးကို ခေါ်သုံးအောင် ပြောင်းပါ။ ထို့နောက် တခြား step များနှင့် filesystem မျှဝေဖို့ လိုအပ်သော step အစဉ်တစ်ခု — setup၊ cache၊ install — ကိုတော့ composite action အဖြစ် ထုပ်ပိုးပါ။ ဒါဆိုလျှင် နှစ်မျိုးလုံးကို တစ်ခုစီ တည်ဆောက်ပြီးဖြစ်၍ နယ်နိမိတ်ကို ခံစားမိလာလိမ့်မည်။

You'll know it worked when: ဒီ file သည် သူ့ဘာသာသူ ဘယ်တော့မှ မ run ပါ — push သို့မဟုတ် pull_request trigger မရှိဘဲ workflow_call သာ ရှိသဖြင့် တခြား workflow တစ်ခုက ကိုးကားမှသာ အလုပ်လုပ်သည်။ Caller တစ်ခုက ကိုးကားလိုက်သောအခါ test job သည် caller ရဲ့ runner မဟုတ်ဘဲ ဒီ workflow ပိုင်ဆိုင်သော runner ပေါ်တွင် run သည်။ inputs.node-version သည် caller က with: အောက်တွင် ပေးလိုက်သည့် တန်ဖိုးဖြစ်လာပြီး၊ ဘာမှ မပေးလျှင် input က default ကြေညာထားသဖြင့် '20' ဖြစ်သည်။ Lint step သည် caller က run-lint ကို true အဖြစ် ပေးမှသာ run ပြီး၊ ဒါကလည်း ကြေညာထားသော default ဖြစ်သည်။ secrets.npm-token သည် caller က တိတိကျကျ မလွှဲပေးလျှင် empty string ဖြစ်နေမည်ဖြစ်၍၊ မေ့ကျန်ခဲ့သော caller တစ်ခုသည် parse ချိန်တွင်မဟုတ်ဘဲ private registry ကို install လုပ်ချိန်တွင် ကျရှုံးမည်။ Workflow ကြေညာထားသော output test-result သည် test step ရဲ့ ရလဒ်ကို ပြန်သယ်ဆောင်ပေးသဖြင့် ခေါ်ထားသော workflow သည် အောက်ဘက် job တစ်ခုတွင် needs မှတစ်ဆင့် ဖတ်နိုင်သည်။

Reusable Workflow များနှင့် Composite Action များ | Thuta Learning