Thuta Learning
Bash / Shell Scripting
ExercisesDevOps & Toolsbeginner

လေ့ကျင့်ခန်း — Deployment Script ရှိ Bug ကို ရှာဖွေပြင်ဆင်ခြင်း

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

  • လေ့ကျင့်ခန်း — Deployment Script ရှိ Bug ကို ရှာဖွေပြင်ဆင်ခြင်း concept ကို နားလည်ရှင်းပြနိုင်ရန်
  • နမူနာ code ကို ကိုယ်တိုင် run ပြီး output စစ်နိုင်ရန်
  • Tutorial Platform project နှင့် production scenario တွင် မှန်ကန်စွာအသုံးချနိုင်ရန်

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

ဤလေ့ကျင့်ခန်းသည် bash တွင် အလွန်အဖြစ်များသော bug class တစ်ခုကို နက်နက်ရှိုင်းရှိုင်း လေ့လာစေသည် — item များစာရင်းတစ်ခုကို plain string variable တစ်ခုအဖြစ်သာ သိမ်းထားပြီး ထို string ကို unquoted expansion ဖြင့် loop ပတ်ခြင်းကြောင့် တိတ်တဆိတ် word splitting ဖြစ်ပေါ်လာခြင်းဖြစ်သည်။ ဤနေရာတွင် symptom တစ်ခုတည်းထက် bug class ကိုယ်တိုင်က ပို၍ အရေးကြီးသည် — for f in $FILES ဟု script တစ်ခုတည်းတွင် ဘယ်နေရာမဆို ရေးလိုက်လျှင် bash သည် IFS (default အားဖြင့် space, tab, newline) ကို အသုံးပြု၍ ထို value ကို အမြဲ ခွဲထုတ်မည်ဖြစ်ပြီး value ထဲတွင် ဘာပါနေသည်ကို ဂရုမစိုက်ပါ။ ဤ bug class ကို အထူးအန္တရာယ်ရှိစေသည့်အချက်မှာ ကျရှုံးသည့်အခြေအနေတွင်ပင် error message တစ်ခုမျှ မထွက်ခြင်းဖြစ်သည် — cp ကို real file တစ်ခုနှင့်မျှ မကိုက်ညီသည့် argument များနှင့် ခေါ်လိုက်ရုံဖြင့် ထို call အတွက်သာ ကျရှုံးပြီး loop က next word သို့ ဆက်သွားသည်၊ 'release' နှင့် 'notes.txt' တို့သည် တကယ်တမ်း file တစ်ခုတည်း ('release notes.txt') မှ ခွဲထွက်လာသည်ကို မည်သူမျှ မသတိထားမိပါ။ ထို့အပြင် script ကိုယ်တိုင်၏ success message — ls | wc -l မှ ရရှိသော file count — သည် တကယ်ရှိသင့်သည့် file အရေအတွက်ကို မမှတ်ဘဲ release directory ထဲတွင် နောက်ဆုံး ရှိနေသော file အရေအတွက်ကိုသာ report ပြုလုပ်သောကြောင့် partial deploy တစ်ခုပင် 'complete' ဟု ပြသနေသည်။ ဤ bug ကို ship မလုပ်မီ ရှာဖွေတွေ့ရှိစေမည့် mental habit မှာ bare $variable expansion တိုင်းကို ဖြစ်နိုင်ချေရှိသော compile error တစ်ခုအဖြစ် သတ်မှတ်ခြင်းဖြစ်သည် — quote မလုပ်ဘဲ ရေးမည့်မတိုင်မီ 'ဤ value တွင် space, glob character ပါနိုင်သလား၊ empty ဖြစ်နိုင်သလား' ဟု မေးရန်နှင့် item တစ်ခုထက်ပိုသော list မှန်သမျှကို test data ရိုးရှင်းနေစဉ် အလုပ်ဖြစ်နေသော string တစ်ခုအဖြစ်မဟုတ်ဘဲ line ပထမမှစ၍ array တစ်ခုအဖြစ် သဘောထားရန်ဖြစ်သည်။ default အားဖြင့် unquoted expansion များကို flag ပြုပေးသော shellcheck ဖြင့် script များကို run ကြည့်ခြင်းသည် ဤ habit တူတူကို automate လုပ်ထားသည့် နည်းလမ်းဖြစ်သည်။

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

