# PR 合并后自动关闭关联的 Issue # 当 PR 从 ai_fix/N_xxx 分支合并到 main 时,关闭 #N 对应的 Issue(若 PR 描述中未含 Closes #N 则通过分支名解析) # 关闭后发送 Telegram 通知(复用 TELEGRAM_BOT_TOKEN / TELEGRAM_CHAT_ID) name: Close issue on PR merge on: pull_request: types: [closed] branches: [main] jobs: close-issue: if: github.event.pull_request.merged == true runs-on: ubuntu-latest permissions: issues: write pull-requests: read steps: - name: Get PR info id: pr run: | # 从 PR body 查找 Closes #N / Fixes #N(若已有则 GitHub 已自动关 issue,本 step 仅做解析) BODY="${{ github.event.pull_request.body }}" HEAD_REF="${{ github.event.pull_request.head.ref }}" # 优先从 PR 描述解析(无匹配时 grep 会 exit 1,需 || true 避免 set -e 导致脚本退出) ISSUE_NUM=$(echo "$BODY" | grep -oE '(Closes|Fixes|Resolves) #([0-9]+)' | head -1 | grep -oE '[0-9]+' || true) if [ -z "$ISSUE_NUM" ]; then # 从分支名解析 ai_fix/N_xxx ISSUE_NUM=$(echo "$HEAD_REF" | sed -n 's|^ai_fix/\([0-9]*\)_.*|\1|p') fi if [ -z "$ISSUE_NUM" ]; then echo "ISSUE_NUMBER=" >> $GITHUB_OUTPUT echo "skip=true" >> $GITHUB_OUTPUT echo "未从 PR 描述或分支名解析到 Issue 编号,跳过关闭" exit 0 fi echo "ISSUE_NUMBER=$ISSUE_NUM" >> $GITHUB_OUTPUT echo "skip=false" >> $GITHUB_OUTPUT echo "解析到 Issue #$ISSUE_NUM" - name: Get issue details if: steps.pr.outputs.skip != 'true' id: issue env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | ISSUE_NUM="${{ steps.pr.outputs.ISSUE_NUMBER }}" # 获取 issue 标题与 URL(用于 TG 消息) JSON=$(gh issue view "$ISSUE_NUM" --json title,url 2>/dev/null || echo '{"title":"","url":""}') TITLE=$(echo "$JSON" | jq -r '.title') ISSUE_URL=$(echo "$JSON" | jq -r '.url') echo "title<> $GITHUB_OUTPUT echo "$TITLE" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT echo "url=$ISSUE_URL" >> $GITHUB_OUTPUT - name: Close issue if: steps.pr.outputs.skip != 'true' uses: actions/github-script@v7 with: script: | const issueNumber = parseInt('${{ steps.pr.outputs.ISSUE_NUMBER }}', 10); if (!issueNumber || isNaN(issueNumber)) { console.log('No valid issue number, skip'); return; } const { data: issue } = await github.rest.issues.get({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber }); if (issue.state === 'open') { await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issueNumber, state: 'closed' }); console.log(`Issue #${issueNumber} closed.`); } else { console.log(`Issue #${issueNumber} already closed.`); } - name: Send Telegram notification if: steps.pr.outputs.skip != 'true' env: TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} run: | # 与 docker-build 一致:未配置则跳过 if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$TELEGRAM_CHAT_ID" ]; then echo "⚠️ Telegram Bot Token 或 Chat ID 未配置,跳过通知" exit 0 fi ISSUE_NUM="${{ steps.pr.outputs.ISSUE_NUMBER }}" ISSUE_TITLE="${{ steps.issue.outputs.title }}" ISSUE_URL="${{ steps.issue.outputs.url }}" PR_URL="${{ github.event.pull_request.html_url }}" # 与 docker-build 相同的 HTML 消息格式 MESSAGE="✅ AI 修复的 Issue 已关闭"$'\n'$'\n'"🔢 Issue: #${ISSUE_NUM} ${ISSUE_TITLE}"$'\n'"📎 查看 Issue"$'\n'"🔗 查看 PR" curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ -H "Content-Type: application/json" \ -d "$(jq -n \ --arg chat_id "$TELEGRAM_CHAT_ID" \ --arg text "$MESSAGE" \ '{chat_id: $chat_id, text: $text, parse_mode: "HTML", disable_web_page_preview: false}')" > /tmp/telegram_response.json if [ $? -eq 0 ]; then RESPONSE=$(cat /tmp/telegram_response.json) if echo "$RESPONSE" | grep -q '"ok":true'; then echo "✅ Telegram 通知发送成功" else echo "❌ Telegram 通知发送失败: $RESPONSE" exit 0 fi else echo "❌ 发送 Telegram 消息时发生错误" exit 0 fi