@dingtalk-real-ai/dingtalk-connector 0.7.0 → 0.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ai-fix-and-test.yml +520 -0
- package/.github/workflows/issue-to-dingtalk.yml +60 -0
- package/.github/workflows/pr-review.yml +108 -0
- package/CHANGELOG.md +56 -2
- package/README.md +48 -2
- package/docs/RELEASE_NOTES_V0.7.2.md +143 -0
- package/docs/RELEASE_NOTES_V0.7.3.md +149 -0
- package/{RELEASE_NOTES_v0.7.0.md → docs/RELEASE_NOTES_v0.7.0.md} +5 -14
- package/docs/RELEASE_NOTES_v0.7.1.md +74 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +15 -2
- package/plugin.ts +104 -9
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
name: 🤖 AI 修复分支与测试
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
issues:
|
|
5
|
+
types: [labeled]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
inputs:
|
|
8
|
+
issue_number:
|
|
9
|
+
description: 'Issue 编号'
|
|
10
|
+
required: true
|
|
11
|
+
type: string
|
|
12
|
+
test_mode:
|
|
13
|
+
description: '测试模式'
|
|
14
|
+
required: false
|
|
15
|
+
default: 'local'
|
|
16
|
+
type: choice
|
|
17
|
+
options:
|
|
18
|
+
- local
|
|
19
|
+
- github
|
|
20
|
+
|
|
21
|
+
env:
|
|
22
|
+
DINGTALK_WEBHOOK: https://oapi.dingtalk.com/robot/send?access_token=8710dbbfa8b8b63a02ddfe41ae428b838d2f5e998e0ec446196981d97e7d5350
|
|
23
|
+
DINGTALK_KEYWORD: dingtalk-openclaw-connector
|
|
24
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
# 检查是否需要执行 AI 修复
|
|
28
|
+
check-trigger:
|
|
29
|
+
runs-on: ubuntu-latest
|
|
30
|
+
outputs:
|
|
31
|
+
should_run: ${{ steps.check.outputs.should_run }}
|
|
32
|
+
issue_number: ${{ steps.check.outputs.issue_number }}
|
|
33
|
+
issue_title: ${{ steps.check.outputs.issue_title }}
|
|
34
|
+
issue_body: ${{ steps.check.outputs.issue_body }}
|
|
35
|
+
steps:
|
|
36
|
+
- name: 检查触发条件
|
|
37
|
+
id: check
|
|
38
|
+
uses: actions/github-script@v7
|
|
39
|
+
with:
|
|
40
|
+
script: |
|
|
41
|
+
let issueNumber, issueTitle, issueBody;
|
|
42
|
+
|
|
43
|
+
// 判断触发方式
|
|
44
|
+
if (context.eventName === 'workflow_dispatch') {
|
|
45
|
+
// 手动触发
|
|
46
|
+
issueNumber = context.payload.inputs.issue_number;
|
|
47
|
+
|
|
48
|
+
// 获取 Issue 信息
|
|
49
|
+
const { data: issue } = await github.rest.issues.get({
|
|
50
|
+
owner: context.repo.owner,
|
|
51
|
+
repo: context.repo.repo,
|
|
52
|
+
issue_number: parseInt(issueNumber)
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
issueTitle = issue.title;
|
|
56
|
+
issueBody = issue.body || '';
|
|
57
|
+
|
|
58
|
+
} else if (context.eventName === 'issues') {
|
|
59
|
+
// Issue 标签触发
|
|
60
|
+
if (context.payload.label?.name !== 'ai-fix') {
|
|
61
|
+
console.log('标签不是 ai-fix,跳过');
|
|
62
|
+
core.setOutput('should_run', 'false');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
issueNumber = context.payload.issue.number;
|
|
67
|
+
issueTitle = context.payload.issue.title;
|
|
68
|
+
issueBody = context.payload.issue.body || '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
core.setOutput('should_run', 'true');
|
|
72
|
+
core.setOutput('issue_number', issueNumber);
|
|
73
|
+
core.setOutput('issue_title', issueTitle);
|
|
74
|
+
core.setOutput('issue_body', issueBody);
|
|
75
|
+
|
|
76
|
+
console.log(`✅ 触发 AI 修复流程 - Issue #${issueNumber}`);
|
|
77
|
+
|
|
78
|
+
# 创建修复分支
|
|
79
|
+
create-branch:
|
|
80
|
+
needs: check-trigger
|
|
81
|
+
runs-on: ubuntu-latest
|
|
82
|
+
if: needs.check-trigger.outputs.should_run == 'true'
|
|
83
|
+
outputs:
|
|
84
|
+
branch_name: ${{ steps.branch.outputs.branch_name }}
|
|
85
|
+
steps:
|
|
86
|
+
- name: 检出代码
|
|
87
|
+
uses: actions/checkout@v4
|
|
88
|
+
with:
|
|
89
|
+
fetch-depth: 0
|
|
90
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
91
|
+
|
|
92
|
+
- name: 创建修复分支
|
|
93
|
+
id: branch
|
|
94
|
+
run: |
|
|
95
|
+
ISSUE_NUMBER=${{ needs.check-trigger.outputs.issue_number }}
|
|
96
|
+
ISSUE_TITLE="${{ needs.check-trigger.outputs.issue_title }}"
|
|
97
|
+
|
|
98
|
+
# 生成安全的分支名
|
|
99
|
+
SAFE_TITLE=$(echo "$ISSUE_TITLE" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9' '-' | sed 's/-*$//' | cut -c1-50)
|
|
100
|
+
BRANCH_NAME="ai-fix/issue-${ISSUE_NUMBER}-${SAFE_TITLE}"
|
|
101
|
+
|
|
102
|
+
echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
|
|
103
|
+
echo "🌿 创建分支: $BRANCH_NAME"
|
|
104
|
+
|
|
105
|
+
# 配置 Git
|
|
106
|
+
git config user.name "github-actions[bot]"
|
|
107
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
108
|
+
|
|
109
|
+
# 创建并推送分支
|
|
110
|
+
git checkout -b "$BRANCH_NAME"
|
|
111
|
+
git push origin "$BRANCH_NAME"
|
|
112
|
+
|
|
113
|
+
echo "✅ 分支已创建: $BRANCH_NAME"
|
|
114
|
+
|
|
115
|
+
# 本地 AI 修复(通过 webhook 调用本地 OpenClaw)
|
|
116
|
+
ai-fix-local:
|
|
117
|
+
needs: [check-trigger, create-branch]
|
|
118
|
+
runs-on: ubuntu-latest
|
|
119
|
+
if: github.event.inputs.test_mode != 'github'
|
|
120
|
+
steps:
|
|
121
|
+
- name: 调用本地 AI 修复服务
|
|
122
|
+
uses: actions/github-script@v7
|
|
123
|
+
with:
|
|
124
|
+
script: |
|
|
125
|
+
const issueNumber = '${{ needs.check-trigger.outputs.issue_number }}';
|
|
126
|
+
const issueTitle = '${{ needs.check-trigger.outputs.issue_title }}';
|
|
127
|
+
const issueBody = `${{ needs.check-trigger.outputs.issue_body }}`;
|
|
128
|
+
const branchName = '${{ needs.create-branch.outputs.branch_name }}';
|
|
129
|
+
|
|
130
|
+
// 构建调用本地 OpenClaw 的 payload
|
|
131
|
+
const payload = {
|
|
132
|
+
action: 'ai-fix-issue',
|
|
133
|
+
issue: {
|
|
134
|
+
number: issueNumber,
|
|
135
|
+
title: issueTitle,
|
|
136
|
+
body: issueBody
|
|
137
|
+
},
|
|
138
|
+
branch: branchName,
|
|
139
|
+
repo: {
|
|
140
|
+
owner: context.repo.owner,
|
|
141
|
+
name: context.repo.repo
|
|
142
|
+
},
|
|
143
|
+
callback_url: `https://api.github.com/repos/${context.repo.owner}/${context.repo.repo}/issues/${issueNumber}/comments`
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
console.log('📤 发送 AI 修复请求到本地服务...');
|
|
147
|
+
console.log('Payload:', JSON.stringify(payload, null, 2));
|
|
148
|
+
|
|
149
|
+
// 这里可以通过 webhook 或 API 调用本地服务
|
|
150
|
+
// 实际实现需要根据你的本地 OpenClaw 部署方式调整
|
|
151
|
+
|
|
152
|
+
// 在 Issue 中添加等待评论
|
|
153
|
+
await github.rest.issues.createComment({
|
|
154
|
+
owner: context.repo.owner,
|
|
155
|
+
repo: context.repo.repo,
|
|
156
|
+
issue_number: parseInt(issueNumber),
|
|
157
|
+
body: `⏳ **AI 修复进行中**\n\n分支 \`${branchName}\` 已创建\n\n已发送请求到本地 AI 服务,等待修复结果...\n\n**提示:** 本地 AI 修复完成后,测试结果将自动同步到钉钉群。`
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
# GitHub 环境 AI 修复(使用 GitHub Actions 运行器)
|
|
161
|
+
ai-fix-github:
|
|
162
|
+
needs: [check-trigger, create-branch]
|
|
163
|
+
runs-on: ubuntu-latest
|
|
164
|
+
# 标签触发时自动执行;手动触发时根据 test_mode 选择
|
|
165
|
+
if: |
|
|
166
|
+
github.event_name == 'issues' ||
|
|
167
|
+
github.event.inputs.test_mode == 'github' ||
|
|
168
|
+
(github.event_name == 'workflow_dispatch' && github.event.inputs.test_mode == 'github')
|
|
169
|
+
outputs:
|
|
170
|
+
lint_status: ${{ steps.lint.outputs.lint_status }}
|
|
171
|
+
typecheck_status: ${{ steps.typecheck.outputs.typecheck_status }}
|
|
172
|
+
test_result: ${{ steps.test.outputs.test_result }}
|
|
173
|
+
steps:
|
|
174
|
+
- name: 检出修复分支
|
|
175
|
+
uses: actions/checkout@v4
|
|
176
|
+
with:
|
|
177
|
+
ref: ${{ needs.create-branch.outputs.branch_name }}
|
|
178
|
+
fetch-depth: 0
|
|
179
|
+
|
|
180
|
+
- name: 设置 Node.js
|
|
181
|
+
uses: actions/setup-node@v4
|
|
182
|
+
with:
|
|
183
|
+
node-version: '22'
|
|
184
|
+
cache: 'npm'
|
|
185
|
+
|
|
186
|
+
- name: 安装依赖
|
|
187
|
+
run: npm ci
|
|
188
|
+
|
|
189
|
+
- name: 分析 Issue 并调用 AI 生成修复
|
|
190
|
+
id: ai-analysis
|
|
191
|
+
env:
|
|
192
|
+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
|
193
|
+
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
194
|
+
DASHSCOPE_API_KEY: ${{ secrets.DASHSCOPE_API_KEY }}
|
|
195
|
+
run: |
|
|
196
|
+
echo "🤖 分析 Issue 内容..."
|
|
197
|
+
echo "Issue #${{ needs.check-trigger.outputs.issue_number }}: ${{ needs.check-trigger.outputs.issue_title }}"
|
|
198
|
+
|
|
199
|
+
# 创建修复标记文件(开始分析)
|
|
200
|
+
cat > .ai-fix-info.json << EOF
|
|
201
|
+
{
|
|
202
|
+
"issue_number": ${{ needs.check-trigger.outputs.issue_number }},
|
|
203
|
+
"issue_title": "${{ needs.check-trigger.outputs.issue_title }}",
|
|
204
|
+
"fix_timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
205
|
+
"fix_method": "github-actions-ai",
|
|
206
|
+
"status": "analyzing"
|
|
207
|
+
}
|
|
208
|
+
EOF
|
|
209
|
+
|
|
210
|
+
# 检查是否有 AI API Key
|
|
211
|
+
if [ -n "$OPENAI_API_KEY" ] || [ -n "$ANTHROPIC_API_KEY" ] || [ -n "$DASHSCOPE_API_KEY" ]; then
|
|
212
|
+
echo "✅ 发现 AI API Key,将尝试调用 AI 模型生成修复..."
|
|
213
|
+
|
|
214
|
+
# 准备 Issue 内容
|
|
215
|
+
ISSUE_TITLE="${{ needs.check-trigger.outputs.issue_title }}"
|
|
216
|
+
ISSUE_BODY='${{ needs.check-trigger.outputs.issue_body }}'
|
|
217
|
+
|
|
218
|
+
# 创建 AI 提示词
|
|
219
|
+
cat > /tmp/ai-prompt.txt << 'PROMPT'
|
|
220
|
+
你是一个专业的代码修复助手。请根据下面的 GitHub Issue 描述,分析代码问题并提供修复建议。
|
|
221
|
+
|
|
222
|
+
Issue 标题: $ISSUE_TITLE
|
|
223
|
+
Issue 描述: $ISSUE_BODY
|
|
224
|
+
|
|
225
|
+
请分析:
|
|
226
|
+
1. 问题的根本原因
|
|
227
|
+
2. 需要修改的文件
|
|
228
|
+
3. 具体的修复方案
|
|
229
|
+
|
|
230
|
+
请以结构化的方式输出分析结果。
|
|
231
|
+
PROMPT
|
|
232
|
+
|
|
233
|
+
# 尝试调用 OpenAI API(如果配置了)
|
|
234
|
+
if [ -n "$OPENAI_API_KEY" ]; then
|
|
235
|
+
echo "🔄 调用 OpenAI API..."
|
|
236
|
+
curl -s -X POST https://api.openai.com/v1/chat/completions \
|
|
237
|
+
-H "Authorization: Bearer $OPENAI_API_KEY" \
|
|
238
|
+
-H "Content-Type: application/json" \
|
|
239
|
+
-d '{
|
|
240
|
+
"model": "gpt-4o-mini",
|
|
241
|
+
"messages": [
|
|
242
|
+
{"role": "system", "content": "你是一个专业的代码修复助手。请分析 GitHub Issue 并提供修复建议。"},
|
|
243
|
+
{"role": "user", "content": "Issue 标题: '"$ISSUE_TITLE"'\n\nIssue 描述: '"$ISSUE_BODY"'\n\n请分析问题原因并提供修复方案。"}
|
|
244
|
+
],
|
|
245
|
+
"temperature": 0.3
|
|
246
|
+
}' > /tmp/ai-response.json 2>/dev/null || echo "⚠️ OpenAI API 调用失败"
|
|
247
|
+
|
|
248
|
+
if [ -f /tmp/ai-response.json ] && grep -q '"choices"' /tmp/ai-response.json; then
|
|
249
|
+
echo "✅ OpenAI 分析完成"
|
|
250
|
+
cat /tmp/ai-response.json | jq -r '.choices[0].message.content' > /tmp/ai-analysis.md || true
|
|
251
|
+
fi
|
|
252
|
+
fi
|
|
253
|
+
|
|
254
|
+
# 尝试调用 Anthropic API(如果配置了且 OpenAI 失败)
|
|
255
|
+
if [ -n "$ANTHROPIC_API_KEY" ] && [ ! -f /tmp/ai-analysis.md ]; then
|
|
256
|
+
echo "🔄 调用 Anthropic API..."
|
|
257
|
+
curl -s -X POST https://api.anthropic.com/v1/messages \
|
|
258
|
+
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
|
259
|
+
-H "Content-Type: application/json" \
|
|
260
|
+
-H "anthropic-version: 2023-06-01" \
|
|
261
|
+
-d '{
|
|
262
|
+
"model": "claude-3-haiku-20240307",
|
|
263
|
+
"max_tokens": 2000,
|
|
264
|
+
"messages": [
|
|
265
|
+
{"role": "user", "content": "Issue 标题: '"$ISSUE_TITLE"'\n\nIssue 描述: '"$ISSUE_BODY"'\n\n请分析问题原因并提供修复方案。"}
|
|
266
|
+
]
|
|
267
|
+
}' > /tmp/ai-response.json 2>/dev/null || echo "⚠️ Anthropic API 调用失败"
|
|
268
|
+
|
|
269
|
+
if [ -f /tmp/ai-response.json ] && grep -q '"content"' /tmp/ai-response.json; then
|
|
270
|
+
echo "✅ Anthropic 分析完成"
|
|
271
|
+
cat /tmp/ai-response.json | jq -r '.content[0].text' > /tmp/ai-analysis.md || true
|
|
272
|
+
fi
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# 尝试调用阿里云百炼 Qwen3.5 Plus API(如果配置了且前面的都失败)
|
|
276
|
+
if [ -n "$DASHSCOPE_API_KEY" ] && [ ! -f /tmp/ai-analysis.md ]; then
|
|
277
|
+
echo "🔄 调用阿里云百炼 Qwen3.5 Plus API..."
|
|
278
|
+
curl -s -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
|
|
279
|
+
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
|
|
280
|
+
-H "Content-Type: application/json" \
|
|
281
|
+
-d '{
|
|
282
|
+
"model": "qwen3.5-plus",
|
|
283
|
+
"messages": [
|
|
284
|
+
{"role": "system", "content": "你是一个专业的代码修复助手。请分析 GitHub Issue 并提供修复建议。"},
|
|
285
|
+
{"role": "user", "content": "Issue 标题: '"$ISSUE_TITLE"'\n\nIssue 描述: '"$ISSUE_BODY"'\n\n请分析问题原因并提供修复方案。"}
|
|
286
|
+
],
|
|
287
|
+
"temperature": 0.3
|
|
288
|
+
}' > /tmp/ai-response.json 2>/dev/null || echo "⚠️ Qwen3.5 Plus API 调用失败"
|
|
289
|
+
|
|
290
|
+
if [ -f /tmp/ai-response.json ] && grep -q '"choices"' /tmp/ai-response.json; then
|
|
291
|
+
echo "✅ Qwen3.5 Plus 分析完成"
|
|
292
|
+
cat /tmp/ai-response.json | jq -r '.choices[0].message.content' > /tmp/ai-analysis.md || true
|
|
293
|
+
fi
|
|
294
|
+
fi
|
|
295
|
+
|
|
296
|
+
# 保存 AI 分析结果
|
|
297
|
+
if [ -f /tmp/ai-analysis.md ]; then
|
|
298
|
+
echo "📝 AI 分析结果:"
|
|
299
|
+
cat /tmp/ai-analysis.md
|
|
300
|
+
cp /tmp/ai-analysis.md .ai-fix-analysis.md
|
|
301
|
+
else
|
|
302
|
+
echo "⚠️ AI 分析未完成,将使用默认分析流程"
|
|
303
|
+
echo "AI 分析暂不可用,请手动查看 Issue 并修复。" > .ai-fix-analysis.md
|
|
304
|
+
fi
|
|
305
|
+
else
|
|
306
|
+
echo "⚠️ 未配置 AI API Key (OPENAI_API_KEY、ANTHROPIC_API_KEY 或 DASHSCOPE_API_KEY)"
|
|
307
|
+
echo "请配置 Secrets 后重新运行,或手动修复代码。"
|
|
308
|
+
echo "未配置 AI API Key,请手动修复。" > .ai-fix-analysis.md
|
|
309
|
+
fi
|
|
310
|
+
|
|
311
|
+
echo "analysis_complete=true" >> $GITHUB_OUTPUT
|
|
312
|
+
|
|
313
|
+
- name: 运行代码检查
|
|
314
|
+
id: lint
|
|
315
|
+
continue-on-error: true
|
|
316
|
+
run: |
|
|
317
|
+
echo "🔍 运行代码检查..."
|
|
318
|
+
npm run lint 2>&1 | tee lint-output.txt || true
|
|
319
|
+
echo "lint_status=$?" >> $GITHUB_OUTPUT
|
|
320
|
+
|
|
321
|
+
- name: 运行类型检查
|
|
322
|
+
id: typecheck
|
|
323
|
+
continue-on-error: true
|
|
324
|
+
run: |
|
|
325
|
+
echo "🔍 运行类型检查..."
|
|
326
|
+
npm run type-check 2>&1 | tee typecheck-output.txt || true
|
|
327
|
+
echo "typecheck_status=$?" >> $GITHUB_OUTPUT
|
|
328
|
+
|
|
329
|
+
- name: 运行测试
|
|
330
|
+
id: test
|
|
331
|
+
continue-on-error: true
|
|
332
|
+
run: |
|
|
333
|
+
echo "🧪 运行自动化测试..."
|
|
334
|
+
npm test 2>&1 | tee test-output.txt || true
|
|
335
|
+
echo "test_status=$?" >> $GITHUB_OUTPUT
|
|
336
|
+
|
|
337
|
+
# 提取测试结果摘要
|
|
338
|
+
if grep -q "PASS\|✓" test-output.txt; then
|
|
339
|
+
echo "test_result=passed" >> $GITHUB_OUTPUT
|
|
340
|
+
else
|
|
341
|
+
echo "test_result=failed" >> $GITHUB_OUTPUT
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
- name: 提交修复结果
|
|
345
|
+
run: |
|
|
346
|
+
git config user.name "github-actions[bot]"
|
|
347
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
348
|
+
|
|
349
|
+
# 更新修复状态
|
|
350
|
+
cat > .ai-fix-info.json << EOF
|
|
351
|
+
{
|
|
352
|
+
"issue_number": ${{ needs.check-trigger.outputs.issue_number }},
|
|
353
|
+
"issue_title": "${{ needs.check-trigger.outputs.issue_title }}",
|
|
354
|
+
"fix_timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
355
|
+
"fix_method": "github-actions-ai",
|
|
356
|
+
"status": "completed",
|
|
357
|
+
"test_results": {
|
|
358
|
+
"lint": "${{ steps.lint.outputs.lint_status == '0' && 'passed' || 'failed' }}",
|
|
359
|
+
"typecheck": "${{ steps.typecheck.outputs.typecheck_status == '0' && 'passed' || 'failed' }}",
|
|
360
|
+
"test": "${{ steps.test.outputs.test_result }}"
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
EOF
|
|
364
|
+
|
|
365
|
+
# 添加 AI 分析结果(如果存在)
|
|
366
|
+
if [ -f .ai-fix-analysis.md ]; then
|
|
367
|
+
git add .ai-fix-analysis.md
|
|
368
|
+
fi
|
|
369
|
+
|
|
370
|
+
git add .ai-fix-info.json
|
|
371
|
+
git commit -m "🤖 AI 修复: Issue #${{ needs.check-trigger.outputs.issue_number }} - 自动修复结果" || echo "无变更需要提交"
|
|
372
|
+
git push origin ${{ needs.create-branch.outputs.branch_name }}
|
|
373
|
+
|
|
374
|
+
# 创建 PR
|
|
375
|
+
create-pr:
|
|
376
|
+
needs: [check-trigger, create-branch, ai-fix-github]
|
|
377
|
+
runs-on: ubuntu-latest
|
|
378
|
+
if: always() && (needs.ai-fix-github.result == 'success' || needs.ai-fix-github.result == 'skipped')
|
|
379
|
+
steps:
|
|
380
|
+
- name: 创建 Pull Request
|
|
381
|
+
uses: actions/github-script@v7
|
|
382
|
+
with:
|
|
383
|
+
script: |
|
|
384
|
+
const issueNumber = parseInt('${{ needs.check-trigger.outputs.issue_number }}');
|
|
385
|
+
const branchName = '${{ needs.create-branch.outputs.branch_name }}';
|
|
386
|
+
|
|
387
|
+
try {
|
|
388
|
+
const { data: pr } = await github.rest.pulls.create({
|
|
389
|
+
owner: context.repo.owner,
|
|
390
|
+
repo: context.repo.repo,
|
|
391
|
+
title: `🤖 AI 修复: ${context.payload.issue?.title || `Issue #${issueNumber}`}`,
|
|
392
|
+
head: branchName,
|
|
393
|
+
base: 'main',
|
|
394
|
+
body: `## 🤖 AI 自动修复\n\n**关联 Issue:** #${issueNumber}\n\n**修复分支:** \`${branchName}\`\n\n**修复说明:**\n- 由 GitHub Actions 自动生成的修复\n- 基于 Issue 描述进行代码分析和修复\n- 包含自动化测试结果\n\n**测试状态:**\n- 代码检查: ${{ needs.ai-fix-github.outputs.lint_status == '0' && '✅ 通过' || '⚠️ 需关注' }}\n- 类型检查: ${{ needs.ai-fix-github.outputs.typecheck_status == '0' && '✅ 通过' || '⚠️ 需关注' }}\n- 单元测试: ${{ needs.ai-fix-github.outputs.test_result == 'passed' && '✅ 通过' || '⚠️ 需关注' }}\n\n**需要人工审核:**\n- [ ] 代码逻辑正确性\n- [ ] 边界情况处理\n- [ ] 性能影响评估\n\n---\n*此 PR 由 AI 修复工作流自动生成*`,
|
|
395
|
+
draft: false
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
console.log(`✅ PR 创建成功: #${pr.number}`);
|
|
399
|
+
core.setOutput('pr_number', pr.number);
|
|
400
|
+
core.setOutput('pr_url', pr.html_url);
|
|
401
|
+
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.error('创建 PR 失败:', error.message);
|
|
404
|
+
// PR 可能已存在,查找现有 PR
|
|
405
|
+
const { data: prs } = await github.rest.pulls.list({
|
|
406
|
+
owner: context.repo.owner,
|
|
407
|
+
repo: context.repo.repo,
|
|
408
|
+
head: `${context.repo.owner}:${branchName}`,
|
|
409
|
+
base: 'main',
|
|
410
|
+
state: 'open'
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
if (prs.length > 0) {
|
|
414
|
+
console.log(`ℹ️ PR 已存在: #${prs[0].number}`);
|
|
415
|
+
core.setOutput('pr_number', prs[0].number);
|
|
416
|
+
core.setOutput('pr_url', prs[0].html_url);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
# 发送测试结果到钉钉
|
|
421
|
+
notify-result:
|
|
422
|
+
needs: [check-trigger, create-branch, ai-fix-github, create-pr]
|
|
423
|
+
runs-on: ubuntu-latest
|
|
424
|
+
if: always()
|
|
425
|
+
outputs:
|
|
426
|
+
notified: ${{ steps.notify.outputs.notified }}
|
|
427
|
+
steps:
|
|
428
|
+
- name: 发送测试结果到钉钉
|
|
429
|
+
id: notify
|
|
430
|
+
uses: actions/github-script@v7
|
|
431
|
+
with:
|
|
432
|
+
script: |
|
|
433
|
+
const issueNumber = '${{ needs.check-trigger.outputs.issue_number }}';
|
|
434
|
+
const issueTitle = '${{ needs.check-trigger.outputs.issue_title }}';
|
|
435
|
+
const branchName = '${{ needs.create-branch.outputs.branch_name }}';
|
|
436
|
+
|
|
437
|
+
// 确定整体状态(处理 ai-fix-github 被跳过的情况)
|
|
438
|
+
const lintStatus = '${{ needs.ai-fix-github.outputs.lint_status }}';
|
|
439
|
+
const typecheckStatus = '${{ needs.ai-fix-github.outputs.typecheck_status }}';
|
|
440
|
+
const testResult = '${{ needs.ai-fix-github.outputs.test_result }}';
|
|
441
|
+
|
|
442
|
+
// 判断执行模式
|
|
443
|
+
const isLocalMode = '${{ github.event.inputs.test_mode }}' === 'local' && '${{ github.event_name }}' === 'workflow_dispatch';
|
|
444
|
+
const isLabelTrigger = '${{ github.event_name }}' === 'issues';
|
|
445
|
+
|
|
446
|
+
let allPassed, statusEmoji, statusText, testResultsText;
|
|
447
|
+
|
|
448
|
+
if (isLocalMode && lintStatus === '') {
|
|
449
|
+
// Local 模式:GitHub Actions 未执行测试
|
|
450
|
+
statusEmoji = '⏳';
|
|
451
|
+
statusText = '等待本地 AI 修复';
|
|
452
|
+
testResultsText = '- 📝 代码检查: ⏳ 等待本地执行\n- 🔍 类型检查: ⏳ 等待本地执行\n- 🧪 单元测试: ⏳ 等待本地执行';
|
|
453
|
+
allPassed = false;
|
|
454
|
+
} else {
|
|
455
|
+
// GitHub 模式(标签触发或手动选择 github 模式):显示实际测试结果
|
|
456
|
+
allPassed = lintStatus === '0' && typecheckStatus === '0' && testResult === 'passed';
|
|
457
|
+
statusEmoji = allPassed ? '✅' : '⚠️';
|
|
458
|
+
statusText = allPassed ? '测试通过' : '需要关注';
|
|
459
|
+
testResultsText = `- 📝 代码检查: ${lintStatus === '0' ? '✅ 通过' : '❌ 失败'}\n- 🔍 类型检查: ${typecheckStatus === '0' ? '✅ 通过' : '❌ 失败'}\n- 🧪 单元测试: ${testResult === 'passed' ? '✅ 通过' : '❌ 失败'}`;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// 构建钉钉消息
|
|
463
|
+
const keyword = process.env.DINGTALK_KEYWORD;
|
|
464
|
+
const message = {
|
|
465
|
+
msgtype: 'markdown',
|
|
466
|
+
markdown: {
|
|
467
|
+
title: `${keyword} - ${statusEmoji} AI 修复测试完成`,
|
|
468
|
+
text: `## ${statusEmoji} AI 修复测试${statusText}\n\n${keyword}\n\n**Issue:** [#${issueNumber} ${issueTitle}](https://github.com/${context.repo.owner}/${context.repo.repo}/issues/${issueNumber})\n\n**修复分支:** \`${branchName}\`\n\n**测试结果:**\n${testResultsText}\n\n**下一步:**\n${allPassed ? '✅ 所有测试通过,请审核 PR 后合并' : (lintStatus === '' ? '⏳ 本地 AI 修复完成后,请手动运行测试' : '⚠️ 部分测试未通过,请查看详细日志')}\n\n---\n⏰ ${new Date().toLocaleString('zh-CN')}`
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
// 发送钉钉通知
|
|
473
|
+
const axios = require('axios');
|
|
474
|
+
try {
|
|
475
|
+
await axios.post(process.env.DINGTALK_WEBHOOK, message);
|
|
476
|
+
console.log('✅ 钉钉通知发送成功');
|
|
477
|
+
core.setOutput('notified', 'true');
|
|
478
|
+
} catch (error) {
|
|
479
|
+
console.error('❌ 钉钉通知发送失败:', error.message);
|
|
480
|
+
core.setOutput('notified', 'false');
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
# 更新 Issue 状态
|
|
484
|
+
update-issue:
|
|
485
|
+
needs: [check-trigger, create-branch, ai-fix-github, create-pr, notify-result]
|
|
486
|
+
runs-on: ubuntu-latest
|
|
487
|
+
if: always()
|
|
488
|
+
steps:
|
|
489
|
+
- name: 更新 Issue 评论
|
|
490
|
+
uses: actions/github-script@v7
|
|
491
|
+
with:
|
|
492
|
+
script: |
|
|
493
|
+
const issueNumber = parseInt('${{ needs.check-trigger.outputs.issue_number }}');
|
|
494
|
+
const branchName = '${{ needs.create-branch.outputs.branch_name }}';
|
|
495
|
+
|
|
496
|
+
const lintStatus = '${{ needs.ai-fix-github.outputs.lint_status }}';
|
|
497
|
+
const typecheckStatus = '${{ needs.ai-fix-github.outputs.typecheck_status }}';
|
|
498
|
+
const testResult = '${{ needs.ai-fix-github.outputs.test_result }}';
|
|
499
|
+
|
|
500
|
+
// 判断执行模式
|
|
501
|
+
const isLocalMode = '${{ github.event.inputs.test_mode }}' === 'local' && '${{ github.event_name }}' === 'workflow_dispatch';
|
|
502
|
+
const isLabelTrigger = '${{ github.event_name }}' === 'issues';
|
|
503
|
+
|
|
504
|
+
let comment = '';
|
|
505
|
+
|
|
506
|
+
if (isLocalMode && lintStatus === '') {
|
|
507
|
+
// Local 模式:GitHub Actions 未执行测试
|
|
508
|
+
comment = `⏳ **AI 修复分支已创建**\n\n**修复分支:** \`${branchName}\`\n\n已触发本地 AI 修复流程。\n\n**注意:** 本地 AI 修复完成后,请手动推送代码并创建 PR。\n\n钉钉群已收到通知,可在群内查看进度。`;
|
|
509
|
+
} else if (lintStatus === '0' && typecheckStatus === '0' && testResult === 'passed') {
|
|
510
|
+
comment = `✅ **AI 修复完成**\n\n所有测试已通过!\n\n**修复分支:** \`${branchName}\`\n\n**测试结果:**\n- 📝 代码检查: ✅ 通过\n- 🔍 类型检查: ✅ 通过\n- 🧪 单元测试: ✅ 通过\n\n已创建 PR,请审核后合并。`;
|
|
511
|
+
} else {
|
|
512
|
+
comment = `⚠️ **AI 修复完成(需关注)**\n\n**修复分支:** \`${branchName}\`\n\n**测试结果:**\n- 📝 代码检查: ${lintStatus === '0' ? '✅ 通过' : '❌ 失败'}\n- 🔍 类型检查: ${typecheckStatus === '0' ? '✅ 通过' : '❌ 失败'}\n- 🧪 单元测试: ${testResult === 'passed' ? '✅ 通过' : '❌ 失败'}\n\n部分测试未通过,请查看详细日志并手动调整。`;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
await github.rest.issues.createComment({
|
|
516
|
+
owner: context.repo.owner,
|
|
517
|
+
repo: context.repo.repo,
|
|
518
|
+
issue_number: issueNumber,
|
|
519
|
+
body: comment
|
|
520
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Issue 自动同步到钉钉群
|
|
2
|
+
# 当有新 Issue 或 Issue 评论时,自动发送到钉钉群
|
|
3
|
+
name: 🔔 Issue Notification to DingTalk
|
|
4
|
+
|
|
5
|
+
on:
|
|
6
|
+
issues:
|
|
7
|
+
types: [opened, reopened, closed, labeled]
|
|
8
|
+
issue_comment:
|
|
9
|
+
types: [created]
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
notify:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- name: 📬 Send Issue to DingTalk
|
|
16
|
+
uses: actions/github-script@v7
|
|
17
|
+
with:
|
|
18
|
+
script: |
|
|
19
|
+
const webhook = process.env.DINGTALK_WEBHOOK;
|
|
20
|
+
if (!webhook) {
|
|
21
|
+
console.log('⚠️ DINGTALK_WEBHOOK not set, skipping notification');
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const event = context.eventName;
|
|
26
|
+
const payload = context.payload;
|
|
27
|
+
let title, content, url, action;
|
|
28
|
+
|
|
29
|
+
if (event === 'issues') {
|
|
30
|
+
const issue = payload.issue;
|
|
31
|
+
action = payload.action;
|
|
32
|
+
title = `[${action.toUpperCase()}] Issue #${issue.number}: ${issue.title}`;
|
|
33
|
+
content = issue.body?.substring(0, 500) || 'No description';
|
|
34
|
+
url = issue.html_url;
|
|
35
|
+
} else if (event === 'issue_comment') {
|
|
36
|
+
const comment = payload.comment;
|
|
37
|
+
const issue = payload.issue;
|
|
38
|
+
action = 'commented';
|
|
39
|
+
title = `[COMMENT] Issue #${issue.number}: ${issue.title}`;
|
|
40
|
+
content = comment.body?.substring(0, 500) || 'No content';
|
|
41
|
+
url = comment.html_url;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const message = {
|
|
45
|
+
msgtype: 'markdown',
|
|
46
|
+
markdown: {
|
|
47
|
+
title: 'GitHub Issue 通知',
|
|
48
|
+
text: `## 🔔 GitHub Issue 通知\n\n**${title}**\n\n${content}${content.length >= 500 ? '...' : ''}\n\n[点击查看详情](${url})\n\n---\n📦 ${context.repo.owner}/${context.repo.repo}`
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
await fetch(webhook, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'Content-Type': 'application/json' },
|
|
55
|
+
body: JSON.stringify(message)
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log('✅ DingTalk notification sent');
|
|
59
|
+
env:
|
|
60
|
+
DINGTALK_WEBHOOK: ${{ secrets.DINGTALK_WEBHOOK }}
|