使用 GitHub Actions 定時接收短信並觸發其他 CI/CD 流程
實戰分享——每一條 YAML 都有註釋,每一個坑都提前替你踩平了。
核心承諾:本文將展示如何在 GitHub Actions 中設置一個定時任務,用它來接收虛擬號碼的短信驗證碼,然後將該事件作為觸發器,去啟動其他重要的 CI/CD 流程,全程無需人工干預。
一、完整的自動化工作流架構圖
這個架構的核心優勢在於兩點:完全免費(使用 GitHub 提供的免費額度)、可與其他任何 GitHub Actions 工作流無縫整合。
二、核心技術點一:安全存儲憑據
將接碼平台的 API Key 和 Webhook URL 等敏感信息,安全地存儲在 GitHub 倉庫的 Settings > Secrets and variables > Actions 中,而不是硬編碼在 YAML 文件裡。這是保障安全的第一步——如果洩露,攻擊者可能利用你的 API 額度。
配置步驟
- 進入你的 GitHub 倉庫。
- 點擊 Settings 標籤頁。
- 在左側選單中找到 Secrets and variables → Actions。
- 點擊 New repository secret,分別添加以下 Secrets:
SMS_API_KEY(你的接碼平台 API Key)、REPO_DISPATCH_TOKEN(具有 repo 權限的 GitHub Personal Access Token)。
在工作流 YAML 文件中,透過 ${{ secrets.SMS_API_KEY }} 語法來安全地引用這些值。它們在執行時會被注入為環境變數,日誌中會自動遮蔽明文,不會暴露。
三、核心技術點二:編寫定時接收短信的工作流
4.1 配置 schedule 觸發器
on.schedule.cron 使用 POSIX cron 語法。以下範例表示每天 UTC 時間 10:00 觸發一次。GitHub Actions 的定時觸發可能會有幾分鐘延遲,如果需要精確到秒的場景,建議配合 workflow_dispatch 手動觸發作為補充。
on:
schedule:
- cron: '0 10 * * *' # 每天 UTC 10:00
workflow_dispatch: # 允許手動觸發
4.2 Job 定義與運行環境
jobs:
receive-sms:
runs-on: ubuntu-latest
outputs:
otp_code: ${{ steps.extract.outputs.otp_code }}
steps:
4.3 第一個 Step:檢出代碼
- name: 檢出代碼
uses: actions/checkout@v4
4.4 第二個 Step:獲取號碼並接收短信
這是整個工作流的核心步驟。使用 cURL 命令調用接碼平台 API 獲取一個號碼,然後在目標服務上觸發驗證碼,最後輪詢並接收最新短信。腳本中使用 grep -oP '\d{6}' 來提取 6 位數字驗證碼。
- name: 獲取號碼並接收短信
id: receive
env:
API_KEY: ${{ secrets.SMS_API_KEY }}
run: |
# 獲取虛擬號碼(以 5sim 為例,可替換為 SMSPool 等其他平台)
RESP=$(curl -s "https://5sim.net/v1/user/buy/activation/usa/any/google" \
-H "Authorization: Bearer $API_KEY" \
-H "Accept: application/json")
PHONE=$(echo "$RESP" | jq -r '.phone')
ACTIVATION_ID=$(echo "$RESP" | jq -r '.id')
echo "📞 已獲取號碼: $PHONE (ID: $ACTIVATION_ID)"
# 在目標服務觸發驗證碼(此處以 Google 為例)
curl -s "https://accounts.google.com/signup/v2/webcreateaccount" \
-X POST \
-H "User-Agent: Mozilla/5.0" \
-d "phone=$PHONE" -o /dev/null -w "狀態碼: %{http_code}\n"
# 輪詢短信(最多 60 秒)
for i in $(seq 1 12); do
sleep 5
SMS_RESP=$(curl -s "https://5sim.net/v1/user/check/$ACTIVATION_ID" \
-H "Authorization: Bearer $API_KEY")
STATUS=$(echo "$SMS_RESP" | jq -r '.status')
if [ "$STATUS" = "RECEIVED" ]; then
SMS_TEXT=$(echo "$SMS_RESP" | jq -r '.sms[0].text')
echo "📩 收到短信: $SMS_TEXT"
OTP=$(echo "$SMS_TEXT" | grep -oP '\d{6}' | head -1)
echo "🔑 提取驗證碼: $OTP"
echo "otp_code=$OTP" >> $GITHUB_OUTPUT
echo "phone=$PHONE" >> $GITHUB_OUTPUT
exit 0
fi
echo "⏳ 第 $i/12 次查詢..."
done
echo "❌ 短信接收超時"
exit 1
4.5 第三個 Step:將驗證碼設為輸出變數
在步驟的 run 塊中使用 echo "otp_code=123456" >> $GITHUB_OUTPUT 將提取到的驗證碼存儲為作業的輸出變數,供後續作業使用。這是最安全的跨步驟數據傳遞方式,不會將敏感信息暴露在日誌中。
- name: 提取驗證碼並設為輸出
id: extract
run: |
echo "otp_code=${{ steps.receive.outputs.otp_code }}" >> $GITHUB_OUTPUT
echo "phone=${{ steps.receive.outputs.phone }}" >> $GITHUB_OUTPUT
四、核心技術點三:觸發下游 CI/CD 流程
這個工作流不僅是收取短信,它的真正力量在於成為自動化鏈路的起點——將「收到一條新的驗證碼」作為任何一個自動化流程的觸發信號。
觸發選項一:使用 repository_dispatch 觸發其他存儲庫
使用 curl 命令,在提取到驗證碼後,向另一個存儲庫的 API 發送 repository_dispatch 事件,將驗證碼值和相關信息作為事件負載傳遞過去,從而啟動那個倉庫中定義的自動化工作流。下游倉庫需要配置 on: repository_dispatch 來接收此事件。
- name: 觸發下游倉庫的測試工作流
env:
PAT: ${{ secrets.REPO_DISPATCH_TOKEN }}
run: |
curl -X POST \
"https://api.github.com/repos/OWNER/DOWNSTREAM_REPO/dispatches" \
-H "Authorization: Bearer $PAT" \
-H "Accept: application/vnd.github.v3+json" \
-d '{
"event_type": "sms_received",
"client_payload": {
"otp_code": "'"${{ needs.receive-sms.outputs.otp_code }}"'",
"phone": "'"${{ needs.receive-sms.outputs.phone }}"'",
"received_at": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"
}
}'
觸發選項二:使用 workflow_dispatch 觸發同一倉庫內的其他工作流
使用 gh workflow run 命令,在當前倉庫內啟動另一個手動運行的工作流,並將驗證碼作為輸入參數傳遞。目標工作流需要定義 on: workflow_dispatch 並聲明 inputs 參數。
- name: 觸發同一倉庫的部署工作流
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh workflow run deploy-after-sms.yml \
-f otp_code="${{ needs.receive-sms.outputs.otp_code }}" \
-f phone="${{ needs.receive-sms.outputs.phone }}"
觸發選項三:通知推送
在這個工作流中調用 Slack、Telegram 或郵件 API,將收到的驗證碼直接推送給相關人員或頻道,實現即時通知。
五、完整的工作流展示
以下是一個可直接複製到 .github/workflows/sms-receiver.yml 的完整範例,將上述所有步驟整合在一起。使用前請確保已在 GitHub Secrets 中配置好 SMS_API_KEY 和 REPO_DISPATCH_TOKEN。
name: 定時接收短信並觸發下游 CI/CD
on:
schedule:
- cron: '0 10 * * *' # 每天 UTC 10:00 執行
workflow_dispatch: # 允許手動觸發測試
jobs:
receive-sms:
runs-on: ubuntu-latest
outputs:
otp_code: ${{ steps.extract.outputs.otp_code }}
phone: ${{ steps.extract.outputs.phone }}
steps:
- name: 檢出代碼
uses: actions/checkout@v4
- name: 獲取號碼並接收短信
id: receive
env:
API_KEY: ${{ secrets.SMS_API_KEY }}
run: |
RESP=$(curl -s "https://5sim.net/v1/user/buy/activation/usa/any/google" \
-H "Authorization: Bearer $API_KEY" \
-H "Accept: application/json")
PHONE=$(echo "$RESP" | jq -r '.phone')
ACTIVATION_ID=$(echo "$RESP" | jq -r '.id')
echo "📞 已獲取號碼: $PHONE"
curl -s "https://accounts.google.com/signup/v2/webcreateaccount" \
-X POST -H "User-Agent: Mozilla/5.0" \
-d "phone=$PHONE" -o /dev/null -w "狀態碼: %{http_code}\n"
for i in $(seq 1 12); do
sleep 5
SMS_RESP=$(curl -s "https://5sim.net/v1/user/check/$ACTIVATION_ID" \
-H "Authorization: Bearer $API_KEY")
STATUS=$(echo "$SMS_RESP" | jq -r '.status')
if [ "$STATUS" = "RECEIVED" ]; then
SMS_TEXT=$(echo "$SMS_RESP" | jq -r '.sms[0].text')
echo "📩 $SMS_TEXT"
OTP=$(echo "$SMS_TEXT" | grep -oP '\d{6}' | head -1)
echo "🔑 $OTP"
echo "otp_code=$OTP" >> $GITHUB_OUTPUT
echo "phone=$PHONE" >> $GITHUB_OUTPUT
exit 0
fi
echo "⏳ 第 $i/12 次查詢..."
done
echo "❌ 短信接收超時"
exit 1
- name: 提取驗證碼並設為輸出
id: extract
run: |
echo "otp_code=${{ steps.receive.outputs.otp_code }}" >> $GITHUB_OUTPUT
echo "phone=${{ steps.receive.outputs.phone }}" >> $GITHUB_OUTPUT
trigger-downstream:
needs: receive-sms
runs-on: ubuntu-latest
steps:
- name: 透過 repository_dispatch 觸發下游倉庫
env:
PAT: ${{ secrets.REPO_DISPATCH_TOKEN }}
run: |
curl -X POST \
"https://api.github.com/repos/OWNER/DOWNSTREAM_REPO/dispatches" \
-H "Authorization: Bearer $PAT" \
-H "Accept: application/vnd.github.v3+json" \
-d '{
"event_type": "sms_received",
"client_payload": {
"otp_code": "'"${{ needs.receive-sms.outputs.otp_code }}"'",
"phone": "'"${{ needs.receive-sms.outputs.phone }}"'"
}
}'
- name: 觸發同一倉庫的其他工作流
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh workflow run deploy-after-sms.yml \
-f otp_code="${{ needs.receive-sms.outputs.otp_code }}" \
-f phone="${{ needs.receive-sms.outputs.phone }}"
六、排坑指南
坑一:GitHub Actions 定時任務延遲
症狀:schedule 觸發器設定的時間到了,但工作流沒有準時執行,可能延遲 10-20 分鐘。
原因:GitHub 在高負載時段會排隊處理定時任務,schedule 不以秒級精度保證。
解法:對實時性要求極高的場景,可配合 workflow_dispatch 手動觸發作為補充。如果需要更精確的定時,可以考慮使用外部監控服務(如 cron-job.org)定時向 GitHub API 發送 repository_dispatch 請求來觸發工作流。
坑二:環境變數與 Secrets 混用導致洩漏
症狀:使用 echo $API_KEY 或將 Secrets 賦值給普通環境變數後輸出,可能導致敏感信息暴露在工作流日誌中。
解法:始終使用 ${{ secrets.SMS_API_KEY }} 直接引用 Secrets,GitHub 會自動在日誌中遮蔽這些值。跨步驟傳遞數據時,使用 GITHUB_OUTPUT 而非環境變數。避免在 run 塊中使用 set -x 調試模式,因為它會打印所有命令和變數值。
坑三:跨倉庫 repository_dispatch 權限問題
症狀:發送 repository_dispatch 請求後返回 HTTP 404 或 403,下游倉庫沒有收到事件。
原因:觸發下游存儲庫需要該存儲庫接受來自上游的事件,或使用的 Personal Access Token 沒有足夠權限(需要 repo scope)。
解法:在下游倉庫的 Settings > Actions > General 中,確保「Allow all actions and reusable workflows」已啟用。創建 PAT 時勾選 repo 權限,並將其存儲為上游倉庫的 REPO_DISPATCH_TOKEN Secret。下游工作流需要在 on: 中聲明 repository_dispatch。
坑四:cURL 請求在 GitHub Actions 中逾時
症狀:接碼平台的 API 請求偶爾卡住,導致整個工作流在輪詢步驟中掛起,直到達到 6 小時的默認超時上限。
解法:在每次 cURL 請求中添加 --max-time 15 參數,確保單次請求不超過 15 秒。在輪詢循環中設定最大嘗試次數(如 12 次 × 5 秒 = 60 秒上限),超時後優雅退出並記錄錯誤日誌,避免浪費 GitHub Actions 的運行分鐘數。
結語:將驗證碼事件融入你的整個 DevOps 自動化體系
這套方案的本質是將虛擬號碼的短信接收能力,變成了一個免費、可靠、完全可編程的 CI/CD 組件。你現在可以將「收到一條新的驗證碼」作為任何一個自動化流程的起點。
① 先從簡單的一個工作流開始,只做「收短信 → 寫日誌」,確保短信接收環節暢通。
② 跑通之後,增加一個下游觸發,例如「收到驗證碼後自動發一條 Slack 通知」。
③ 當你適應這套模式後,你會發現測試、監控、甚至警報的自動化都可以圍繞著這個「收碼」的信號來構建——每次收到驗證碼,都可以自動觸發一整套測試流水線。