@newstarzj/cann-review 3.2.0
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/INSTALL.md +168 -0
- package/LICENSE +21 -0
- package/README.md +186 -0
- package/SKILL.md +663 -0
- package/auto-review-final.sh +170 -0
- package/auto-review-post-comment.sh +197 -0
- package/auto-review-single-simple.sh +123 -0
- package/auto-review-single-v2.sh +183 -0
- package/auto-review-single.sh +172 -0
- package/auto-review.sh +170 -0
- package/check-mentions.py +145 -0
- package/check-mentions.sh +128 -0
- package/config/cron.example.json +9 -0
- package/config/gitcode.conf +10 -0
- package/config/gitcode.conf.example +10 -0
- package/config/repos.conf +13 -0
- package/config/repos.conf.example +15 -0
- package/cron.yaml +89 -0
- package/examples/auto_review_summary.md +210 -0
- package/examples/example_report.md +88 -0
- package/gitcode-api.sh +205 -0
- package/index.js +13 -0
- package/package.json +52 -0
- package/post-install.sh +41 -0
- package/prompt.md +212 -0
- package/simple-review.sh +56 -0
- package/skill.yaml +172 -0
- package/test-api.sh +86 -0
- package/test-auto-review.sh +26 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 自动审查脚本 - 扫描所有仓库,逐个审查
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
CONFIG_FILE="$SCRIPT_DIR/config/repos.conf"
|
|
6
|
+
STATE_FILE="$SCRIPT_DIR/.review-state.json"
|
|
7
|
+
PENDING_FILE="$SCRIPT_DIR/.pending-reviews.json"
|
|
8
|
+
|
|
9
|
+
# 加载 Token
|
|
10
|
+
if [ -f "$SCRIPT_DIR/config/gitcode.conf" ]; then
|
|
11
|
+
source "$SCRIPT_DIR/config/gitcode.conf"
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if [ -z "$GITCODE_API_TOKEN" ]; then
|
|
15
|
+
echo "❌ 错误: 未配置 GitCode API Token"
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# 读取审查状态
|
|
20
|
+
load_state() {
|
|
21
|
+
if [ -f "$STATE_FILE" ]; then
|
|
22
|
+
cat "$STATE_FILE"
|
|
23
|
+
else
|
|
24
|
+
echo '{"reviewed": []}'
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# 检查 PR 是否已审查
|
|
29
|
+
is_reviewed() {
|
|
30
|
+
local repo=$1
|
|
31
|
+
local pr_number=$2
|
|
32
|
+
local state=$(load_state)
|
|
33
|
+
|
|
34
|
+
if echo "$state" | grep -q "\"$repo#$pr_number\""; then
|
|
35
|
+
return 0
|
|
36
|
+
else
|
|
37
|
+
return 1
|
|
38
|
+
fi
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# 主函数
|
|
42
|
+
main() {
|
|
43
|
+
echo "🤖 CANN 自动审查(批量扫描模式)"
|
|
44
|
+
echo "======================================"
|
|
45
|
+
echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
46
|
+
echo ""
|
|
47
|
+
|
|
48
|
+
# 统计仓库数量
|
|
49
|
+
REPO_COUNT=$(grep -v '^#' "$CONFIG_FILE" | grep -v '^$' | wc -l | tr -d ' ')
|
|
50
|
+
echo "📊 配置信息:"
|
|
51
|
+
echo " 仓库数量: $REPO_COUNT"
|
|
52
|
+
echo ""
|
|
53
|
+
|
|
54
|
+
# 初始化待审查列表
|
|
55
|
+
python3 << EOF
|
|
56
|
+
import json
|
|
57
|
+
with open('$PENDING_FILE', 'w') as f:
|
|
58
|
+
json.dump({"pending": [], "scan_time": "$(date -Iseconds)"}, f, indent=2)
|
|
59
|
+
EOF
|
|
60
|
+
|
|
61
|
+
# 遍历仓库,收集所有待审查的 PR
|
|
62
|
+
echo "🔍 扫描所有仓库..."
|
|
63
|
+
local total_pending=0
|
|
64
|
+
|
|
65
|
+
while IFS= read -r repo; do
|
|
66
|
+
# 跳过注释和空行
|
|
67
|
+
[[ "$repo" =~ ^#.*$ ]] && continue
|
|
68
|
+
[[ -z "$repo" ]] && continue
|
|
69
|
+
|
|
70
|
+
local owner=$(echo "$repo" | cut -d'/' -f1)
|
|
71
|
+
local repo_name=$(echo "$repo" | cut -d'/' -f2)
|
|
72
|
+
|
|
73
|
+
echo " 检查: $repo"
|
|
74
|
+
|
|
75
|
+
# 获取开放的 PR(获取前 20 个)
|
|
76
|
+
local pr_list=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
|
|
77
|
+
"https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls?state=opened&per_page=20")
|
|
78
|
+
|
|
79
|
+
# 提取 PR 编号
|
|
80
|
+
local pr_numbers=$(echo "$pr_list" | grep -o '"number":[0-9]*' | grep -o '[0-9]*')
|
|
81
|
+
|
|
82
|
+
local repo_pending=0
|
|
83
|
+
for pr_number in $pr_numbers; do
|
|
84
|
+
if ! is_reviewed "$repo" "$pr_number"; then
|
|
85
|
+
# 获取 PR 详情
|
|
86
|
+
local pr_info=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
|
|
87
|
+
"https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls/$pr_number")
|
|
88
|
+
|
|
89
|
+
# 提取标题
|
|
90
|
+
local title=$(echo "$pr_info" | grep -o '"title":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
91
|
+
# 提取作者
|
|
92
|
+
local author=$(echo "$pr_info" | grep -o '"user":{[^}]*"login":"[^"]*"' | grep -o '"login":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
93
|
+
# 提取链接
|
|
94
|
+
local html_url="https://gitcode.com/$repo/merge_requests/$pr_number"
|
|
95
|
+
|
|
96
|
+
# 添加到待审查列表
|
|
97
|
+
python3 << EOF
|
|
98
|
+
import json
|
|
99
|
+
|
|
100
|
+
# 读取现有列表
|
|
101
|
+
try:
|
|
102
|
+
with open('$PENDING_FILE', 'r') as f:
|
|
103
|
+
data = json.load(f)
|
|
104
|
+
except:
|
|
105
|
+
data = {"pending": [], "scan_time": "$(date -Iseconds)"}
|
|
106
|
+
|
|
107
|
+
# 添加新的待审查 PR
|
|
108
|
+
data["pending"].append({
|
|
109
|
+
"repo": "$repo",
|
|
110
|
+
"pr_number": $pr_number,
|
|
111
|
+
"title": """$title""",
|
|
112
|
+
"author": """$author""",
|
|
113
|
+
"url": "$html_url"
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
# 保存
|
|
117
|
+
with open('$PENDING_FILE', 'w') as f:
|
|
118
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
119
|
+
EOF
|
|
120
|
+
|
|
121
|
+
((repo_pending++))
|
|
122
|
+
((total_pending++))
|
|
123
|
+
fi
|
|
124
|
+
done
|
|
125
|
+
|
|
126
|
+
if [ $repo_pending -gt 0 ]; then
|
|
127
|
+
echo " ✅ 发现 $repo_pending 个待审查的 PR"
|
|
128
|
+
else
|
|
129
|
+
echo " ℹ️ 无待审查的 PR"
|
|
130
|
+
fi
|
|
131
|
+
done < "$CONFIG_FILE"
|
|
132
|
+
|
|
133
|
+
echo ""
|
|
134
|
+
echo "======================================"
|
|
135
|
+
echo "扫描完成"
|
|
136
|
+
echo " 待审查 PR 总数: $total_pending"
|
|
137
|
+
echo ""
|
|
138
|
+
|
|
139
|
+
if [ $total_pending -gt 0 ]; then
|
|
140
|
+
echo "📋 待审查 PR 列表:"
|
|
141
|
+
python3 << 'EOF'
|
|
142
|
+
import json
|
|
143
|
+
with open('/Users/zj/.openclaw/workspace/skills/cann-review/.pending-reviews.json', 'r') as f:
|
|
144
|
+
data = json.load(f)
|
|
145
|
+
for i, pr in enumerate(data["pending"], 1):
|
|
146
|
+
print(f" {i}. {pr['repo']}#{pr['pr_number']} - {pr['title']}")
|
|
147
|
+
print(f" 作者: {pr['author']}")
|
|
148
|
+
print(f" 链接: {pr['url']}")
|
|
149
|
+
print()
|
|
150
|
+
EOF
|
|
151
|
+
|
|
152
|
+
echo "💡 接下来将逐个审查这些 PR..."
|
|
153
|
+
echo ""
|
|
154
|
+
|
|
155
|
+
# 输出 JSON 标记,方便 Agent 解析
|
|
156
|
+
echo "PENDING_REVIEWS_JSON_START"
|
|
157
|
+
cat "$PENDING_FILE"
|
|
158
|
+
echo ""
|
|
159
|
+
echo "PENDING_REVIEWS_JSON_END"
|
|
160
|
+
else
|
|
161
|
+
echo "✅ 所有 PR 都已审查完毕"
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
echo ""
|
|
165
|
+
echo "======================================"
|
|
166
|
+
echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
# 运行主函数
|
|
170
|
+
main
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 真正的单次自动审查 - 会实际发布评论到 PR
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
CONFIG_FILE="$SCRIPT_DIR/config/repos.conf"
|
|
6
|
+
STATE_FILE="$SCRIPT_DIR/.review-state.json"
|
|
7
|
+
|
|
8
|
+
# 加载 Token
|
|
9
|
+
if [ -f "$SCRIPT_DIR/config/gitcode.conf" ]; then
|
|
10
|
+
source "$SCRIPT_DIR/config/gitcode.conf"
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [ -z "$GITCODE_API_TOKEN" ]; then
|
|
14
|
+
echo "❌ 错误: 未配置 GitCode API Token"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# 读取审查状态
|
|
19
|
+
load_state() {
|
|
20
|
+
if [ -f "$STATE_FILE" ]; then
|
|
21
|
+
cat "$STATE_FILE"
|
|
22
|
+
else
|
|
23
|
+
echo '{"reviewed": []}'
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# 检查 PR 是否已审查
|
|
28
|
+
is_reviewed() {
|
|
29
|
+
local repo=$1
|
|
30
|
+
local pr_number=$2
|
|
31
|
+
local state=$(load_state)
|
|
32
|
+
|
|
33
|
+
if echo "$state" | grep -q "\"$repo#$pr_number\""; then
|
|
34
|
+
return 0
|
|
35
|
+
else
|
|
36
|
+
return 1
|
|
37
|
+
fi
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# 标记为已审查
|
|
41
|
+
mark_reviewed() {
|
|
42
|
+
local repo=$1
|
|
43
|
+
local pr_number=$2
|
|
44
|
+
|
|
45
|
+
python3 << EOF
|
|
46
|
+
import json
|
|
47
|
+
from datetime import datetime
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
state = json.loads('''$(load_state)''')
|
|
51
|
+
except:
|
|
52
|
+
state = {"reviewed": []}
|
|
53
|
+
|
|
54
|
+
key = "$repo#$pr_number"
|
|
55
|
+
if "reviewed" not in state:
|
|
56
|
+
state["reviewed"] = []
|
|
57
|
+
if key not in state["reviewed"]:
|
|
58
|
+
state["reviewed"].append(key)
|
|
59
|
+
state["last_review"] = {
|
|
60
|
+
"repo": "$repo",
|
|
61
|
+
"pr": $pr_number,
|
|
62
|
+
"time": datetime.now().isoformat()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
with open('$STATE_FILE', 'w') as f:
|
|
66
|
+
json.dump(state, f, indent=2)
|
|
67
|
+
EOF
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# 发布简单的审查评论
|
|
71
|
+
post_review_comment() {
|
|
72
|
+
local owner=$1
|
|
73
|
+
local repo_name=$2
|
|
74
|
+
local pr_number=$3
|
|
75
|
+
local title=$4
|
|
76
|
+
|
|
77
|
+
# 生成简单的审查报告
|
|
78
|
+
local comment="## 🤖 CANN 自动审查通知
|
|
79
|
+
|
|
80
|
+
**PR**: #$pr_number
|
|
81
|
+
**标题**: $title
|
|
82
|
+
**时间**: $(date '+%Y-%m-%d %H:%M:%S')
|
|
83
|
+
|
|
84
|
+
### ✅ 审查状态
|
|
85
|
+
|
|
86
|
+
本 PR 已被自动审查系统标记。
|
|
87
|
+
|
|
88
|
+
**注意**: 这是自动审查的初步标记。完整的代码审查报告将由人工审查或后续的详细审查流程生成。
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
*此评论由 CANN 自动审查系统自动生成*"
|
|
92
|
+
|
|
93
|
+
# 转义并发布评论
|
|
94
|
+
local escaped_comment=$(echo "$comment" | python3 -c "import sys, json; print(json.dumps(sys.stdin.read()))")
|
|
95
|
+
|
|
96
|
+
local response=$(curl -s -X POST \
|
|
97
|
+
-H "Authorization: Bearer $GITCODE_API_TOKEN" \
|
|
98
|
+
-H "Content-Type: application/json" \
|
|
99
|
+
-d "{\"body\":$escaped_comment}" \
|
|
100
|
+
"https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls/$pr_number/comments")
|
|
101
|
+
|
|
102
|
+
# 检查是否成功
|
|
103
|
+
if echo "$response" | grep -q '"id"'; then
|
|
104
|
+
local comment_id=$(echo "$response" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
105
|
+
echo "✅ 评论发布成功 (ID: $comment_id)"
|
|
106
|
+
return 0
|
|
107
|
+
else
|
|
108
|
+
echo "❌ 评论发布失败"
|
|
109
|
+
echo "响应: $response"
|
|
110
|
+
return 1
|
|
111
|
+
fi
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
# 主函数
|
|
115
|
+
main() {
|
|
116
|
+
echo "🤖 CANN 真正的单次自动审查"
|
|
117
|
+
echo "=========================="
|
|
118
|
+
echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
119
|
+
echo ""
|
|
120
|
+
|
|
121
|
+
# 统计仓库数量
|
|
122
|
+
REPO_COUNT=$(grep -v '^#' "$CONFIG_FILE" | grep -v '^$' | wc -l | tr -d ' ')
|
|
123
|
+
echo "📊 配置信息:"
|
|
124
|
+
echo " 仓库数量: $REPO_COUNT"
|
|
125
|
+
echo ""
|
|
126
|
+
|
|
127
|
+
# 遍历仓库
|
|
128
|
+
echo "🔍 扫描仓库..."
|
|
129
|
+
local found=false
|
|
130
|
+
|
|
131
|
+
while IFS= read -r repo; do
|
|
132
|
+
# 跳过注释和空行
|
|
133
|
+
[[ "$repo" =~ ^#.*$ ]] && continue
|
|
134
|
+
[[ -z "$repo" ]] && continue
|
|
135
|
+
|
|
136
|
+
local owner=$(echo "$repo" | cut -d'/' -f1)
|
|
137
|
+
local repo_name=$(echo "$repo" | cut -d'/' -f2)
|
|
138
|
+
|
|
139
|
+
echo " 检查: $repo"
|
|
140
|
+
|
|
141
|
+
# 获取开放的 PR(只获取前 5 个)
|
|
142
|
+
local pr_list=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
|
|
143
|
+
"https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls?state=opened&per_page=5")
|
|
144
|
+
|
|
145
|
+
# 提取 PR 编号
|
|
146
|
+
local pr_numbers=$(echo "$pr_list" | grep -o '"number":[0-9]*' | grep -o '[0-9]*')
|
|
147
|
+
|
|
148
|
+
for pr_number in $pr_numbers; do
|
|
149
|
+
if ! is_reviewed "$repo" "$pr_number"; then
|
|
150
|
+
# 找到未审查的 PR
|
|
151
|
+
found=true
|
|
152
|
+
|
|
153
|
+
echo ""
|
|
154
|
+
echo "✅ 找到需要审查的 PR:"
|
|
155
|
+
echo " 仓库: $repo"
|
|
156
|
+
echo " PR: #$pr_number"
|
|
157
|
+
echo ""
|
|
158
|
+
|
|
159
|
+
# 获取 PR 详情
|
|
160
|
+
local pr_info=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
|
|
161
|
+
"https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls/$pr_number")
|
|
162
|
+
|
|
163
|
+
# 提取标题
|
|
164
|
+
local title=$(echo "$pr_info" | grep -o '"title":"[^"]*"' | head -1 | cut -d'"' -f4)
|
|
165
|
+
|
|
166
|
+
echo " 标题: $title"
|
|
167
|
+
echo ""
|
|
168
|
+
echo "📝 发布审查评论..."
|
|
169
|
+
|
|
170
|
+
# 发布评论
|
|
171
|
+
if post_review_comment "$owner" "$repo_name" "$pr_number" "$title"; then
|
|
172
|
+
# 标记为已审查
|
|
173
|
+
mark_reviewed "$repo" "$pr_number"
|
|
174
|
+
echo "✅ 已标记为已审查"
|
|
175
|
+
else
|
|
176
|
+
echo "⚠️ 评论发布失败,但仍标记为已审查(避免重复)"
|
|
177
|
+
mark_reviewed "$repo" "$pr_number"
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# 找到第一个就退出
|
|
181
|
+
break 2
|
|
182
|
+
fi
|
|
183
|
+
done
|
|
184
|
+
done < "$CONFIG_FILE"
|
|
185
|
+
|
|
186
|
+
if [ "$found" = false ]; then
|
|
187
|
+
echo ""
|
|
188
|
+
echo "✅ 所有 PR 都已审查完毕"
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
echo ""
|
|
192
|
+
echo "=========================="
|
|
193
|
+
echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# 运行主函数
|
|
197
|
+
main
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 完整的单次审查流程 - 找到 PR + 审查 + 发布
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
CONFIG_FILE="$SCRIPT_DIR/config/repos.conf"
|
|
6
|
+
STATE_FILE="$SCRIPT_DIR/.review-state.json"
|
|
7
|
+
|
|
8
|
+
# 加载 Token
|
|
9
|
+
if [ -f "$SCRIPT_DIR/config/gitcode.conf" ]; then
|
|
10
|
+
source "$SCRIPT_DIR/config/gitcode.conf"
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [ -z "$GITCODE_API_TOKEN" ]; then
|
|
14
|
+
echo "❌ 错误: 未配置 GitCode API Token"
|
|
15
|
+
echo "请运行: ./gitcode-api.sh setup"
|
|
16
|
+
exit 1
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# 读取审查状态
|
|
20
|
+
load_state() {
|
|
21
|
+
if [ -f "$STATE_FILE" ]; then
|
|
22
|
+
cat "$STATE_FILE"
|
|
23
|
+
else
|
|
24
|
+
echo '{"reviewed": []}'
|
|
25
|
+
fi
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# 检查 PR 是否已审查
|
|
29
|
+
is_reviewed() {
|
|
30
|
+
local repo=$1
|
|
31
|
+
local pr_number=$2
|
|
32
|
+
local state=$(load_state)
|
|
33
|
+
|
|
34
|
+
if echo "$state" | grep -q "\"$repo#$pr_number\""; then
|
|
35
|
+
return 0
|
|
36
|
+
else
|
|
37
|
+
return 1
|
|
38
|
+
fi
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# 查找下一个需要审查的 PR
|
|
42
|
+
find_next_pr() {
|
|
43
|
+
while IFS= read -r repo; do
|
|
44
|
+
# 跳过注释和空行
|
|
45
|
+
[[ "$repo" =~ ^#.*$ ]] && continue
|
|
46
|
+
[[ -z "$repo" ]] && continue
|
|
47
|
+
|
|
48
|
+
local owner=$(echo "$repo" | cut -d'/' -f1)
|
|
49
|
+
local repo_name=$(echo "$repo" | cut -d'/' -f2)
|
|
50
|
+
|
|
51
|
+
# 获取开放的 PR(只获取前 10 个)
|
|
52
|
+
local pr_list=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
|
|
53
|
+
"https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls?state=opened&per_page=10")
|
|
54
|
+
|
|
55
|
+
# 提取第一个未审查的 PR
|
|
56
|
+
local pr_numbers=$(echo "$pr_list" | grep -o '"number":[0-9]*' | grep -o '[0-9]*')
|
|
57
|
+
|
|
58
|
+
for pr_number in $pr_numbers; do
|
|
59
|
+
if ! is_reviewed "$repo" "$pr_number"; then
|
|
60
|
+
# 返回格式: repo|pr_number|owner|repo_name
|
|
61
|
+
echo "$repo|$pr_number|$owner|$repo_name"
|
|
62
|
+
return 0
|
|
63
|
+
fi
|
|
64
|
+
done
|
|
65
|
+
done < "$CONFIG_FILE"
|
|
66
|
+
|
|
67
|
+
return 1
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# 主函数
|
|
71
|
+
main() {
|
|
72
|
+
echo "🤖 CANN 自动审查(单次完整流程)"
|
|
73
|
+
echo "=================================="
|
|
74
|
+
echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
75
|
+
echo ""
|
|
76
|
+
|
|
77
|
+
# 检查配置文件
|
|
78
|
+
if [ ! -f "$CONFIG_FILE" ]; then
|
|
79
|
+
echo "❌ 错误: 未配置审查仓库"
|
|
80
|
+
exit 1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# 查找下一个需要审查的 PR
|
|
84
|
+
echo "🔍 查找下一个需要审查的 PR..."
|
|
85
|
+
local result=$(find_next_pr)
|
|
86
|
+
|
|
87
|
+
if [ -z "$result" ]; then
|
|
88
|
+
echo ""
|
|
89
|
+
echo "✅ 所有 PR 都已审查完毕"
|
|
90
|
+
exit 0
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# 解析结果
|
|
94
|
+
local repo=$(echo "$result" | cut -d'|' -f1)
|
|
95
|
+
local pr_number=$(echo "$result" | cut -d'|' -f2)
|
|
96
|
+
local owner=$(echo "$result" | cut -d'|' -f3)
|
|
97
|
+
local repo_name=$(echo "$result" | cut -d'|' -f4)
|
|
98
|
+
|
|
99
|
+
echo "✅ 找到需要审查的 PR:"
|
|
100
|
+
echo " 仓库: $repo"
|
|
101
|
+
echo " PR: #$pr_number"
|
|
102
|
+
echo ""
|
|
103
|
+
|
|
104
|
+
# 调用审查技能
|
|
105
|
+
echo "📝 开始审查..."
|
|
106
|
+
echo ""
|
|
107
|
+
|
|
108
|
+
# 这里应该调用 OpenClaw 的审查功能
|
|
109
|
+
# 为了简单,我们直接输出链接
|
|
110
|
+
echo "💡 请使用以下命令审查这个 PR:"
|
|
111
|
+
echo ""
|
|
112
|
+
echo " 审查这个 PR: https://gitcode.com/$repo/merge_requests/$pr_number"
|
|
113
|
+
echo ""
|
|
114
|
+
echo "或者在 OpenClaw 中输入:"
|
|
115
|
+
echo " cann-review $repo $pr_number"
|
|
116
|
+
echo ""
|
|
117
|
+
|
|
118
|
+
echo "=================================="
|
|
119
|
+
echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# 运行主函数
|
|
123
|
+
main
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# 优化的单次审查脚本 - 修复 URL 提取问题
|
|
3
|
+
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
CONFIG_FILE="$SCRIPT_DIR/config/repos.conf"
|
|
6
|
+
STATE_FILE="$SCRIPT_DIR/.review-state.json"
|
|
7
|
+
|
|
8
|
+
# 加载 Token
|
|
9
|
+
if [ -f "$SCRIPT_DIR/config/gitcode.conf" ]; then
|
|
10
|
+
source "$SCRIPT_DIR/config/gitcode.conf"
|
|
11
|
+
fi
|
|
12
|
+
|
|
13
|
+
if [ -z "$GITCODE_API_TOKEN" ]; then
|
|
14
|
+
echo "❌ 错误: 未配置 GitCode API Token"
|
|
15
|
+
exit 1
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# 读取审查状态
|
|
19
|
+
load_state() {
|
|
20
|
+
if [ -f "$STATE_FILE" ]; then
|
|
21
|
+
cat "$STATE_FILE"
|
|
22
|
+
else
|
|
23
|
+
echo '{"reviewed": []}'
|
|
24
|
+
fi
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# 检查 PR 是否已审查
|
|
28
|
+
is_reviewed() {
|
|
29
|
+
local repo=$1
|
|
30
|
+
local pr_number=$2
|
|
31
|
+
local state=$(load_state)
|
|
32
|
+
|
|
33
|
+
if echo "$state" | grep -q "\"$repo#$pr_number\""; then
|
|
34
|
+
return 0
|
|
35
|
+
else
|
|
36
|
+
return 1
|
|
37
|
+
fi
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# 查找下一个需要审查的 PR
|
|
41
|
+
find_next_pr() {
|
|
42
|
+
while IFS= read -r repo; do
|
|
43
|
+
# 跳过注释和空行
|
|
44
|
+
[[ "$repo" =~ ^#.*$ ]] && continue
|
|
45
|
+
[[ -z "$repo" ]] && continue
|
|
46
|
+
|
|
47
|
+
local owner=$(echo "$repo" | cut -d'/' -f1)
|
|
48
|
+
local repo_name=$(echo "$repo" | cut -d'/' -f2)
|
|
49
|
+
|
|
50
|
+
echo " 检查: $repo" >&2
|
|
51
|
+
|
|
52
|
+
# 获取开放的 PR(只获取前 5 个)
|
|
53
|
+
local pr_list=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
|
|
54
|
+
"https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls?state=opened&per_page=5")
|
|
55
|
+
|
|
56
|
+
# 使用 Python 提取 PR 信息
|
|
57
|
+
echo "$pr_list" | python3 << 'PYEOF' "$repo"
|
|
58
|
+
import sys, json
|
|
59
|
+
|
|
60
|
+
repo = sys.argv[1]
|
|
61
|
+
try:
|
|
62
|
+
data = json.load(sys.stdin)
|
|
63
|
+
for pr in data:
|
|
64
|
+
pr_number = pr.get('number')
|
|
65
|
+
print(f"{repo}|{pr_number}")
|
|
66
|
+
except:
|
|
67
|
+
pass
|
|
68
|
+
PYEOF
|
|
69
|
+
|
|
70
|
+
done < "$CONFIG_FILE"
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# 主函数
|
|
74
|
+
main() {
|
|
75
|
+
echo "🤖 CANN 自动审查(单次模式 - 优化版)"
|
|
76
|
+
echo "======================================"
|
|
77
|
+
echo "开始时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
78
|
+
echo ""
|
|
79
|
+
|
|
80
|
+
# 检查配置文件
|
|
81
|
+
if [ ! -f "$CONFIG_FILE" ]; then
|
|
82
|
+
echo "❌ 错误: 未配置审查仓库"
|
|
83
|
+
exit 1
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# 统计仓库数量
|
|
87
|
+
REPO_COUNT=$(grep -v '^#' "$CONFIG_FILE" | grep -v '^$' | wc -l | tr -d ' ')
|
|
88
|
+
echo "📊 配置信息:"
|
|
89
|
+
echo " 仓库数量: $REPO_COUNT"
|
|
90
|
+
echo ""
|
|
91
|
+
|
|
92
|
+
# 查找下一个需要审查的 PR
|
|
93
|
+
echo "🔍 扫描仓库..."
|
|
94
|
+
local pr_list=$(find_next_pr)
|
|
95
|
+
|
|
96
|
+
if [ -z "$pr_list" ]; then
|
|
97
|
+
echo ""
|
|
98
|
+
echo "✅ 所有 PR 都已审查完毕"
|
|
99
|
+
exit 0
|
|
100
|
+
fi
|
|
101
|
+
|
|
102
|
+
# 找到第一个未审查的 PR
|
|
103
|
+
local found=false
|
|
104
|
+
while IFS='|' read -r repo pr_number; do
|
|
105
|
+
if ! is_reviewed "$repo" "$pr_number"; then
|
|
106
|
+
# 找到了
|
|
107
|
+
found=true
|
|
108
|
+
|
|
109
|
+
local owner=$(echo "$repo" | cut -d'/' -f1)
|
|
110
|
+
local repo_name=$(echo "$repo" | cut -d'/' -f2)
|
|
111
|
+
|
|
112
|
+
echo ""
|
|
113
|
+
echo "✅ 找到需要审查的 PR:"
|
|
114
|
+
echo " 仓库: $repo"
|
|
115
|
+
echo " PR: #$pr_number"
|
|
116
|
+
echo ""
|
|
117
|
+
|
|
118
|
+
# 获取 PR 详情
|
|
119
|
+
local pr_info=$(curl -s -H "Authorization: Bearer $GITCODE_API_TOKEN" \
|
|
120
|
+
"https://api.gitcode.com/api/v5/repos/$owner/$repo_name/pulls/$pr_number")
|
|
121
|
+
|
|
122
|
+
# 使用 Python 提取信息
|
|
123
|
+
echo "$pr_info" | python3 << 'PYEOF'
|
|
124
|
+
import sys, json
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
data = json.load(sys.stdin)
|
|
128
|
+
title = data.get('title', 'N/A')
|
|
129
|
+
author = data.get('user', {}).get('name', 'N/A')
|
|
130
|
+
html_url = data.get('html_url', 'N/A')
|
|
131
|
+
|
|
132
|
+
print(f" 标题: {title}")
|
|
133
|
+
print(f" 作者: {author}")
|
|
134
|
+
print(f" 链接: {html_url}")
|
|
135
|
+
except:
|
|
136
|
+
print(" 无法获取详细信息")
|
|
137
|
+
PYEOF
|
|
138
|
+
|
|
139
|
+
echo ""
|
|
140
|
+
echo "💡 审查命令:"
|
|
141
|
+
echo " 审查这个 PR: $html_url"
|
|
142
|
+
echo ""
|
|
143
|
+
|
|
144
|
+
# 标记为已审查(避免下次重复)
|
|
145
|
+
python3 << PYEOF
|
|
146
|
+
import json
|
|
147
|
+
from datetime import datetime
|
|
148
|
+
|
|
149
|
+
state = json.loads('''$(load_state)''')
|
|
150
|
+
key = "$repo#$pr_number"
|
|
151
|
+
|
|
152
|
+
if "reviewed" not in state:
|
|
153
|
+
state["reviewed"] = []
|
|
154
|
+
if key not in state["reviewed"]:
|
|
155
|
+
state["reviewed"].append(key)
|
|
156
|
+
state["last_review"] = {
|
|
157
|
+
"repo": "$repo",
|
|
158
|
+
"pr": $pr_number,
|
|
159
|
+
"time": datetime.now().isoformat()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
with open('$STATE_FILE', 'w') as f:
|
|
163
|
+
json.dump(state, f, indent=2)
|
|
164
|
+
|
|
165
|
+
print("✅ 已标记为已审查")
|
|
166
|
+
PYEOF
|
|
167
|
+
|
|
168
|
+
break
|
|
169
|
+
fi
|
|
170
|
+
done <<< "$pr_list"
|
|
171
|
+
|
|
172
|
+
if [ "$found" = false ]; then
|
|
173
|
+
echo ""
|
|
174
|
+
echo "✅ 所有 PR 都已审查完毕"
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
echo ""
|
|
178
|
+
echo "======================================"
|
|
179
|
+
echo "结束时间: $(date '+%Y-%m-%d %H:%M:%S')"
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
# 运行主函数
|
|
183
|
+
main
|