name: 发布验收测试 on: workflow_run: workflows: - 测试环境部署 types: - completed branches: - internal - main workflow_dispatch: permissions: contents: read actions: read concurrency: group: release-acceptance cancel-in-progress: false env: ACCEPTANCE_ARTIFACT_NAME: release-acceptance-${{ github.run_id }}-${{ github.run_attempt }} ACCEPTANCE_REPORT_DIR: acceptance-artifacts ACCEPTANCE_REPORT_PATH: acceptance-artifacts/acceptance-report.json ACCEPTANCE_TEST_JSON: acceptance-artifacts/go-test.json ACCEPTANCE_FAILURE_LOG: acceptance-artifacts/failure.log ACCEPTANCE_NODE_SERVER_ID: ${{ vars.ACCEPTANCE_NODE_SERVER_ID || '31' }} ACCEPTANCE_NODE_PROTOCOL: ${{ vars.ACCEPTANCE_NODE_PROTOCOL || 'trojan' }} jobs: acceptance: name: Staging acceptance runs-on: ubuntu-latest timeout-minutes: 20 env: STAGING_BASE_URL: ${{ secrets.STAGING_BASE_URL || vars.STAGING_BASE_URL }} if: >- ${{ github.event_name == 'workflow_dispatch' || ( github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'push' ) }} steps: - name: 检出代码 uses: actions/checkout@v6 with: ref: ${{ github.event.workflow_run.head_sha || github.sha }} - name: 配置 Go 环境 uses: actions/setup-go@v6 with: go-version-file: go.mod cache: true - name: 准备报告目录 run: mkdir -p "$ACCEPTANCE_REPORT_DIR" - name: 校验必需配置 env: ACCEPTANCE_ADMIN_EMAIL: ${{ secrets.ACCEPTANCE_ADMIN_EMAIL }} ACCEPTANCE_ADMIN_PASSWORD: ${{ secrets.ACCEPTANCE_ADMIN_PASSWORD }} ACCEPTANCE_USER_EMAIL: ${{ secrets.ACCEPTANCE_USER_EMAIL }} ACCEPTANCE_USER_PASSWORD: ${{ secrets.ACCEPTANCE_USER_PASSWORD }} STAGING_BASE_URL: ${{ secrets.STAGING_BASE_URL || vars.STAGING_BASE_URL }} STAGING_DB_HOST: ${{ secrets.STAGING_DB_HOST }} STAGING_DB_USER: ${{ secrets.STAGING_DB_USER }} STAGING_DB_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }} STAGING_DB_NAME: ${{ secrets.STAGING_DB_NAME }} STAGING_REDIS_ADDR: ${{ secrets.STAGING_REDIS_ADDR }} STAGING_REDIS_PASSWORD: ${{ secrets.STAGING_REDIS_PASSWORD }} run: | set -euo pipefail missing=() required=( ACCEPTANCE_ADMIN_EMAIL ACCEPTANCE_ADMIN_PASSWORD ACCEPTANCE_USER_EMAIL ACCEPTANCE_USER_PASSWORD STAGING_BASE_URL STAGING_DB_HOST STAGING_DB_USER STAGING_DB_PASSWORD STAGING_DB_NAME STAGING_REDIS_ADDR STAGING_REDIS_PASSWORD ) for key in "${required[@]}"; do if [ -z "${!key:-}" ]; then missing+=("$key") fi done if [ "${#missing[@]}" -gt 0 ]; then printf '缺少必需 GitHub Actions secrets/vars:\n' | tee "$ACCEPTANCE_FAILURE_LOG" printf -- '- %s\n' "${missing[@]}" | tee -a "$ACCEPTANCE_FAILURE_LOG" { echo "## 发布验收测试" echo echo "状态:配置缺失" echo echo "缺少以下 secrets/vars:" printf -- '- `%s`\n' "${missing[@]}" } >> "$GITHUB_STEP_SUMMARY" exit 1 fi - name: 下载依赖模块 run: go mod download - name: 运行 acceptance 测试 env: ACCEPTANCE_ADMIN_EMAIL: ${{ secrets.ACCEPTANCE_ADMIN_EMAIL }} ACCEPTANCE_ADMIN_PASSWORD: ${{ secrets.ACCEPTANCE_ADMIN_PASSWORD }} ACCEPTANCE_USER_EMAIL: ${{ secrets.ACCEPTANCE_USER_EMAIL }} ACCEPTANCE_USER_PASSWORD: ${{ secrets.ACCEPTANCE_USER_PASSWORD }} ACCEPTANCE_NODE_SECRET: ${{ secrets.ACCEPTANCE_NODE_SECRET }} ACCEPTANCE_RUN_ID: qa_${{ github.run_id }}_${{ github.run_attempt }} STAGING_DB_HOST: ${{ secrets.STAGING_DB_HOST }} STAGING_DB_USER: ${{ secrets.STAGING_DB_USER }} STAGING_DB_PASSWORD: ${{ secrets.STAGING_DB_PASSWORD }} STAGING_DB_NAME: ${{ secrets.STAGING_DB_NAME }} STAGING_REDIS_ADDR: ${{ secrets.STAGING_REDIS_ADDR }} STAGING_REDIS_PASSWORD: ${{ secrets.STAGING_REDIS_PASSWORD }} STAGING_REDIS_DB: ${{ vars.STAGING_REDIS_DB || '0' }} run: | set -o pipefail go test -json ./tests/acceptance/... \ -staging-url="$STAGING_BASE_URL" \ -report-path="$ACCEPTANCE_REPORT_PATH" \ > >(tee "$ACCEPTANCE_TEST_JSON") \ 2> >(tee "$ACCEPTANCE_FAILURE_LOG" >&2) - name: 生成测试摘要 if: always() run: | set -euo pipefail { echo "## 发布验收测试" echo echo "- 状态:${{ job.status }}" echo "- Staging URL:${STAGING_BASE_URL}" echo "- Artifact:${ACCEPTANCE_ARTIFACT_NAME}" echo "- Run URL:${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" if [ "${{ github.event_name }}" = "workflow_run" ]; then echo "- Deploy run:${{ github.event.workflow_run.html_url }}" echo "- Deploy SHA:${{ github.event.workflow_run.head_sha }}" else echo "- 手动触发 SHA:${{ github.sha }}" fi echo } | tee "$ACCEPTANCE_REPORT_DIR/summary.md" >> "$GITHUB_STEP_SUMMARY" if [ "${{ job.status }}" = "failure" ]; then if [ -s "$ACCEPTANCE_FAILURE_LOG" ]; then { echo echo "Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" echo "Artifact: ${ACCEPTANCE_ARTIFACT_NAME}" } >> "$ACCEPTANCE_FAILURE_LOG" else { echo "Acceptance 测试失败。" echo "Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" echo "Artifact: ${ACCEPTANCE_ARTIFACT_NAME}" echo tail -100 "$ACCEPTANCE_TEST_JSON" 2>/dev/null || true } > "$ACCEPTANCE_FAILURE_LOG" fi elif [ ! -f "$ACCEPTANCE_FAILURE_LOG" ]; then : > "$ACCEPTANCE_FAILURE_LOG" fi - name: 上传 acceptance artifact if: always() uses: actions/upload-artifact@v6 with: name: ${{ env.ACCEPTANCE_ARTIFACT_NAME }} path: | ${{ env.ACCEPTANCE_REPORT_PATH }} ${{ env.ACCEPTANCE_TEST_JSON }} ${{ env.ACCEPTANCE_FAILURE_LOG }} ${{ env.ACCEPTANCE_REPORT_DIR }}/summary.md if-no-files-found: warn retention-days: 14 - name: Telegram 成功通知 if: success() env: TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }} run: | if [ -z "${TG_BOT_TOKEN:-}" ] || [ -z "${TG_CHAT_ID:-}" ]; then echo "Telegram 密钥未配置,跳过通知。" exit 0 fi MESSAGE="$(cat <<'EOF' ✅ 发布验收测试通过 仓库:${{ github.repository }} 分支:${{ github.event.workflow_run.head_branch || github.ref_name }} 提交:${{ github.event.workflow_run.head_sha || github.sha }} Staging:${{ env.STAGING_BASE_URL }} Artifact:${{ env.ACCEPTANCE_ARTIFACT_NAME }} 运行记录:${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} EOF )" curl -fsS -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \ --data-urlencode "chat_id=${TG_CHAT_ID}" \ --data-urlencode "text=${MESSAGE}" || echo "Telegram 通知发送失败,但验收结果不受影响。" - name: Telegram 失败通知 if: failure() env: TG_BOT_TOKEN: ${{ secrets.TG_BOT_TOKEN }} TG_CHAT_ID: ${{ secrets.TG_CHAT_ID }} run: | if [ -z "${TG_BOT_TOKEN:-}" ] || [ -z "${TG_CHAT_ID:-}" ]; then echo "Telegram 密钥未配置,跳过通知。" exit 0 fi MESSAGE="$(cat <<'EOF' ❌ 发布验收测试失败 仓库:${{ github.repository }} 分支:${{ github.event.workflow_run.head_branch || github.ref_name }} 提交:${{ github.event.workflow_run.head_sha || github.sha }} Staging:${{ env.STAGING_BASE_URL }} Artifact:${{ env.ACCEPTANCE_ARTIFACT_NAME }} 运行记录:${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} EOF )" curl -fsS -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \ --data-urlencode "chat_id=${TG_CHAT_ID}" \ --data-urlencode "text=${MESSAGE}" || echo "Telegram 通知发送失败,工作流失败状态已记录。"