နားလည်ထားရမယ့် အချက်
Workflow file တစ်ခုဟာ static YAML သက်သက် မဟုတ်ပါဘူး - ${{ }} အတွင်းမှာ expression တွေ ရေးလို့ရပြီး၊ job မ run ခင် အဲဒါတွေကို အရင် evaluate လုပ်ပါတယ်။ Expression တွေက context တွေဆီကနေ data ယူပါတယ်။ အသုံးအများဆုံးက github context ပါ - github.event_name (ဒီ run ကို ဘာက trigger လုပ်လဲ)၊ github.ref (ဘယ် branch သို့မဟုတ် tag လဲ)၊ github.sha (ဘယ် commit လဲ)၊ github.actor (ဘယ်သူက စတင်လဲ)၊ github.repository စတာတွေပါ။ အခြား context တွေက runner (runner.os, runner.temp)၊ job၊ steps (အရင် step ရဲ့ output တွေ)၊ env, vars နဲ့ secrets တို့ပါ။
Expression တွေဟာ comparison (==, !=)၊ logical operator (&&, ||, !) နဲ့ contains(), startsWith(), hashFiles(), always(), failure() လိုမျိုး function တွေကို ထောက်ပံ့ပါတယ်။ if: key ထဲမှာ တစ်ခုလုံးက expression ဖြစ်နေပြီးသားမို့ ${{ }} မထည့်ဘဲ ရေးလို့ရပါတယ်။
env ကို အဆင့်သုံးဆင့်မှာ သတ်မှတ်နိုင်ပါတယ် - workflow level (job အားလုံးအတွက်)၊ job level (အဲဒီ job ရဲ့ step အားလုံးအတွက်)၊ နဲ့ step level (အဲဒီ step တစ်ခုတည်းအတွက်)။ တူညီတဲ့ နာမည်ကို နှစ်နေရာ သတ်မှတ်ထားရင် အနီးဆုံးက အနိုင်ရပါတယ် - step က job ကို လွှမ်းမိုးပြီး၊ job က workflow ကို လွှမ်းမိုးပါတယ်။ ဒါက ရိုးရှင်းတယ်လို့ ထင်ရပေမယ့် workflow တစ်ခုလုံးမှာ NODE_ENV ကို production လို့ ထားပြီး test job တစ်ခုမှာ ဘာလို့ test မ run နိုင်လဲ ရှာမတွေ့ဘဲ နာရီဝက်ကုန်တာမျိုး ဖြစ်တတ်ပါတယ်။
နောက်ဆုံးအချက်က အရေးအကြီးဆုံးပါ။ Repository setting မှာ variables နဲ့ secrets ဆိုပြီး နှစ်မျိုး ရှိပါတယ်။ vars.X က plain text ဖြစ်ပြီး log ထဲမှာ အတိုင်းသား ပေါ်ပါတယ် - API base URL, region, feature flag လိုမျိုးတွေအတွက်ပါ။ secrets.X ကတော့ encrypt လုပ်ထားပြီး log ထဲမှာ *** နဲ့ ဖုံးပေးပါတယ်။ token, password, private key မှန်သမျှကို secrets ထဲမှာသာ ထားရပါမယ်။ masking ဟာ အပြည့်အဝ အာမခံချက် မဟုတ်တာကိုလည်း သတိထားပါ - secret ကို base64 လုပ်လိုက်တာ၊ ဖြတ်တောက်ပြီး echo လုပ်လိုက်တာမျိုးဆိုရင် mask မဖမ်းမိတော့ပါဘူး။
CONTEXTS IN, ENV PRECEDENCE DOWN
--------------------------------
WHERE VALUES COME FROM (read-only contexts)
github.* vars.* secrets.* runner.* steps.*
event_name plain encrypted os outputs of
ref, sha text masked *** temp earlier steps
actor
| | | | |
+----------+-----+------+-----------+------------+
v
${{ expression }}
== != && || ! contains() hashFiles()
ENV PRECEDENCE (the closest declaration wins)
workflow-level env: APP_ENV = base weakest
|
v overridden by
job-level env: APP_ENV = staging
|
v overridden by
step-level env: APP_ENV = test WINS in that step
a step with no env of its own sees staging, not base.လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
အောက်က workflow မှာ အလွှာသုံးဆင့်လုံးကို တစ်နေရာတည်းမှာ တွေ့ရပါမယ်။ file အပေါ်ဆုံးမှာ APP_ENV: base နဲ့ LOG_LEVEL: info ရှိပါတယ်။ report job က APP_ENV ကို staging လို့ ပြန်သတ်မှတ်ထားပြီး၊ "Env precedence" ဆိုတဲ့ step ကတော့ သူ့အတွက်ပဲ test လို့ ထပ်သတ်မှတ်ပါတယ်။ ဒါကြောင့် အဲဒီ step က APP_ENV=test ကို မြင်ပြီး၊ တခြား step တွေက staging ကို မြင်ကာ၊ LOG_LEVEL ကတော့ ဘယ်နေရာမှာမှ မလွှမ်းမိုးထားလို့ အားလုံးက info ကို မြင်ပါတယ်။
shell ထဲမှာ env variable ကို $APP_ENV နဲ့ ဖတ်တာနဲ့ ${{ env.APP_ENV }} နဲ့ ဖတ်တာ မတူပါဘူး - ဒုတိယနည်းက job မ run ခင် YAML ထဲကို string အဖြစ် အစားထိုးပြီးသား ဖြစ်နေပါတယ်။
secret ကို ကိုင်တွယ်ပုံကို အထူး သတိပြုပါ။ "Secret passed through env" step မှာ token ကို run: line ထဲ တိုက်ရိုက် မထည့်ဘဲ env: အောက်မှာ API_TOKEN အဖြစ် ပေးထားပါတယ်။ ဘာကြောင့်လဲဆိုတော့ ${{ }} က YAML ထဲကို စာသား အတိုင်းသား အစားထိုးလိုက်တာမို့၊ secret ထဲမှာ quote ဒါမှမဟုတ် $ ပါနေရင် shell က အဲဒါကို ကုဒ်အဖြစ် ဖတ်နိုင်ပါတယ် - script injection ဖြစ်နိုင်ပါတယ်။ env နဲ့ ပေးလိုက်ရင် တန်ဖိုးက variable တစ်ခုအဖြစ်သာ ရောက်သွားပါတယ်။ github.event.pull_request.title လိုမျိုး လူတွေ ရေးလို့ရတဲ့ တန်ဖိုးတွေအတွက်လည်း ဒီနည်းအတိုင်းပါပဲ။
အတူတူ စမ်းရေးကြည့်မယ်
name: Contexts and Variables
on:
push:
branches: [main]
workflow_dispatch:
env:
APP_ENV: base
LOG_LEVEL: info
jobs:
report:
runs-on: ubuntu-latest
env:
APP_ENV: staging
steps:
- uses: actions/checkout@v4
- name: Read the github context
run: |
echo "event: ${{ github.event_name }}"
echo "ref: ${{ github.ref }}"
echo "sha: ${{ github.sha }}"
echo "actor: ${{ github.actor }}"
echo "repo: ${{ github.repository }}"
echo "os: ${{ runner.os }}"
- name: Env precedence
env:
APP_ENV: test
run: |
echo "APP_ENV is $APP_ENV"
echo "LOG_LEVEL is $LOG_LEVEL"
- name: This step has no env of its own
run: echo "APP_ENV is $APP_ENV"
- name: Repository variable, plain text and safe to print
run: echo "API base is ${{ vars.API_BASE_URL }}"
- name: Secret bound through env, never inlined into the script
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
run: ./scripts/publish.sh
- name: Only on a manual run
if: github.event_name == 'workflow_dispatch'
run: echo "Started by hand by ${{ github.actor }}"
main ကို push လုပ်တာ ဒါမှမဟုတ် Run workflow ခလုတ် နှိပ်တာနဲ့ report job တစ်ခုတည်း run ပါတယ်။ ပထမ step က trigger, ref, sha, actor, repository နဲ့ runner OS တို့ကို log ထဲ ရေးပါတယ်။ "Env precedence" step က APP_ENV အတွက် test ကို ပြပြီး LOG_LEVEL အတွက် info ကို ပြပါတယ်။ သူ့နောက်က env မပါတဲ့ step ကတော့ job level က staging ကို ပြပါတယ် - workflow level က base ကို ဘယ်တော့မှ မမြင်ပါဘူး။ vars.API_BASE_URL ကို repository မှာ သတ်မှတ်ထားရင် အတိုင်းသား ပေါ်ပြီး၊ မသတ်မှတ်ထားရင် ဗလာ ဖြစ်နေပါတယ်။ secrets.API_TOKEN ကတော့ env အဖြစ်သာ ရောက်သွားပြီး log ထဲမှာ ထင်ရှားစွာ ပေါ်ရင် *** နဲ့ ဖုံးခံရပါတယ်။ နောက်ဆုံး step က workflow_dispatch run မှသာ run ပြီး၊ push run မှာ skipped ဖြစ်ပါတယ်။၅ မိနစ် စမ်းကြည့်
ဒီ workflow ကို ထည့်ပြီး push လုပ်ကာ log ထဲက APP_ENV သုံးကြောင်းကို နှိုင်းယှဉ်ကြည့်ပါ။ ပြီးရင် job level env ကို ဖျက်လိုက်ပြီး env မပါတဲ့ step က ဘာပြောင်းသွားလဲ ကြည့်ပါ။ နောက်တစ်ဆင့်အနေနဲ့ repository Settings > Secrets and variables > Actions မှာ API_BASE_URL ကို variable အဖြစ်၊ API_TOKEN ကို secret အဖြစ် ထည့်ပါ။ ပြီးရင် secret ကို echo လုပ်ကြည့်ပြီး log မှာ ဘယ်လိုပေါ်လဲ ကြည့်ပါ - ပြီးရင် အဲဒီ echo step ကို ချက်ချင်း ပြန်ဖျက်ပါ။
သတိလေးတစ်ချက်
credential တစ်ခုကို secret အစား repository variable (vars.*) ထဲ ထည့်မိတာ - variables တွေဟာ encrypt မလုပ်ထားဘဲ log ထဲမှာ အတိုင်းသား ပေါ်ပြီး mask မခံရပါဘူး။
လူရေးထားတဲ့ တန်ဖိုး (ဥပမာ ${{ github.event.pull_request.title }}) ကို run: script ထဲ တိုက်ရိုက် ထည့်လိုက်တာ - စာသားအတိုင်းသား အစားထိုးခံရလို့ shell က command အဖြစ် ဖတ်နိုင်ပြီး injection ဖြစ်ပါတယ်။ env နဲ့ ပေးပါ။
GitHub Docs - Contexts reference — CI/CD with GitHub Actions