From 377f13da48318c2e40555c5264a487a17fbca193 Mon Sep 17 00:00:00 2001 From: shanshanzhong147 Date: Thu, 4 Jun 2026 02:45:15 -0700 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E7=BD=AE(#6):=20=E6=96=B0=E5=A2=9E=20?= =?UTF-8?q?release-acceptance=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/release-acceptance.yml | 244 +++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 .github/workflows/release-acceptance.yml diff --git a/.github/workflows/release-acceptance.yml b/.github/workflows/release-acceptance.yml new file mode 100644 index 0000000..17e106c --- /dev/null +++ b/.github/workflows/release-acceptance.yml @@ -0,0 +1,244 @@ +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 通知发送失败,工作流失败状态已记录。"