deploy.sh သည် ကူးယူရမည့် file များစာရင်းကို FILES="app.conf release notes.txt server.py" ဟူသော string တစ်ခုတည်းတွင် သိမ်းထားပြီး for f in $FILES ဖြင့် loop ပတ်သည် — $FILES ကို quote လုံးဝ မလုပ်ထားပါ။ ဤ line က တကယ်ဘာလုပ်သည်ကို တစ်ဆင့်ချင်း လိုက်ကြည့်ပါ- bash သည် $FILES ကို app.conf release notes.txt server.py ဟူသော raw text အဖြစ် expand လုပ်ပြီး၊ expansion ကို quote မလုပ်ထားသောကြောင့် for loop သို့ မပေးမီ ထို text ကို whitespace အလိုက် ခွဲထုတ်လိုက်သည်၊ ထို့ကြောင့် loop က file သုံးခုကို မမြင်ဘဲ word လေးခု — app.conf, release, notes.txt, server.py ကို မြင်ရသည်။ ပထမနှင့် နောက်ဆုံး word တို့သည် real filename များ ဖြစ်နေသဖြင့် cp "$SOURCE_DIR/app.conf" နှင့် cp "$SOURCE_DIR/server.py" တို့သည် ပုံမှန် အောင်မြင်သည်။ အလယ်ပိုင်း word နှစ်ခုမှာမူ မအောင်မြင်ပါ — cp သည် ./build/release ကို stat လုပ်ရန် ကြိုးစားပြီးနောက် ./build/notes.txt ကို ကြိုးစားသည်၊ နှစ်ခုစလုံး 'No such file or directory' ဖြင့် ကျရှုံးပြီး real file ဖြစ်သော release notes.txt ကိုမူ cp command တစ်ခုမျှ တကယ် ထိတွေ့ခြင်းမရှိပါ — bash generate လုပ်ပေးသော command တိုင်းတွင် ၎င်းသည် လုံးဝ ပျောက်ဆုံးနေသည်။ script က cp ၏ exit status ကို ဘယ်တော့မှ မစစ်ဆေးသောကြောင့် ထို failure နှစ်ခုသည် stderr ပေါ်တွင် print ထွက်ပြီး loop က ဘာမှမဖြစ်သလို next word သို့ ဆက်သွားသည်။ နောက်ဆုံး line ကို ကြည့်ပါ- ၎င်းသည် $RELEASE_DIR ထဲတွင် ရှိနှင့်ပြီးသား file များကို ls | wc -l ဖြင့် ရေတွက်ပြီး 2 ဟု report ပြုလုပ်သည် (တကယ် ကူးယူနိုင်ခဲ့သော file နှစ်ခု) — 3 မဟုတ်၊ error မဟုတ်၊ log ကို နှစ်ကြိမ်စစ်ဆေးမည့်သူ မည်သူမျှ သံသယ ဝင်မည်မဟုတ်လောက်အောင် ယုတ္တိရှိနေသော number တစ်ခုမျှသာ ဖြစ်သည်။ ဤ failure ကို ပြန်လည်ဖန်တီးခြင်းဆိုသည်မှာ script ကို ယခုအတိုင်း run ပြီး ပုံမှန်ပုံစံ output ထဲတွင် ဝှက်နေသော 'cannot stat' line နှစ်ကြောင်းကို သတိထားကြည့်ပြီး ၎င်းတို့ကို for line ထဲရှိ quote မလုပ်ထားသော $FILES expansion တစ်ခုတည်းနှင့် ပြန်လည် ချိတ်ဆက်ရန် ဖြစ်သည်။

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

bash
#!/usr/bin/env bash
# deploy.sh - copies build artifacts into the release directory and restarts the app

SOURCE_DIR="./build"
RELEASE_DIR="./releases/current"
FILES="app.conf release notes.txt server.py"

mkdir -p "$SOURCE_DIR" "$RELEASE_DIR"

# Set up fake build artifacts so this demo is self-contained
touch "$SOURCE_DIR/app.conf"
touch "$SOURCE_DIR/release notes.txt"
touch "$SOURCE_DIR/server.py"

echo "Deploying files to $RELEASE_DIR..."

for f in $FILES; do
  cp "$SOURCE_DIR/$f" "$RELEASE_DIR/"
  echo "  copied: $f"
done

echo "Restarting app service..."
echo "Deployment complete. $(ls "$RELEASE_DIR" | wc -l) file(s) now in the release directory."
You should see
Deploying files to ./releases/current...
  copied: app.conf
cp: cannot stat './build/release': No such file or directory
  copied: release
cp: cannot stat './build/notes.txt': No such file or directory
  copied: notes.txt
  copied: server.py
