使用 GitHub Actions 定時接收短信並觸發其他 CI/CD 流程

實戰分享——每一條 YAML 都有註釋,每一個坑都提前替你踩平了。

真實的「人肉監控」場景:你有一項關鍵服務需要每天測試其短信驗證碼到達率,但手動操作耗時、易忘,而且無法與其他自動化流程聯動。如果能夠讓 GitHub Actions 每天定時自動完成這些工作,並將結果反饋到 CI/CD 流程中,就完美了。
核心承諾:本文將展示如何在 GitHub Actions 中設置一個定時任務,用它來接收虛擬號碼的短信驗證碼,然後將該事件作為觸發器,去啟動其他重要的 CI/CD 流程,全程無需人工干預。

一、完整的自動化工作流架構圖

這個架構的核心優勢在於兩點:完全免費(使用 GitHub 提供的免費額度)、可與其他任何 GitHub Actions 工作流無縫整合

[GitHub Actions 定時觸發器 (schedule)] │ ▼ [Job 1: 獲取號碼與接收短信] │ ├─ 調用接碼平台 API 獲取號碼 ├─ 在目標服務觸發驗證碼 ├─ 輪詢並接收短信 └─ 提取驗證碼 │ ▼ [Job 2: 觸發下游流程] │ ├─ repository_dispatch 到其他倉庫 └─ 觸發部署/測試/通知等操作

二、核心技術點一:安全存儲憑據

將接碼平台的 API Key 和 Webhook URL 等敏感信息,安全地存儲在 GitHub 倉庫的 Settings > Secrets and variables > Actions 中,而不是硬編碼在 YAML 文件裡。這是保障安全的第一步——如果洩露,攻擊者可能利用你的 API 額度。

配置步驟

  1. 進入你的 GitHub 倉庫。
  2. 點擊 Settings 標籤頁。
  3. 在左側選單中找到 Secrets and variablesActions
  4. 點擊 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_KEYREPO_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 通知」。
③ 當你適應這套模式後,你會發現測試、監控、甚至警報的自動化都可以圍繞著這個「收碼」的信號來構建——每次收到驗證碼,都可以自動觸發一整套測試流水線。