Skip to content

Commit a18cb35

Browse files
committed
feat(backend): github ci 加上检查-makemigrations 检查、import检查 #15341
1 parent 0eaa34c commit a18cb35

File tree

4 files changed

+118
-19
lines changed

4 files changed

+118
-19
lines changed

dbm-ui/backend/core/translation/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,7 @@ class UnTranslatedFileExistException(CoreBaseException):
2525

2626
class LanguageSpecificFStringException(CoreBaseException):
2727
MESSAGE = _("存在包含特定翻译语言的f-string")
28+
29+
30+
class IllegalImportException(CoreBaseException):
31+
MESSAGE = _("存在非法导入")

dbm-ui/backend/core/translation/language_finder.py

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@
2626
Language,
2727
LanguageFindMode,
2828
)
29-
from backend.core.translation.exceptions import LanguageSpecificFStringException, UnTranslatedFileExistException
29+
from backend.core.translation.exceptions import (
30+
IllegalImportException,
31+
LanguageSpecificFStringException,
32+
UnTranslatedFileExistException,
33+
)
3034

3135
logger = logging.getLogger("root")
3236

@@ -54,7 +58,8 @@ class NodeTranslateInit(ast.NodeVisitor):
5458
获取当前文件导入的翻译函数,并检查文件中的format字符
5559
"""
5660

57-
ImportPath = "django.utils.translation"
61+
TranslateImportPath = "django.utils.translation"
62+
IllegalImportPaths = ["backend.db_periodic_task.local_tasks"]
5863

5964
def __init__(self, ignored_string, string_regex, file_path, *args, **kwargs):
6065
super(NodeTranslateInit, self).__init__(*args, **kwargs)
@@ -63,6 +68,8 @@ def __init__(self, ignored_string, string_regex, file_path, *args, **kwargs):
6368
self.string_regex = string_regex
6469
self.trans_func_names = []
6570
self.formatted_strings = []
71+
self.illegal_imports = []
72+
self.illegal_import_paths = self.IllegalImportPaths
6673

6774
@classmethod
6875
def get_node_module(cls, node):
@@ -74,10 +81,39 @@ def get_node_module(cls, node):
7481
def _check_special_language(self, string):
7582
return check_special_language(self.ignored_string, self.file_path, self.string_regex, string)
7683

84+
def visit_Import(self, node):
85+
"""检查直接import语句是否包含非法导入"""
86+
for name in node.names:
87+
if any(name.name.startswith(path) for path in self.illegal_import_paths):
88+
self.illegal_imports.append(
89+
{
90+
"line": node.lineno,
91+
"col": node.col_offset,
92+
"module": name.name,
93+
"file_path": self.file_path,
94+
"type": "import",
95+
}
96+
)
97+
logger.warning(f"Illegal import found: {name.name} at line {node.lineno} in {self.file_path}")
98+
7799
def visit_ImportFrom(self, node):
78100
"""游走到import,缓存翻译函数"""
79101

80-
if node.module != self.ImportPath:
102+
# 检查是否导入了 from backend.db_periodic_task
103+
if node.module and any(node.module.startswith(path) for path in self.illegal_import_paths):
104+
self.illegal_imports.append(
105+
{
106+
"line": node.lineno,
107+
"col": node.col_offset,
108+
"module": node.module,
109+
"names": [name.name for name in node.names],
110+
"file_path": self.file_path,
111+
"type": "from",
112+
}
113+
)
114+
logger.warning(f"Illegal import from local_tasks at line {node.lineno} in {self.file_path}")
115+
116+
if node.module != self.TranslateImportPath:
81117
return
82118

83119
for name in node.names:
@@ -92,7 +128,7 @@ def visit_Str(self, node):
92128
# 标记Module,后续可能添加import函数
93129
module_node = self.get_node_module(node)
94130
for import_from in module_node.body:
95-
if isinstance(import_from, ast.ImportFrom) and import_from.module == self.ImportPath:
131+
if isinstance(import_from, ast.ImportFrom) and import_from.module == self.TranslateImportPath:
96132
return
97133

98134
module_node.language_flag = True
@@ -225,7 +261,7 @@ def visit_Module(self, node):
225261
node.body.insert(
226262
0,
227263
ast.ImportFrom(
228-
module=NodeTranslateInit.ImportPath, names=[ast.alias(name="gettext", asname="_")], level=0
264+
module=NodeTranslateInit.TranslateImportPath, names=[ast.alias(name="gettext", asname="_")], level=0
229265
),
230266
)
231267
return node
@@ -248,6 +284,7 @@ class LanguageFinder:
248284
# 写入待翻译信息的文件名
249285
TRANSLATE_INFO_FILE: str = "backend/locale/translate_info.json"
250286
FORMATTED_STRINGS_FILE: str = "backend/locale/formatted_string_info.json"
287+
ILLEGAL_IMPORTS_FILE: str = "backend/locale/illegal_imports_info.json"
251288

252289
def __init__(
253290
self,
@@ -277,6 +314,7 @@ def __init__(
277314
"mock.py",
278315
"mock_data.py",
279316
"backend/bk_dataview/grafana",
317+
"local_tasks",
280318
}
281319
if exclude_dir_or_file_list:
282320
_exclude_dir_or_file_list.update(set(exclude_dir_or_file_list))
@@ -296,6 +334,8 @@ def __init__(
296334
self.sentences_to_be_translated = {}
297335
# 缓存format的字符串
298336
self.formatted_strings = {}
337+
# 缓存非法导入信息
338+
self.illegal_imports = {}
299339

300340
def check_file(self, file_path):
301341
"""
@@ -316,6 +356,11 @@ def check_file(self, file_path):
316356
string_regex=LANGUAGE_REGEX_MAP[self.translate_language],
317357
)
318358
tree.visit(nodes)
359+
360+
# 收集非法导入信息
361+
if tree.illegal_imports:
362+
self.illegal_imports[file_path] = tree.illegal_imports
363+
319364
# 缓存当前文件的format语句
320365
if tree.formatted_strings:
321366
self.formatted_strings[file_path] = tree.formatted_strings
@@ -393,7 +438,7 @@ def run(self):
393438

