Compare commits

...

4 Commits

Author SHA1 Message Date
xiaoxia f5ad1b2b31 fix(ci): 修复CI Gate失败时返回exit 0而非exit 1的P0 Bug
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 18s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 46s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m14s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m17s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 32s
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m23s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 48s
AI Code Review / AI Code Review (pull_request) Successful in 1m19s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 3m26s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 4m21s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 8m56s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m11s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m56s
CI/CD Pipeline / Deploy Production (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 / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 2s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 18s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 34s
CI Gate判断失败时脚本错误地写了exit 0,导致所有PR不管CI失败成什么样,
Gate永远显示success,分支保护完全失效。
修复:将失败分支的exit 0改为exit 1
2026-07-28 10:12:35 +08:00
xiaoxia 02e3246f5a fix(ci): 格式修复防循环索引 + AI审查fail-open(2个bug修复) (#1045)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 55s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m3s
CI/CD Pipeline / Build Production API Image (push) Failing after 27s
CI/CD Pipeline / Build Production Web Image (push) Failing after 20s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m25s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1m18s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 23s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Validate - Code Quality (push) Failing after 2m7s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m4s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 2m3s
CI/CD Pipeline / Build Staging API Image (push) Failing after 2m6s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Unit Tests (push) Successful in 3m26s
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 2m46s
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been skipped
fix(ci): 修复auto_fix_formatting防循环索引错误 + AI审查fail-open未生效

1. 防循环索引bug:Gitea API返回commits倒序,commits[-1]取到最旧commit,改为commits[0]
2. fail-open bug:LLM调用失败和未捕获异常都是exit 1,改为exit 0不阻塞合并
2026-07-28 09:52:23 +08:00
xiaoxia 5cdafd2559 feat: AI代码审查添加commit status输出和阻塞级问题判定 (#1035)
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m49s
CI/CD Pipeline / Frontend Lint (push) Successful in 1m53s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m58s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 2m2s
CI/CD Pipeline / Build Staging API Image (push) Failing after 2m31s
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 2m33s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 2m55s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 34s
CI/CD Pipeline / Build Production API Image (push) Failing after 18s
CI/CD Pipeline / Build Production Web Image (push) Failing after 17s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 18s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Unit Tests (push) Successful in 4m38s
CI/CD Pipeline / Integration Tests (push) Successful in 4m21s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been cancelled
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
PR Automation / Auto Approve on CI Green (pull_request) Successful in 1m2s
- 为AI代码审查添加commit status输出,PR页面可直接看到审查结果
- 添加阻塞级问题判定逻辑,严重问题标记为failure状态
- 优化审查报告格式和输出精度
2026-07-27 21:40:07 +08:00
xiaoxia a6afb344ba feat: 格式自动修复对所有PR开放,添加防循环机制 (#1037)
CI/CD Pipeline / Validate - Type Check (mypy) (push) Failing after 0s
CI/CD Pipeline / Validate - Code Quality (push) Failing after 0s
CI/CD Pipeline / Validate - Migration (alembic) (push) Failing after 0s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Failing after 0s
CI/CD Pipeline / Integration Tests (push) Failing after 0s
CI/CD Pipeline / Unit Tests (push) Failing after 0s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 0s
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Failing after 28s
CI/CD Pipeline / Build Production Web Image (push) Failing after 30s
CI/CD Pipeline / Build Production API Image (push) Failing after 34s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 1m32s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (push) Failing after 2m12s
CI/CD Pipeline / Build Staging API Image (push) Failing after 2m13s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Has been skipped
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
2026-07-27 20:29:56 +08:00
4 changed files with 138 additions and 52 deletions
+1 -1
View File
@@ -1805,7 +1805,7 @@ jobs:
echo "❌ CI Gate: FAILED"
echo "失败项: ${FAILED_ITEMS[*]}"
echo "gate_result=failure" >> $GITHUB_OUTPUT
exit 0
exit 1
fi
- name: Report CI trace
+4 -2
View File
@@ -48,6 +48,7 @@ jobs:
GITEA_TOKEN: ${{ secrets.REVIEW_GITEA_TOKEN }}
REPO_NAME: ${{ gitea.repository }}
PR_NUMBER: ${{ gitea.event.pull_request.number }}
PR_HEAD_SHA: ${{ gitea.event.pull_request.head.sha }}
# LLM 提供商: coze (扣子原生Bot) / openai (OpenAI兼容)
LLM_PROVIDER: "coze"
# 扣子模式配置(默认国内站 api.coze.cn
@@ -60,8 +61,9 @@ jobs:
LLM_TIMEOUT: "120"
run: |
python3 scripts/ci_code_review.py
# 审查脚本异常不影响 CI 通过
continue-on-error: true
# 注意:脚本退出码决定job状态
# - 有阻塞级问题 → exit 1 → job失败 → 门禁拦截
# - 无阻塞级问题/LLM异常 → exit 0 → 通过(fail-open
- name: Report CI trace
if: always()
+24 -33
View File
@@ -1,8 +1,10 @@
#!/usr/bin/env python3
"""CI中自动修复代码格式(Python: black + isort | Frontend: prettier),并推送回原分支。
- PR事件:自动修复并push回PR源分支(Agent提交的PR自动修,人提交的仅诊断)
- PR事件:所有PR只要Code Quality因格式问题失败,自动修复并push回源分支
- Push事件(develop/main):自动修复并push回原分支,保持主干格式永远正确
- 防循环:修复commit带 [skip ci-format-check] 标记,检测到该标记则跳过修复
- 只修格式(black/isort/prettier),ruff逻辑类错误不动
当code quality检查因格式问题失败时触发。
"""
@@ -239,7 +241,7 @@ def main():
print("无法获取PR号,跳过自动修复")
return
# 获取PR作者信息,判断是人还是Agent提交的
# 获取PR信息
pr_info_url = f"{api_url}/repos/{repo}/pulls/{pr_number}"
req_pr = urllib.request.Request(pr_info_url, headers={"Authorization": f"token {token}"})
with urllib.request.urlopen(req_pr) as resp:
@@ -247,17 +249,26 @@ def main():
pr_author = pr_info.get("user", {}).get("login", "")
print(f"PR作者: {pr_author}")
# 判断是否为Agent提交的PR
agent_authors = {"actions", "auto-approve-bot", "gitea-actions"}
is_agent_pr = pr_author in agent_authors or "bot" in pr_author.lower()
# 防循环检测:检查最新commit是否已经是格式修复commit
# 修复commit message 带 [skip ci-format-check] 标记,检测到则跳过
head_branch_tmp = pr_info.get("head", {}).get("ref", "")
skip_marker = "[skip ci-format-check]"
try:
commits_url = f"{api_url}/repos/{repo}/pulls/{pr_number}/commits?limit=3"
req_commits = urllib.request.Request(commits_url, headers={"Authorization": f"token {token}"})
with urllib.request.urlopen(req_commits) as resp_commits:
commits = json.loads(resp_commits.read())
latest_msg = commits[0].get("commit", {}).get("message", "") if commits else ""
if skip_marker in latest_msg:
print(f"检测到最新commit包含 {skip_marker} 标记,跳过格式修复(防循环)")
print("本次格式检查失败是格式修复commit触发的CI回跑,属正常现象")
sys.exit(0)
except Exception as e:
print(f"⚠️ 防循环检测失败,继续执行: {e}")
if is_agent_pr:
print(f"检测到Agent提交的PR(作者: {pr_author},将自动修复并推送")
fix_mode = "auto_fix_and_push"
else:
print(f"检测到人提交的PR(作者: {pr_author}),仅诊断不自动修改")
print("(如需自动修复,请用Agent账号提交PR,或手动运行格式化脚本)")
fix_mode = "diagnose_only"
# 所有PR都自动修复格式(不再区分人/Agent)
print("检测到格式问题,将自动修复并推送回分支")
fix_mode = "auto_fix_and_push"
print("=== 检测到代码格式问题,尝试自动修复 ===")
print(f"PR #{pr_number}")
@@ -315,26 +326,6 @@ def main():
print("没有需要提交的格式改动")
return
# 诊断模式:只报告问题,不修改不推送
if fix_mode == "diagnose_only":
print()
print("=" * 50)
print("📋 格式问题诊断报告(人提交的PR,仅诊断不自动修复)")
print("=" * 50)
print()
print("以下文件存在格式问题,建议手动修复:")
for line in result.stdout.strip().split("\n"):
print(f" {line}")
print()
print("修复方式:")
print(" 后端(Python): 运行 black + isort")
print(" 前端: 运行 prettier --write")
print(" 或使用 scripts/agent-commit.sh 提交(自动格式化)")
print()
print("=" * 50)
# 以非0状态码退出,让CI继续报失败(因为问题没修)
sys.exit(1)
print()
print("变更文件:")
for line in result.stdout.strip().split("\n"):
@@ -342,7 +333,7 @@ def main():
# 提交修复
run("git add -A")
run('git commit -m "style: auto-format with black + isort + prettier"')
run('git commit -m "style: auto-format with black + isort + prettier [skip ci-format-check]"')
# 推送(head_branch已从ensure_git_repo获取)
print(f"\nPR来源分支: {head_branch}")
+109 -16
View File
@@ -10,6 +10,7 @@ import argparse
import json
import logging
import os
import re
import sys
from typing import Optional, Tuple
@@ -183,6 +184,37 @@ class GiteaClient:
return False
return True
def create_commit_status(
self, sha: str, state: str, context: str, description: str = "", target_url: str = ""
) -> bool:
"""
给指定 commit 打 status。
state: pending / success / failure / error / warning
Gitea API: POST /repos/{owner}/{repo}/statuses/{sha}
"""
url = self._api_url(f"statuses/{sha}")
logger.info(f"设置 commit status: sha={sha[:12]}..., state={state}, context={context}")
payload = {
"state": state,
"context": context,
"description": description[:200] if description else "",
}
if target_url:
payload["target_url"] = target_url
resp = self.session.post(
url,
data=json.dumps(payload),
timeout=GITEA_TIMEOUT,
)
if resp.status_code not in (200, 201):
logger.error(f"设置 status 失败: HTTP {resp.status_code} - {resp.text[:200]}")
return False
logger.info(f"Status 设置成功: {context} = {state}")
return True
def call_llm_openai(
prompt: str,
@@ -416,7 +448,22 @@ def build_review_prompt(diff_text: str, pr_number: int, file_list: list) -> str:
```
## 审查要求
请从以下维度进行审查,重点关注严重问题
请从以下维度进行审查,重点关注**阻塞级问题**
### 问题分级标准
- **🔴 阻塞级(BLOCKER)**:必须修复,否则不允许合并。包括:
1. **明显逻辑bug**:条件判断错误、死循环、返回值错误、空指针/None引用未处理、边界条件遗漏导致功能异常
2. **安全漏洞**:SQL注入、XSS、命令注入、敏感信息明文存储/泄露、权限绕过、认证缺失
3. **语法错误**:代码存在语法层面的错误,无法运行
4. **数据损坏风险**:可能导致数据丢失、数据不一致、脏数据写入的问题
- **💡 建议级(SUGGESTION)**:不阻塞合并,仅供参考改进。包括:
1. 命名不规范、代码风格问题
2. 最佳实践建议、设计模式优化
3. 格式问题(缩进、空行、import顺序等)
4. 代码可读性改进、注释补充
5. 非关键路径的轻微性能优化建议
6. 重复代码、过长函数等代码质量问题
1. **逻辑正确性**:是否有明显的逻辑错误、边界条件遗漏、空指针/None引用风险
2. **异常处理**:异常捕获是否合理,是否有裸except,错误处理是否完善
@@ -426,20 +473,24 @@ def build_review_prompt(diff_text: str, pr_number: int, file_list: list) -> str:
6. **安全问题**:是否有注入风险、敏感信息泄露、权限控制问题
## 输出格式
请使用以下格式输出,语言为中文:
请使用以下格式输出,语言为中文。**必须严格按照格式输出,尤其是【阻塞级判定】部分**
### 【阻塞级判定】
- 是否存在阻塞级问题:(是 / 否)
- 阻塞级问题数量:X 个
### 📊 审查概览
- 整体评价:(通过 / 有建议 / 需修改)
- 严重问题数量:X 个
- 一般建议数量:X 个
- 建议级问题数量:X 个
### ❌ 需修改的问题(严重
(如果没有严重问题,写""
### 🔴 阻塞级问题(必须修复
(如果没有阻塞级问题,写""
1. **[文件: 行号] 问题标题**
- 问题类型:(逻辑bug / 安全漏洞 / 语法错误 / 数据损坏风险)
- 问题描述:...
- 修改建议:...
### 💡 改进建议(一般
### 💡 改进建议(不阻塞合并
(如果没有建议,写""
1. **[文件: 行号] 建议标题**
- 具体内容:...
@@ -448,10 +499,46 @@ def build_review_prompt(diff_text: str, pr_number: int, file_list: list) -> str:
(可选,列出值得肯定的地方)
请务必基于代码实际内容审查,不要编造不存在的问题。如果代码质量良好,直接给出通过结论即可。
**重要:【阻塞级判定】必须准确,只有确实存在严重问题时才写""。**
"""
return prompt
def parse_blocker_result(review_text: str) -> Tuple[bool, int]:
"""
从审查结果中解析是否存在阻塞级问题。
返回 (has_blocker, blocker_count)
"""
# 先找【阻塞级判定】部分的明确标记
pattern = r"【阻塞级判定】[\s\S]*?是否存在阻塞级问题[:]\s*(是|否)"
match = re.search(pattern, review_text)
if match:
has_blocker = match.group(1) == ""
else:
# fallback 1: 找"阻塞级问题数量"
count_pattern = r"阻塞级问题数量[:]\s*(\d+)"
count_match = re.search(count_pattern, review_text)
if count_match:
has_blocker = int(count_match.group(1)) > 0
else:
# fallback 2: 检查是否有"阻塞级问题"section且内容不是"无"
has_blocker = False
blocker_section = re.search(r"### 🔴 阻塞级问题[\s\S]*?(?=### |\Z)", review_text)
if blocker_section:
section_text = blocker_section.group(0)
# 如果有编号列表项,说明有问题
if re.search(r"\d+\.\s*\*\*", section_text):
has_blocker = True
# 提取数量
count_pattern = r"阻塞级问题数量[:]\s*(\d+)"
count_match = re.search(count_pattern, review_text)
blocker_count = int(count_match.group(1)) if count_match else (1 if has_blocker else 0)
logger.info(f"阻塞级问题解析: 存在={has_blocker}, 数量={blocker_count}")
return has_blocker, blocker_count
def call_llm_for_review(
diff_text: str,
pr_number: int,
@@ -628,7 +715,7 @@ def main():
if not review_result:
logger.error("LLM 审查失败")
sys.exit(1)
sys.exit(0) # fail-open: LLM调用失败不阻塞合并
# 7. 加上审查时间和标识(便于识别是自动审查)
from datetime import datetime
@@ -669,18 +756,24 @@ def main():
logger.error("评论发布失败")
sys.exit(1)
# 10. 判断是否有严重问题(可选阻断)
# 目前只做建议,不阻断合并,始终返回 0
has_critical = "问题" in review_result and ("" in review_result or "需修改" in review_result)
if has_critical:
logger.warning("检测到需修改的问题,但当前配置为仅建议,不阻断合并")
# 10. 解析阻塞级问题,用退出码决定 job 状态
# 有阻塞级问题 → exit 1 → job失败 → Gitea自动打failure status → 门禁拦截
# 无阻塞级问题 → exit 0 → job成功 → Gitea自动打success status
# LLM调用失败等异常 → exit 0 → fail-open,不阻塞正常开发
has_blocker, blocker_count = parse_blocker_result(review_result)
logger.info("代码审查完成")
sys.exit(0)
if has_blocker:
logger.error(f"检测到 {blocker_count} 个阻塞级问题,审查不通过")
logger.info("代码审查完成(失败)")
sys.exit(1)
else:
logger.info("无阻塞级问题,审查通过")
logger.info("代码审查完成(通过)")
sys.exit(0)
except Exception as e:
logger.exception(f"审查脚本发生未预期的异常: {e}")
sys.exit(1)
sys.exit(0) # fail-open: 异常不阻塞正常开发
if __name__ == "__main__":