Skip to content

fix: add feixiaohao #18

fix: add feixiaohao

fix: add feixiaohao #18

Workflow file for this run

name: PR Merge Notifications
on:
pull_request_target:
types: [closed] # PR 关闭时触发(下方判断 merged=true 才发送)
push:
branches: [main] # 本地合并或直接提交推到 main
workflow_dispatch: {} # 手动测试
jobs:
notify:
runs-on: ubuntu-latest
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'pull_request' && github.event.pull_request.merged == true) ||
(github.event_name == 'push' && github.ref == 'refs/heads/main')
steps:
- name: Install jq
run: |
sudo apt-get update
sudo apt-get install -y jq
# push 事件需要完整历史用于判别是否为“合并提交”
- name: "Checkout (push only)"
if: github.event_name == 'push'
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: "Detect merge-like push (push only)"
id: detect_merge
if: github.event_name == 'push'
env:
BEFORE: ${{ github.event.before }}
AFTER: ${{ github.event.after }}
run: |
set -e
# 1) 多父提交 => 合并提交
PARENTS_COUNT=$(git rev-list --parents -n 1 "${GITHUB_SHA}" | wc -w)
IS_MERGE=false
if [ "$PARENTS_COUNT" -ge 3 ]; then IS_MERGE=true; fi
# 2) 提交标题以 "Merge" 开头 => 视为合并
SUBJECT=$(git log -1 --pretty=%s)
if printf '%s' "$SUBJECT" | grep -qE '^Merge'; then IS_MERGE=true; fi
# 3) 本次 push 含多提交(FF 合并常见) => 视为合并
RANGE_COUNT=$(git rev-list --count "${BEFORE}..${AFTER}" || echo 1)
if [ "$RANGE_COUNT" -ge 2 ]; then IS_MERGE=true; fi
echo "is_merge=${IS_MERGE}" >> "$GITHUB_OUTPUT"
- name: "Detect PR association (push only)"
id: detect_pr
if: github.event_name == 'push'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN || github.token }}
REPO: ${{ github.repository }}
AFTER: ${{ github.event.after }}
HEAD_MSG: ${{ github.event.head_commit.message }}
run: |
set -e
owner="${REPO%%/*}"
repo="${REPO##*/}"
# 查询该提交是否与 PR 关联(官方接口)
prs_json=$(curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/${owner}/${repo}/commits/${AFTER}/pulls")
count=$(jq 'length' <<< "$prs_json")
HAS_PR=false
if [ "$count" -ge 1 ]; then HAS_PR=true; fi
# 额外:根据常见提交信息模式判断(squash / merge)
MSG="${HEAD_MSG:-}"
if printf '%s' "$MSG" | grep -qE '^Merge pull request #[0-9]+'; then HAS_PR=true; fi
if printf '%s' "$MSG" | grep -qE '\(#([0-9]+)\)$'; then HAS_PR=true; fi
echo "has_pr=${HAS_PR}" >> "$GITHUB_OUTPUT"
- name: "Check secrets"
env:
LARK_WEBHOOK_URL: ${{ secrets.LARK_WEBHOOK_URL }}
LARK_SIGNING_SECRET: ${{ secrets.LARK_SIGNING_SECRET }}
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
run: |
set -e
for v in LARK_WEBHOOK_URL LARK_SIGNING_SECRET TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID; do
if [ -z "${!v}" ]; then
echo "::error title=Missing secret::$v is empty or not visible to this repo"
exit 1
fi
done
- name: "Compose message"
id: compose
env:
EVENT: ${{ github.event_name }}
REPO: ${{ github.repository }}
# PR 事件字段
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
AUTHOR: ${{ github.event.pull_request.user.login }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
BASE_REF: ${{ github.event.pull_request.base.ref }}
# push 事件字段
REF_NAME: ${{ github.ref_name }}
ACTOR: ${{ github.actor }}
BEFORE: ${{ github.event.before }}
AFTER: ${{ github.event.after }}
IS_MERGE: ${{ steps.detect_merge.outputs.is_merge }}
HAS_PR: ${{ steps.detect_pr.outputs.has_pr }}
run: |
set -e
repo_short=$(printf "%s" "$REPO" | awk -F/ '{print $2}')
should_send=false
if [ "$EVENT" = "pull_request" ]; then
# 只在已合并的 PR 场景
SAFE_TITLE=$(printf "%s" "$PR_TITLE" | tr '\n' ' ')
TEXT=$(printf '✅ 已合并\n标题:%s\n仓库:%s\n提交者:%s\n分支:%s → %s\n记录:%s' \
"$SAFE_TITLE" "$repo_short" "$AUTHOR" "$HEAD_REF" "$BASE_REF" "$PR_URL")
should_send=true
elif [ "$EVENT" = "push" ]; then
# 如果与 PR 关联,则认为 PR 事件会/已发送,避免重复
if [ "$HAS_PR" = "true" ]; then
echo "Skip push: commit associated with a PR -> 去重"
TEXT=""
should_send=false
else
COMPARE_URL="https://github.com/${REPO}/compare/${BEFORE}...${AFTER}"
if [ "$IS_MERGE" = "true" ]; then
TEXT=$(printf '✅ 已合并到 %s\n仓库:%s\n提交者:%s\n范围:%s...%s\n记录:%s' \
"$REF_NAME" "$repo_short" "$ACTOR" "$BEFORE" "$AFTER" "$COMPARE_URL")
else
TEXT=$(printf '✅ 已更新 %s\n仓库:%s\n提交者:%s\n范围:%s...%s\n记录:%s' \
"$REF_NAME" "$repo_short" "$ACTOR" "$BEFORE" "$AFTER" "$COMPARE_URL")
fi
should_send=true
fi
else
TEXT="✅ 连通性测试:这是一条手动触发的测试消息。"
should_send=true
fi
echo "text<<EOF" >> "$GITHUB_OUTPUT"
echo "$TEXT" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
echo "should_send=$should_send" >> "$GITHUB_OUTPUT"
- name: "Send to channel A"
if: steps.compose.outputs.should_send == 'true'
env:
LARK_WEBHOOK_URL: ${{ secrets.LARK_WEBHOOK_URL }}
LARK_SIGNING_SECRET: ${{ secrets.LARK_SIGNING_SECRET }}
TEXT: ${{ steps.compose.outputs.text }}
run: |
set -e
ts=$(date +%s)
key=$(printf '%s\n%s' "$ts" "$LARK_SIGNING_SECRET")
sign=$(printf '' | openssl dgst -sha256 -hmac "$key" -binary | base64 | tr -d '\n')
payload=$(jq -n --arg ts "$ts" --arg sign "$sign" --arg text "$TEXT" \
'{timestamp:$ts, sign:$sign, msg_type:"text", content:{text:$text}}')
resp=$(curl -sS -w '\n%{http_code}' -H 'Content-Type: application/json' -d "$payload" "$LARK_WEBHOOK_URL")
http=$(tail -n1 <<< "$resp")
body=$(head -n-1 <<< "$resp")
code=$(jq -r '.code // .StatusCode // .status_code // -1' <<< "$body")
msg=$(jq -r '.msg // .StatusMessage // .msg // empty' <<< "$body")
echo "ChannelA HTTP: $http"
echo "ChannelA Body: $body"
if [ "$http" != "200" ] || [ "$code" != "0" ]; then
echo "::error title=ChannelA push failed::HTTP=$http code=$code msg=$msg"
exit 1
fi
- name: "Send to channel B"
if: steps.compose.outputs.should_send == 'true'
env:
TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }}
TEXT: ${{ steps.compose.outputs.text }}
run: |
set -e
api="https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage"
resp=$(curl -sS -w '\n%{http_code}' -X POST \
--data-urlencode "chat_id=${TELEGRAM_CHAT_ID}" \
--data-urlencode "text=${TEXT}" \
--data-urlencode "disable_web_page_preview=true" \
"$api")
http=$(tail -n1 <<< "$resp")
body=$(head -n-1 <<< "$resp")
ok=$(jq -r '.ok // false' <<< "$body")
echo "ChannelB HTTP: $http"
echo "ChannelB Body: $body"
if [ "$http" != "200" ] || [ "$ok" != "true" ]; then
echo "::error title=ChannelB push failed::HTTP=$http body=$body"
exit 1
fi
- name: "Done"
if: steps.compose.outputs.should_send == 'true'
run: echo "✅ Notifications sent"