394439
# 导入翻译函数包, 默认为gettext, TODO: 做成一个参数传递?
395440
if getattr(translate_nodes, "language_flag", False):
396-
file_lines.insert(1, f"from {NodeTranslateInit.ImportPath} import gettext as _\n")
441+
file_lines.insert(1, f"from {NodeTranslateInit.TranslateImportPath} import gettext as _\n")
397442

398443
with open(each_file, "w") as f:
399444
f.write("".join(file_lines))
@@ -413,6 +458,18 @@ def run(self):
413458
f"There are f-strings containing the specific translation language in the project:"
414459
f"{json.dumps(self.formatted_strings, ensure_ascii=False, indent=4)}"
415460
)
461+
if self.illegal_imports:
462+
# TODO: 待import问题解决后,恢复此异常抛出
463+
# raise IllegalImportException(
464+
# f"There are illegal imports in the project: "
465+
# f"{json.dumps(self.illegal_imports, ensure_ascii=False, indent=4)}"
466+
# )
467+
logger.error(
468+
IllegalImportException(
469+
f"There are illegal imports in the project: "
470+
f"{json.dumps(self.illegal_imports, ensure_ascii=False, indent=4)}"
471+
)
472+
)
416473
else:
417474
# 写入未翻译信息
418475
with open(self.TRANSLATE_INFO_FILE, "w+") as f:
@@ -421,3 +478,6 @@ def run(self):
421478
# 写入format字符串信息
422479
with open(self.FORMATTED_STRINGS_FILE, "w+") as f:
423480
f.write(json.dumps(self.formatted_strings, ensure_ascii=False, indent=4))
481+
# 写入非法导入信息
482+
with open(self.ILLEGAL_IMPORTS_FILE, "w+") as f:
483+
f.write(json.dumps(self.illegal_imports, ensure_ascii=False, indent=4))

