fix(ci): 修复ci-cd.yml的YAML语法错误,将Python覆盖率汇总和失败通知抽到独立脚本
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 10s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m35s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 10s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m35s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
This commit is contained in:
@@ -269,91 +269,14 @@ jobs:
|
||||
run: |
|
||||
set +e
|
||||
echo "=== 覆盖率汇总 ==="
|
||||
if [ -f coverage.xml ]; then
|
||||
python3 -c "
|
||||
import xml.etree.ElementTree as ET
|
||||
tree = ET.parse('coverage.xml')
|
||||
root = tree.getroot()
|
||||
line_rate = float(root.get('line-rate', 0)) * 100
|
||||
branch_rate = float(root.get('branch-rate', 0)) * 100
|
||||
lines_covered = int(root.get('lines-covered', 0))
|
||||
lines_valid = int(root.get('lines-valid', 0))
|
||||
print(f'行覆盖率: {line_rate:.2f}% ({lines_covered}/{lines_valid})')
|
||||
print(f'分支覆盖率: {branch_rate:.2f}%')
|
||||
print(f'门槛: 65%')
|
||||
print(f'状态: {"PASS ✅" if line_rate >= 65 else "FAIL ❌"}')
|
||||
"
|
||||
else
|
||||
echo "coverage.xml 不存在,跳过汇总"
|
||||
fi
|
||||
|
||||
python3 scripts/ci_coverage_summary.py
|
||||
- name: Notify CI failure
|
||||
if: failure()
|
||||
shell: sh
|
||||
run: |
|
||||
set +e
|
||||
echo "=== CI 失败通知 ==="
|
||||
|
||||
# 收集失败信息
|
||||
FAILED_JOB="Validate Code Quality And Tests"
|
||||
BRANCH="${GITHUB_REF_NAME:-unknown}"
|
||||
COMMIT="${GITHUB_SHA:0:8}"
|
||||
ACTOR="${GITHUB_ACTOR:-unknown}"
|
||||
RUN_ID="${GITHUB_RUN_ID:-unknown}"
|
||||
REPO="${GITHUB_REPOSITORY:-unknown}"
|
||||
RUN_URL="https://git.xiaoxiajianji.com/${REPO}/actions/runs/${RUN_ID}"
|
||||
|
||||
# 构造通知消息
|
||||
PAYLOAD=$(cat <<EOF
|
||||
{
|
||||
"msg_type": "interactive",
|
||||
"card": {
|
||||
"header": {
|
||||
"title": {
|
||||
"tag": "plain_text",
|
||||
"content": "❌ CI 构建失败"
|
||||
},
|
||||
"status": "red"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"tag": "div",
|
||||
"text": {
|
||||
"tag": "lark_md",
|
||||
"content": "**任务**: ${FAILED_JOB}
|
||||
**分支**: ${BRANCH}
|
||||
**提交**: ${COMMIT}
|
||||
**提交者**: ${ACTOR}
|
||||
**Run ID**: ${RUN_ID}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tag": "action",
|
||||
"actions": [
|
||||
{
|
||||
"tag": "button",
|
||||
"text": {
|
||||
"tag": "plain_text",
|
||||
"content": "查看失败日志"
|
||||
},
|
||||
"url": "${RUN_URL}",
|
||||
"type": "danger"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
# 如果配置了通知 webhook 就发送
|
||||
if [ -n "${CI_NOTIFY_WEBHOOK:-}" ]; then
|
||||
curl -s -X POST -H "Content-Type: application/json" "${CI_NOTIFY_WEBHOOK}" -d "$PAYLOAD" > /dev/null 2>&1 && echo "通知已发送" || echo "通知发送失败"
|
||||
else
|
||||
echo "未配置 CI_NOTIFY_WEBHOOK,跳过通知"
|
||||
echo "如需启用,请在仓库 Settings -> Secrets and variables -> Actions 中添加 CI_NOTIFY_WEBHOOK"
|
||||
fi
|
||||
FAILED_JOB="Validate Code Quality And Tests" python3 scripts/ci_notify_failure.py
|
||||
|
||||
- name: Build summary
|
||||
if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'
|
||||
@@ -364,15 +287,7 @@ print(f'状态: {"PASS ✅" if line_rate >= 65 else "FAIL ❌"}')
|
||||
echo "Branch: ${GITHUB_REF_NAME}"
|
||||
echo "Commit: ${GITHUB_SHA}"
|
||||
# 输出最终覆盖率
|
||||
if [ -f coverage.xml ]; then
|
||||
python3 -c "
|
||||
import xml.etree.ElementTree as ET
|
||||
tree = ET.parse('coverage.xml')
|
||||
root = tree.getroot()
|
||||
line_rate = float(root.get('line-rate', 0)) * 100
|
||||
print(f'Total coverage: {line_rate:.2f}%')
|
||||
"
|
||||
fi
|
||||
python3 scripts/ci_coverage_summary.py
|
||||
|
||||
frontend-lint:
|
||||
name: Frontend Lint
|
||||
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env python3
|
||||
"""解析 coverage.xml 并输出覆盖率汇总。"""
|
||||
import sys
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
THRESHOLD = 65 # 行覆盖率门槛,百分比
|
||||
|
||||
|
||||
def main() -> int:
|
||||
try:
|
||||
tree = ET.parse("coverage.xml")
|
||||
except FileNotFoundError:
|
||||
print("coverage.xml 不存在,跳过汇总")
|
||||
return 0
|
||||
|
||||
root = tree.getroot()
|
||||
line_rate = float(root.get("line-rate", 0)) * 100
|
||||
branch_rate = float(root.get("branch-rate", 0)) * 100
|
||||
lines_covered = int(root.get("lines-covered", 0))
|
||||
lines_valid = int(root.get("lines-valid", 0))
|
||||
|
||||
print(f"行覆盖率: {line_rate:.2f}% ({lines_covered}/{lines_valid})")
|
||||
print(f"分支覆盖率: {branch_rate:.2f}%")
|
||||
print(f"门槛: {THRESHOLD}%")
|
||||
status = "PASS ✅" if line_rate >= THRESHOLD else "FAIL ❌"
|
||||
print(f"状态: {status}")
|
||||
|
||||
return 0 if line_rate >= THRESHOLD else 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
#!/usr/bin/env python3
|
||||
"""发送 CI 失败通知到飞书/项目群 webhook。"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
|
||||
def main() -> int:
|
||||
webhook = os.environ.get("CI_NOTIFY_WEBHOOK", "")
|
||||
if not webhook:
|
||||
print("未配置 CI_NOTIFY_WEBHOOK,跳过通知")
|
||||
print("如需启用,请在仓库 Settings -> Secrets and variables -> Actions 中添加 CI_NOTIFY_WEBHOOK")
|
||||
return 0
|
||||
|
||||
failed_job = os.environ.get("FAILED_JOB", "Unknown Job")
|
||||
branch = os.environ.get("GITHUB_REF_NAME", "unknown")
|
||||
commit = os.environ.get("GITHUB_SHA", "unknown")[:8]
|
||||
actor = os.environ.get("GITHUB_ACTOR", "unknown")
|
||||
run_id = os.environ.get("GITHUB_RUN_ID", "unknown")
|
||||
repo = os.environ.get("GITHUB_REPOSITORY", "unknown")
|
||||
run_url = f"https://git.xiaoxiajianji.com/{repo}/actions/runs/{run_id}"
|
||||
|
||||
payload = {
|
||||
"msg_type": "interactive",
|
||||
"card": {
|
||||
"header": {
|
||||
"title": {
|
||||
"tag": "plain_text",
|
||||
"content": "❌ CI 构建失败",
|
||||
},
|
||||
"status": "red",
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"tag": "div",
|
||||
"text": {
|
||||
"tag": "lark_md",
|
||||
"content": (
|
||||
f"**任务**: {failed_job}\n"
|
||||
f"**分支**: {branch}\n"
|
||||
f"**提交**: {commit}\n"
|
||||
f"**提交者**: {actor}\n"
|
||||
f"**Run ID**: {run_id}"
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
"tag": "action",
|
||||
"actions": [
|
||||
{
|
||||
"tag": "button",
|
||||
"text": {"tag": "plain_text", "content": "查看失败日志"},
|
||||
"url": run_url,
|
||||
"type": "danger",
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
data = json.dumps(payload).encode("utf-8")
|
||||
req = urllib.request.Request(
|
||||
webhook,
|
||||
data=data,
|
||||
headers={"Content-Type": "application/json"},
|
||||
method="POST",
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
resp.read()
|
||||
print("通知已发送")
|
||||
except Exception as e:
|
||||
print(f"通知发送失败: {e}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user