Restarting app service...
Deployment complete. 2 file(s) now in the release directory.

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

script ကို run ပြီး နောက်ဆုံးတွင် ပြသည့် 'file(s) now in the release directory' အရေအတွက်ကို ကူးယူသင့်သော file သုံးခုနှင့် နှိုင်းယှဉ်ကြည့်ပါ။ ထို့နောက် loop ၏ အလယ်ပိုင်း iteration နှစ်ခုတွင် ပြသည့် 'copied:' line များကို သေချာကြည့်ပါ — ၎င်းတို့သည် ./build ထဲရှိ file စစ်စစ် တစ်ခုနှင့်မျှ မကိုက်ညီပါ။ space ပါဝင်သော file list ကို word splitting မှ မကာကွယ်ဘဲ expand လုပ်နေသည့် line ကို ရှာပြီး filename တစ်ခုတွင် space ပါလာလျှင်ပင် item တစ်ခုတည်းအဖြစ် ဆက်လက်ရှင်သန်နေအောင် file list ကို သိမ်းဆည်းပုံနှင့် loop ပတ်ပုံကို ပြန်လည်ဖွဲ့စည်းပါ။

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

space ပါနိုင်သည့် item များစာရင်းကို space-separated string variable တစ်ခုတည်းဖြင့် သိမ်းထားခြင်း — item တစ်ခုတွင် space ပါလာပါက ဘေးကင်းစွာ loop ပတ်၍ မရနိုင်တော့ပါ

'Deployment complete' ဟု print ထုတ်ပြီး exit code 0 ပြန်ပေးသော script တစ်ခုသည် ဆိုလိုသည့်အတိုင်း အမှန်တကယ် ပြီးမြောက်ခဲ့သည်ဟု ယူဆခြင်း — success message ကို echo ဖြင့်သာ ယုံကြည်မနေဘဲ အမှန်တကယ် count/check ဖြင့် အတည်ပြုရန်

Greg's Wiki — BashPitfallsBash / Shell Scripting

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

  • space ပါနိုင်သည့် item များစာရင်းကို space-separated string variable တစ်ခုတည်းဖြင့် သိမ်းထားခြင်း — item တစ်ခုတွင် space ပါလာပါက ဘေးကင်းစွာ loop ပတ်၍ မရနိုင်တော့ပါ
  • 'Deployment complete' ဟု print ထုတ်ပြီး exit code 0 ပြန်ပေးသော script တစ်ခုသည် ဆိုလိုသည့်အတိုင်း အမှန်တကယ် ပြီးမြောက်ခဲ့သည်ဟု ယူဆခြင်း — success message ကို echo ဖြင့်သာ ယုံကြည်မနေဘဲ အမှန်တကယ် count/check ဖြင့် အတည်ပြုရန်
  • နမူနာ code ကို production system ပေါ် တိုက်ရိုက်မစမ်းဘဲ local/test environment တွင် အရင်အတည်ပြုပါ။

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

script ကို run ပြီး နောက်ဆုံးတွင် ပြသည့် 'file(s) now in the release directory' အရေအတွက်ကို ကူးယူသင့်သော file သုံးခုနှင့် နှိုင်းယှဉ်ကြည့်ပါ။ ထို့နောက် loop ၏ အလယ်ပိုင်း iteration နှစ်ခုတွင် ပြသည့် 'copied:' line များကို သေချာကြည့်ပါ — ၎င်းတို့သည် ./build ထဲရှိ file စစ်စစ် တစ်ခုနှင့်မျှ မကိုက်ညီပါ။ space ပါဝင်သော file list ကို word splitting မှ မကာကွယ်ဘဲ expand လုပ်နေသည့် line ကို ရှာပြီး filename တစ်ခုတွင် space ပါလာလျှင်ပင် item တစ်ခုတည်းအဖြစ် ဆက်လက်ရှင်သန်နေအောင် file list ကို သိမ်းဆည်းပုံနှင့် loop ပတ်ပုံကို ပြန်လည်ဖွဲ့စည်းပါ။

You'll know it worked when: Deploying files to ./releases/current... copied: app.conf cp: cannot stat './build/release': No such file or directory copied: release cp: cannot stat './build/notes.txt': No such file or directory copied: notes.txt copied: server.py Restarting app service... Deployment complete. 2 file(s) now in the release directory.

လေ့ကျင့်ခန်း — Deployment Script ရှိ Bug ကို ရှာဖွေပြင်ဆင်ခြင်း | Thuta Learning