dbm-ui/scripts/ci/bk_ci.sh

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,44 @@
11
#!/bin/bash
2-
# 当前脚本目录
3-
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
2+
# ============================================================
3+
# DBM 项目 CI 检查主脚本
4+
# 功能:执行项目的完整性检查、安装和质量控制
5+
# ============================================================
46

5-
cat << EOF
6-
SCRIPT_DIR -> "$SCRIPT_DIR"
7-
EOF
7+
# 获取当前脚本所在目录
8+
SCRIPT_DIR=$(dirname $(readlink -f "$0"))
9+
echo "当前脚本目录: $SCRIPT_DIR"
10+
echo "============================================================"
811

12+
# 初始化失败计数器
913
FAILED_COUNT=0
1014

15+
# ============================================================
16+
# 执行各个检查步骤
17+
# ============================================================
18+
19+
# 1. 准备服务环境
20+
echo "[1/3] 准备服务环境..."
1121
${SCRIPT_DIR}/prepare_services.sh
1222
FAILED_COUNT=$[$FAILED_COUNT+$?]
1323

24+
# 3. 安装依赖
25+
echo "[2/3] 安装项目依赖并进行迁移文件检查..."
1426
${SCRIPT_DIR}/install.sh
1527
FAILED_COUNT=$[$FAILED_COUNT+$?]
1628

29+
# 5. 代码质量检查
30+
echo "[3/3] 执行代码质量检查..."
1731
${SCRIPT_DIR}/code_quality.sh
1832
FAILED_COUNT=$[$FAILED_COUNT+$?]
1933

20-
echo "命令执行失败数量:$FAILED_COUNT"
21-
if [[ $FAILED_COUNT -ne 0 ]];
22-
then
23-
echo "Error: 单元测试未通过!"
24-
exit 1
34+
# ============================================================
35+
# 汇总检查结果
36+
# ============================================================
37+
echo "============================================================"
38+
if [[ $FAILED_COUNT -ne 0 ]]; then
39+
echo "❌ CI 检查失败:共有 $FAILED_COUNT 个检查项未通过"
40+
exit 1
2541
else
26-
echo "单元测试已通过"
27-
fi
42+
echo "🎉 所有 CI 检查通过!"
43+
exit 0
44+
fi

dbm-ui/scripts/ci/install.sh

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,32 @@ python manage.py createcachetable django_cache
7373
python manage.py language_finder -p backend/ -m error
7474
if [[ $? -ne 0 ]];
7575
then
76-
echo -e "\033[1;31m Error: python manage.py language_finder -p backend/ -m error 执行失败!请检查中文是否已标记 \033[0m"
76+
echo -e "\033[1;31m Error: python manage.py language_finder -p backend/ -m error 执行失败!请检查中文是否已标记并且是否存在非法导入 \033[0m"
7777
FAILED_COUNT=$[$FAILED_COUNT+1]
7878
fi
7979

80+
echo "正在检查是否有未提交的数据库迁移文件..."
81+
MIGRATION_OUTPUT=$(python manage.py makemigrations --dry-run --check 2>&1)
82+
83+
# 捕获命令的退出状态
84+
STATUS=$?
85+
86+
if [ $STATUS -ne 0 ]; then
87+
echo "❌ 错误:检测到模型变更但未生成对应的迁移文件!"
88+
echo ""
89+
echo "====== 迁移详情 ======"
90+
echo "$MIGRATION_OUTPUT"
91+
exit 1
92+
else
93+
echo "✅ 检查通过:没有未提交的模型变更。"
94+
exit 0
95+
fi
96+
8097
if [[ $FAILED_COUNT -ne 0 ]];
8198
then
8299
echo -e "\033[1;31m Error: 前置命令未通过! 前置命令执行失败数量: $FAILED_COUNT \033[0m"
83100
exit 1
84101
else
85102
echo "前置命令已通过"
86103
fi
104+

0 commit comments

Comments
 (0)