# pre-commit 自动化代码审查配置 # 详见 docs/代码规范/pre-commit使用说明.md default_install_hook_types: [pre-commit, commit-msg] exclude: | (?x)^( \.opencode/node_modules/| Lib/lvgl/| simulator/| MDK-ARM/RTE/ ) repos: # 1) 通用钩子(官方) - repo: https://github.com/pre-commit/pre-commit-hooks rev: v5.0.0 hooks: - id: trailing-whitespace # 排除第三方库(CubeMX 重生成会带回行尾空白,避免每次重生成产生提交抖动); # 自研代码(Drivers/BSP、Src、Inc、App 等)不受影响 exclude: | (?x)^( Drivers/CMSIS/| Drivers/STM32F4xx_HAL_Driver/| Middlewares/ ) - id: end-of-file-fixer - id: check-added-large-files - id: check-merge-conflict - id: check-yaml # 2) C 代码格式:只检查,绝不改写(编码铁律) # Route A:暂不启用,待全库格式化基线后再放开 # - repo: https://github.com/pre-commit/mirrors-clang-format # rev: v19.1.4 # hooks: # - id: clang-format # args: [--dry-run, --Werror] # 3) commit message 格式(宽松规则,兼容中文标题) - repo: https://github.com/jorisroovers/gitlint rev: v0.19.1 hooks: - id: gitlint # 4) 本地自定义钩子(仓库级) - repo: local hooks: - id: cppcheck name: cppcheck entry: cppcheck # 注意:YAML 流序列中逗号是元素分隔符,含逗号/空格的参数必须加引号 args: ['--enable=warning,performance,portability,style', '--error-exitcode=1', '--inline-suppr', '--std=c99', '--quiet', '--suppress=missingIncludeSystem', '--suppress=missingInclude', '--suppress=invalidPrintfArgType_sint', '--suppress=invalidPrintfArgType_uint', '--suppress=unusedFunction', '--suppress=unusedStructMember', '--suppress=noExplicitConstructor', '--suppress=useStlAlgorithm', '--suppress=constParameterPointer', '--suppress=constParameterCallback', '--suppress=redundantAssignment', '--suppress=normalCheckLevelMaxBranches', '-I', 'Inc', '-I', 'App', '-I', 'Drivers/BSP', '-I', 'Drivers/STM32F4xx_HAL_Driver/Inc', '-I', 'Drivers/CMSIS/Include', '-I', 'Middlewares/Third_Party/FreeRTOS/Source/include'] language: system types: [c] # 仅扫描项目自有代码;排除第三方库与 LVGL 依赖文件 # (UI 生成文件 ui_font_cn_12/ui_menu_tab 由生成器按工程约定产出,纳入受检) exclude: | (?x)^( Drivers/CMSIS/| Drivers/STM32F4xx_HAL_Driver/| Drivers/BSP/NET/lftpd/| Drivers/BSP/dhara/| Lib/| Middlewares/| MDK-ARM/| test/ ) # 4b) test/ 代码宽松静态检查(S5,2026-10-03 加) # 背景:`test/` 此前完全豁免 pre-commit(靠"自律"),而 `test/board/` 下 # 6300+ 行 C 代码**且编入固件**(曾出现 `s_rx_buf` 使用先于声明)。 # 本钩子**只开 warning**(不引入存量 style 噪音),排除模拟器桩 # (`test/host/*/stub/`,第三方/桩头形态不受控)。 # 基线:2026-10-03 全量实测 **0 条 warning**(存量可控,故直接挂钩子)。 # 依据:docs/审查记录/测试与架构改进建议.md §3.4;实机测试实施指南.md §8.1 - id: test-cppcheck name: cppcheck (test/, warning only) entry: cppcheck args: ['--enable=warning', '--error-exitcode=1', '--inline-suppr', '--std=c99', '--quiet', '--suppress=missingIncludeSystem', '--suppress=missingInclude', '--suppress=invalidPrintfArgType_sint', '--suppress=invalidPrintfArgType_uint', '--suppress=unusedFunction', '--suppress=unusedStructMember', '--suppress=constParameterPointer', '--suppress=constParameterCallback', '--suppress=redundantAssignment', '--suppress=normalCheckLevelMaxBranches', '-i', 'test/host/watchdog/stub', '-i', 'test/host/cfg_file/stub', '-i', 'test/host/sys_clock/stub', '-I', 'Inc', '-I', 'App', '-I', 'Drivers/BSP', '-I', 'Drivers/STM32F4xx_HAL_Driver/Inc', '-I', 'Drivers/CMSIS/Include', '-I', 'Middlewares/Third_Party/FreeRTOS/Source/include', 'test/'] language: system # 触发条件:test/ 下 C/头文件变动;扫描范围恒为整个 test/(pass_filenames=false) files: ^test/.*\.(c|h)$ pass_filenames: false - id: no-mojibake name: Forbid U+FFFD mojibake entry: python scripts/check_mojibake.py language: system types: [c] - id: coding-standard name: C coding standard (规范V1.0) entry: python scripts/check_coding_standard.py language: system types: [c] # 范围与 cppcheck 钩子一致;另排除 CubeMX 生成文件(Src/、Inc/,重新生成即覆盖)、 # dhara/FatFS 接口适配层(nand_ftl,函数名由外部 API 固定) exclude: | (?x)^( Drivers/CMSIS/| Drivers/STM32F4xx_HAL_Driver/| Drivers/BSP/NET/lftpd/| Drivers/BSP/GD5F2GQ5UE/nand_ftl\.| Drivers/BSP/dhara/| Lib/| Middlewares/| MDK-ARM/| test/| Src/| Inc/ ) # 5) 文档一致性门禁(D-7) # 存在性:md 互链 / 反引号路径是否指向真实文件(防迁移/删除后残留死链) - id: doc-links name: 文档链接存在性 (D-3/D-7) entry: python scripts/check_doc_links.py language: system files: \.md$ # 外部资料(芯片手册转录)链接形态不受控;第三方目录整体排除 exclude: | (?x)^( \.opencode/node_modules/| Lib/lvgl/| simulator/| MDK-ARM/RTE/| docs/芯片手册/ ) # 数值:README/AGENTS 任务表须与 Src/freertos.c 一致(D-2/D-7) - id: task-table name: FreeRTOS 任务表一致性 (D-2/D-7) entry: python scripts/gen_task_table.py --check language: system files: ^(README\.md|AGENTS\.md|Src/freertos\.c)$ pass_filenames: false # 体积:内存/Flash 数字须与最新 .map 一致(D-7 第三档) # 只在内存文档或代码变动时触发;.map 未生成时脚本自身给出明确提示 - id: mem-numbers name: 内存/Flash 数字一致性 (D-7 体积档) entry: python scripts/check_mem_numbers.py --scan language: system files: ^(docs/内存管理/.*\.md|scripts/check_mem_numbers\.py|App/.*\.c|App/.*\.h|Src/.*\.c|Inc/.*\.h)$ pass_filenames: false # 6) CubeMX 重生成存活审计(2026-09-29 加) # 背景:.ioc 的 KeepUserCode 只保护 USER CODE 标记区;标记之外的手工改动 # 重生成即被覆盖,且多数不报编译错(崩溃采集被打回空 while(1)、加固宏被删)。 # 本钩子验证"重生成易失清单"各项是否仍在预期位置,并对关键区做哈希漂移检测。 # 范围:仅在这些易失文件变动时触发(改动 .ioc 或生成文件本身就值得复核)。 - id: cubemx-survival name: CubeMX 重生成存活审计 entry: python scripts/check_cubemx_survival.py --check language: system files: ^(STM32F407-Demo\.ioc|Src/.*\.(c|h)|Inc/.*\.h|App/util/task_watchdog\.(c|h)|MDK-ARM/.*\.uvprojx|scripts/check_cubemx_survival\.py)$ pass_filenames: false # 7) 看门狗判定状态机主机侧单元测试(2026-10-01 加) # 背景:task_watchdog.c 的判定逻辑(位图取走清零 / 掩码 / miss_streak # 累计 / 100ms 节流 / tick 回绕)是纯逻辑,板端套件无法穷举边界;本钩子 # 用 MSVC 离线编译真实源文件(#include 纳入)并用可观测桩驱动,秒级、零 # 硬件、不占 MCU RAM。仅在被测文件或其平台层 / 测试变动时触发。 # 详见 docs/测试说明/看门狗单元测试说明.md - id: watchdog-unit-test name: 看门狗判定状态机单测(主机侧) entry: python test/host/watchdog/run_unit_test.py language: system files: ^(App/util/task_watchdog\.(c|h)|test/host/watchdog/.*|test/host/common/unit_test_harness\.py)$ pass_filenames: false # 7b) 系统时钟时间换算主机侧单元测试(2026-10-02 加) # 背景:sys_clock.c 的 Unix↔日历换算(闰年 / 月末 / 世纪 / 2038 上限)、 # 星期(Zeller)、"30 天惰性再基准化"(防 49.7 天 tick 回绕)、RTC 故障 # 降级路径均为纯逻辑;本钩子用 MSVC 离线编译真实源文件(#include 纳入) # 并以可注入桩驱动(可控时钟 + 阴影 RTC)。首次落地即抓出 Zeller 公式 # 缺陷(1970~2099 全部日期星期皆错,详见测试说明文档)。 # files 含 sys_clock.c 依赖的 sd2506.h(时间结构/返回值定义)。 - id: sys-clock-unit-test name: 系统时钟换算单测(主机侧) entry: python test/host/sys_clock/run_unit_test.py language: system files: ^(App/util/sys_clock\.(c|h)|Drivers/BSP/SD2506/sd2506\.h|test/host/sys_clock/.*|test/host/common/unit_test_harness\.py)$ pass_filenames: false # 7c) 配置引擎主机侧单元测试(2026-10-02 加,S1-b) # 背景:cfg_file.c 的核心是"文本 <-> 结构"纯变换(INI 段/键解析、四种值 # 类型编解码、外科手术式保存、超长守卫、load/save/poll 契约);板端套件只 # 保存到缺失/新文件(走 s_gen_document 路径),**从不触发对已存在文件的外科 # 手术改写**,故掩盖了该路径的缺陷。本钩子用 MSVC 离线编译真实源文件 # (#include 纳入)+ 内存虚拟文件系统后端驱动,首次落地即抓出 P0: # `s_encode(...) == 0` 恒假(其成功返回文本长度而非 0)使**外科手术保存的 # 受管值改写路径沦为死码**——对已存在 /config.ini 保存时受管键从不改写(静默 # 不落盘,且 net_cfg_save 仍报 OK)。详见测试说明文档。 - id: cfg-file-unit-test name: 配置引擎单测(主机侧) entry: python test/host/cfg_file/run_unit_test.py language: system files: ^(App/cfg/cfg_file\.(c|h)|App/cfg/cfg_file_store\.h|App/log/sys_log\.h|test/host/cfg_file/.*|test/host/common/unit_test_harness\.py)$ pass_filenames: false # 8) 测试套件开关门禁(2026-10-01 加) # 背景:test_config.h 的 TEST_SUITE_* 决定固件里编入哪个测试套件, # 全部注释 = 产品构建。但此前**无任何门禁检查**——实测已提交的 HEAD 里 # TEST_SUITE_CH395F 就是开启状态(.map 含 34 处 test 代码),即仓库里 # 躺着的是测试固件。本钩子抓这个静默漏网:默认要求产品构建,跑套件回归 # 确需提交测试构建时用 `--allow-test-build` / ALLOW_TEST_BUILD=1 显式放行。 # 详见 docs/待处理内容.md T-1 - id: test-suite-switch name: 测试套件开关=产品构建 entry: python scripts/check_test_suite.py language: system files: ^test/board/test_config\.h$ pass_filenames: false