已合并
infra(pre-commit): 引入 pre-commit/OAT 检查体系 #41
infra(pre-commit): 引入 pre-commit/OAT 检查体系 #41
已合并
景明创建于 21 天前
共 8 个文件变更+953-2
@@ -0,0 +1,72 @@
1+# -----------------------------------------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# -----------------------------------------------------------------------------------------------------------
10+ 
11+# Usage:
12+# Using clang-format to reformat a file:
13+# clang-format -i -style=file <file>
14+ 
15+BasedOnStyle: Google
16+ColumnLimit: 120
17+AccessModifierOffset: -4
18+SortIncludes: false
19+SortUsingDeclarations: false
20+BreakBeforeBraces: Custom
21+ 
22+# 大括号换行配置
23+BraceWrapping:
24+ AfterCaseLabel: false
25+ AfterClass: false
26+ AfterControlStatement: false
27+ # AfterEnum: false (紧凑模式,左大括号和enum同行, enum Color {...), AfterEnum: true (左大括号和enum换行).
28+ AfterEnum: false
29+ AfterFunction: true
30+ AfterNamespace: false
31+ AfterStruct: false
32+ AfterUnion: false
33+ AfterExternBlock: false
34+ BeforeCatch: false
35+ BeforeElse: false
36+ IndentBraces: false
37+ SplitEmptyFunction: false
38+ SplitEmptyRecord: true
39+ SplitEmptyNamespace: true
40+ 
41+# Align
42+AlignAfterOpenBracket: Align
43+PenaltyBreakAssignment: 2
44+AlignEscapedNewlines: DontAlign
45+AlignOperands: true
46+AlignTrailingComments: true
47+AllowAllArgumentsOnNextLine: true
48+AllowAllParametersOfDeclarationOnNextLine: true
49+AlwaysBreakBeforeMultilineStrings: false
50+AlwaysBreakTemplateDeclarations: true
51+ 
52+BinPackArguments: true
53+BreakBeforeTernaryOperators: false
54+SpacesBeforeTrailingComments: 1
55+PackConstructorInitializers: Never
56+ 
57+# pointer aligment
58+DerivePointerAlignment: false
59+PointerAlignment: Left
60+ 
61+# single line layout
62+AllowShortBlocksOnASingleLine: false
63+AllowShortCaseLabelsOnASingleLine: false
64+AllowShortEnumsOnASingleLine: false
65+AllowShortFunctionsOnASingleLine: Empty
66+AllowShortIfStatementsOnASingleLine: false
67+AllowShortLoopsOnASingleLine: false
68+ 
69+# tab space
70+UseTab: Never
71+IndentWidth: 4
72+TabWidth: 4
@@ -47,3 +47,4 @@ captures/
47# opencode / agent 本地配置47# opencode / agent 本地配置
48.opencode/48.opencode/
49.claude/49.claude/
50+pre-commit_reports/
@@ -0,0 +1,88 @@
1+# -----------------------------------------------------------------------------------------------------------
2+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4+# CANN Open Software License Agreement Version 2.0 (the "License").
5+# Please refer to the License for details. You may not use this file except in compliance with the License.
6+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
8+# See LICENSE in the root of the software repository for the full text of the License.
9+# -----------------------------------------------------------------------------------------------------------
10+minimum_pre_commit_version: 4.0.0
11+exclude: ^third_party/|\.(html|csv|svg)$
12+ 
13+default_stages: [pre-commit]
14+ci:
15+ autofix_prs: false
16+ autoupdate_schedule: monthly
17+ 
18+repos:
19+ # pre-commit 基础检查
20+ - repo: https://gitcode.com/pre-commit/pre-commit-hooks
21+ rev: v4.6.0
22+ hooks:
23+ - id: trailing-whitespace
24+ # md 保留行尾双空格(markdown 硬换行语法),其余尾随空白照删
25+ args: ["--markdown-linebreak-ext=md"]
26+ - id: end-of-file-fixer
27+ - id: check-yaml
28+ args: ["--allow-multiple-documents"]
29+ - id: check-added-large-files
30+ # eval 数据集(reviewer_bench 等)合法大文件,新增 jsonl 不拦截
31+ exclude: \.jsonl$
32+ - id: check-merge-conflict
33+ - id: detect-private-key
34+ - id: check-json
35+ # tsconfig*/settings.json 属 JSONC(允许注释),非严格 JSON
36+ exclude: (^|/)tsconfig(\.[a-z]+)?\.json$|(^|/)\.claude/settings\.json$|(^|/)example_add\.json$
37+ 
38+ # C++ 代码格式化检查
39+ - repo: https://gitcode.com/pre-commit-clang/mirrors-clang-format
40+ rev: v18.1.8
41+ hooks:
42+ - id: clang-format
43+ files: \.(c|h|cpp|hpp|cc|hh|cxx|hxx|asc)$
44+ args:
45+ - "--style=file"
46+ - "--verbose"
47+ - "-i"
48+ # kb/ 为从 CANN 仓提取的知识库快照,重排会与提取源漂移
49+ exclude: (^|/)kb/
50+ 
51+ # Python 代码检查
52+ - repo: https://gitcode.com/gh_mirrors/ru/ruff-pre-commit
53+ rev: v0.14.14
54+ hooks:
55+ - id: ruff-check
56+ args: ["--output-format", "github", "--fix", "--ignore", "E501,RUF001,RUF002,RUF003,E402,E701,E702,N815"]
57+ types: [python]
58+ - id: ruff-format
59+ types: [python]
60+ 
61+ # 拼写检查
62+ - repo: https://gitcode.com/gh_mirrors/co/codespell
63+ rev: v2.4.1
64+ hooks:
65+ - id: codespell
66+ # 包装 entry:输出 tee 到 pre-commit_reports/codespell.log(仍跑在托管 venv)
67+ entry: bash scripts/run_codespell.sh
68+ args: [
69+ "-L",
70+ "CANN,cann,NNAL,nnal,ASCEND,ascend,EnQue,CopyIn,ArchType,AND,ND,nd,te,tbe,copyin,alog,wIh,dout,indexs,inH,padD,outWeight,vor,invokable,VALU,LastR,nin,COO,registe,CLen,REGIST,fo,subtile,rIn,IndexT,ot,iput,datas",
71+ "--skip",
72+ "*.cc,*.cjs,*.clang-format,*.cmake,*.cpp,*.csv,*.cts,*.gitignore,*.gitkeep,*.gitmodules,*.h,*.hpp,*.in,*.info,*.ini,*.js,*.json,*.jsonl,*.log,*.py,*.sh,*.TAG,*.ts,*.tsx,*.txt,*.xml",
73+ ]
74+ # kb/ 为提取快照,不检
75+ exclude: (^|/)kb/
76+ 
77+ # OAT 合规性检查 (Open Source Audit Tool)
78+ - repo: local
79+ hooks:
80+ - id: oat-check
81+ name: OAT Compliance Check
82+ entry: bash scripts/oat_check.sh
83+ language: system
84+ require_serial: true
85+ pass_filenames: true
86+ types: [file]
87+ stages: [pre-commit]
88+ verbose: true
MOAT.xml+45-2
@@ -1,7 +1,7 @@
1<?xml version="1.0" encoding="UTF-8"?>1<?xml version="1.0" encoding="UTF-8"?>
2<!-- Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.2<!-- Copyright (c) 2025-2026 Huawei Technologies Co., Ltd.
3 This program is free software, you can redistribute it and/or modify it under the terms and conditions of3 This program is free software, you can redistribute it and/or modify it under the terms and conditions of
4- CANN Open Software License Agreement Version 2.0 (the License).4+ CANN Open Software License Agreement Version 2.0 (the "License").
5 Please refer to the License for details. You may not use this file except in compliance with the License.5 Please refer to the License for details. You may not use this file except in compliance with the License.
6 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,6 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
7 INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.7 INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
@@ -13,29 +13,72 @@
13 <licensefile></licensefile>13 <licensefile></licensefile>
14 <policylist>14 <policylist>
15 <policy name="projectPolicy" desc="">15 <policy name="projectPolicy" desc="">
16+ <!--policyitem type="compatibility" name="GPL-2.0+" path="abc/.*" desc="Process that runs independently, invoked by the X process."/-->
16 <policyitem type="license" name="CANN-2.0" path=".*" filefilter ="licenseFileNamePolicyFilter" desc=""/>17 <policyitem type="license" name="CANN-2.0" path=".*" filefilter ="licenseFileNamePolicyFilter" desc=""/>
18+ <policyitem type="copyright" name="Huawei Technologies Co., Ltd." path=".*" rule="may" group="defaultGroup" filefilter="copyrightPolicyFilter" desc=""/>
17 </policy>19 </policy>
18 </policylist>20 </policylist>
19 <filefilterlist>21 <filefilterlist>
20 <filefilter name="defaultFilter" desc="Files not to check">22 <filefilter name="defaultFilter" desc="Files not to check">
23+ <filteritem type="filename" name="*.mjs" desc="Workflow runtime ES modules, not distributed, skip OAT entirely"/>
24+ <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
25+ <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
26+ <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/-->
21 </filefilter>27 </filefilter>
22 <filefilter name="defaultPolicyFilter" desc="Filters for compatibility,license header policies">28 <filefilter name="defaultPolicyFilter" desc="Filters for compatibility,license header policies">
29+ <filteritem type="filename" name="*.jsonl" desc="JSON Lines data files, cannot carry license header"/>
30+ <filteritem type="filename" name=".npmignore" desc="npm ignore dotfile, no license header"/>
31+ <filteritem type="filename" name="requirements*.txt" desc="Dependency manifest files, license header not applicable"/>
32+ <filteritem type="filename" name="requirements*.txt" desc="Dependency manifest files, license header not applicable"/>
33+ <filteritem type="filename" name="*.jsonl" desc="JSON Lines data files, cannot carry license header"/>
34+ <filteritem type="filename" name="*.yaml" desc="YAML configuration files, no license header required"/>
35+ <filteritem type="filename" name="*.yml" desc="YAML configuration files, no license header required"/>
36+ <filteritem type="filename" name="*.csv" desc="CSV test case files, no license header required"/>
37+ <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
38+ <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
39+ <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/-->
23 </filefilter>40 </filefilter>
24 <filefilter name="copyrightPolicyFilter" desc="Filters for copyright header policies">41 <filefilter name="copyrightPolicyFilter" desc="Filters for copyright header policies">
42+ <filteritem type="filename" name="*.yaml" desc="YAML configuration files, no copyright header required"/>
43+ <filteritem type="filename" name="*.yml" desc="YAML configuration files, no copyright header required"/>
44+ <filteritem type="filename" name="*.csv" desc="CSV test case files cannot contain a copyright header without breaking their schema"/>
45+ <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
46+ <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
47+ <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/-->
25 </filefilter>48 </filefilter>
26 <filefilter name="licenseFileNamePolicyFilter" desc="Filters for LICENSE file policies">49 <filefilter name="licenseFileNamePolicyFilter" desc="Filters for LICENSE file policies">
50+ <filteritem type="filename" name="*.yaml" desc="YAML configuration files, no license header required"/>
51+ <filteritem type="filename" name="*.yml" desc="YAML configuration files, no license header required"/>
52+ <filteritem type="filename" name="*.csv" desc="CSV test case files, no license header required"/>
53+ <filteritem type="filename" name="*.png" desc="PNG image files for documentation"/>
54+ <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
55+ <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
56+ <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/-->
27 </filefilter>57 </filefilter>
28 <filefilter name="readmeFileNamePolicyFilter" desc="Filters for README file policies">58 <filefilter name="readmeFileNamePolicyFilter" desc="Filters for README file policies">
59+ <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
60+ <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
61+ <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/-->
29 </filefilter>62 </filefilter>
30 <filefilter name="readmeOpenSourcefileNamePolicyFilter" desc="Filters for README.OpenSource file policies">63 <filefilter name="readmeOpenSourcefileNamePolicyFilter" desc="Filters for README.OpenSource file policies">
64+ <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
65+ <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
66+ <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/-->
31 </filefilter>67 </filefilter>
32 <filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies">68 <filefilter name="binaryFileTypePolicyFilter" desc="Filters for binary file policies">
69+ <filteritem type="filename" name="*.png" desc="Documentation images, legitimate binary assets"/>
70+ <filteritem type="filename" name="*.jsonl.gz" desc="Compressed UT fixture session logs, legitimate test data archives"/>
71+ <filteritem type="filename" name=".npmignore" desc="npm ignore dotfile, misdetected as binary"/>
72+ <!--filteritem type="filename" name="*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
73+ <!--filteritem type="filepath" name="abcdefg/.*.uvwxyz" desc="Describe the reason for filtering scan results"/-->
74+ <!--filteritem type="filepath" name="projectroot/[a-zA-Z0-9]{20,}.sh" desc="Temp files"/-->
33 </filefilter>75 </filefilter>
76+ 
34 </filefilterlist>77 </filefilterlist>
35 <licensematcherlist>78 <licensematcherlist>
36 <licensematcher name="CANN-2.0" desc="If the scanning result is InvalidLicense, you can define matching rules here. Note that quotation marks must be escaped.">79 <licensematcher name="CANN-2.0" desc="If the scanning result is InvalidLicense, you can define matching rules here. Note that quotation marks must be escaped.">
37 <licensetext name="80 <licensetext name="
38- This program is free software, you can redistribute it and/or modify it under the terms and conditions of 81+ This program is free software, you can redistribute it and/or modify it under the terms and conditions of
39 CANN Open Software License Agreement Version 2.0 (the &quot;License&quot;).82 CANN Open Software License Agreement Version 2.0 (the &quot;License&quot;).
40 Please refer to the License for details. You may not use this file except in compliance with the License.83 Please refer to the License for details. You may not use this file except in compliance with the License.
41 THIS SOFTWARE IS PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,84 THIS SOFTWARE IS PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
@@ -0,0 +1,147 @@
1+# pre-commit 配置指导书
2+ 
3+## 快速使用
4+ 
5+```bash
6+# 1. 安装 pre-commit
7+pip3 install pre-commit
8+ 
9+# 2. 安装 Git Hooks(此后 git commit 自动触发检查)
10+cd /path/to/cannbot-sentry
11+pre-commit install # 取消:pre-commit uninstall
12+ 
13+# 3. 配置 git pc 别名(对指定范围提交运行检查,每个环境执行一次)
14+git config --global alias.pc '!f() { pre-commit run --files $(git diff --name-only "$@"); }; f'
15+git pc HEAD~x # 检查最近x笔提交
16+```
17+ 
18+完成上述配置后,`git commit` 时将自动执行检查,无需手动干预。完整说明见下文。
19+ 
20+## 一、概述
21+ 
22+pre-commit 是一个 Git Hooks 框架,在 `git commit` 时自动运行代码检查和格式化,提前拦截规范问题,避免远程 CI 门禁失败。
23+ 
24+| Hook | 功能 |
25+|------|------|
26+| **pre-commit-hooks** | 行尾空格、文件末尾换行、YAML/JSON 合法性、大文件、合并冲突标记、私钥检测 |
27+| **clang-format** | C/C++/asc 代码格式化,遵循 `.clang-format` |
28+| **ruff-check / ruff-format** | Python 静态检查(自动修复)+ 格式化 |
29+| **codespell** | 拼写检查(CANN/ascend 等术语已加白名单) |
30+| **OAT Check** | 开源合规检查(许可证头、二进制/归档文件拦截) |
31+ 
32+## 二、环境要求
33+ 
34+- **Git**: 2.0+,**Python**: 3.9+,**pre-commit**: 4.0+
35+ 
36+> clang-format / ruff / codespell 由 pre-commit 首次运行时自动下载(版本见 `.pre-commit-config.yaml`);OAT 首次运行时自动 `pip install oat-py`。均无需手动安装。
37+ 
38+## 三、安装配置
39+ 
40+```bash
41+# 1. 安装 pre-commit
42+pip3 install pre-commit
43+ 
44+# 2. 安装 Git Hooks
45+cd /path/to/cannbot-sentry
46+pre-commit install # 取消:pre-commit uninstall
47+ 
48+# 3. 配置 git pc 别名(对指定范围提交运行检查,每个环境执行一次)
49+git config --global alias.pc '!f() { pre-commit run --files $(git diff --name-only "$@"); }; f'
50+git pc HEAD~x # 检查最近x笔提交
51+```
52+ 
53+## 四、日常使用
54+ 
55+```bash
56+# 提交时自动检查
57+git add . && git commit -m "msg"
58+ 
59+# 手动运行
60+pre-commit run # 暂存区
61+pre-commit run clang-format # 单个hook
62+ 
63+# 检查指定文件
64+pre-commit run --files src/foo.py tests/bar.cpp
65+ 
66+# 检查指定目录(pre-commit 不支持目录参数,需用 find 展开成文件列表)
67+find ops -type f | xargs pre-commit run --files
68+ 
69+# 跳过(紧急情况)
70+git commit --no-verify -m "msg"
71+```
72+ 
73+> ⚠️ 本仓存量文件(数千 md / 上千 py)从未被检查过,hook 只查**暂存文件**属增量生效;**不建议** `pre-commit run --all-files` 全量跑(会翻出大量存量问题),存量债按目录分批用 `find <目录> | xargs pre-commit run --files` 治理。
74+ 
75+## 五、失败排查
76+ 
77+搜索输出中的 `Failed` 定位失败项,查看该 hook 下方输出处理,或粘贴给 AI 获取修复建议。
78+ 
79+拦截类钩子的报告同时落盘在 `pre-commit_reports/`(不入 git):`codespell.log`(拼写命中清单)、`oat_result.txt`(合规违规明细),可随时复查。
80+ 
81+| 报错 | 原因 | 处理方式 |
82+|-----------------------|--------------------|---------------------------------------------------|
83+| clang-format Failed | 代码存在规范问题 | rebase 最新代码后再做 pre-commit;重新 `git add` 后再次 commit |
84+| OAT Compliance Failed | 缺版权声明等 | 搜索 `OAT Scan Result Summary` 定位文件;版权声明无法自动订正,需参照仓内文件头手动补齐 |
85+ 
86+## 六、补查历史提交(--no-verify 跳过后)
87+ 
88+对指定范围的提交变更文件补跑检查:
89+ 
90+```bash
91+git pc HEAD~x # 最近x笔提交
92+git pc <commit>^ # 某一笔提交
93+```
94+ 
95+> 部分钩子会自动修复文件,修复后需重新 `git add` 并追加提交。
96+ 
97+## 七、检查项说明
98+ 
99+1. **pre-commit-hooks** (v4.6.0):trailing-whitespace(md 文件保留行尾双空格硬换行语法)、end-of-file-fixer、check-yaml/json(`tsconfig*` 与 `.claude/settings.json` 等 JSONC 豁免)、check-added-large-files(`*.jsonl` eval 数据集豁免)、check-merge-conflict、detect-private-key(2 个脱敏测试固件豁免)
100+2. **clang-format** (v18.1.8):遵循项目 `.clang-format`(Google 风格,4 空格缩进,120 列限宽,枚举逐行,构造函数初始化列表逐行换行,函数定义大括号换行,指针左对齐 `int* ptr`);`third_party/`、`kb/`(提取快照)、`.html/.csv/.svg` 排除
101+3. **ruff** (v0.14.14):ruff-check(`--fix` 自动修复,豁免 `E501,RUF001-003,E402,E701,E702,N815`——F 系列真 bug 类规则全启用)+ ruff-format
102+4. **codespell** (v2.4.1):拼写检查,**覆盖 markdown**(面向用户的主内容);py/ts/cpp 不查(变量名误报多);CANN、ascend、`AscendC::Te`、nd 格式等术语已加白名单;`kb/` 提取快照不检
103+5. **OAT**:基于 oat-py,检查许可证头(YAML/CSV/PNG/mjs 已豁免;**存量无 header 的 sh 脚本触碰时需补齐**,参照仓内文件头)、禁止二进制和归档文件。注:`tools/opsbot` 等自带 `[tool.ruff]` 的子工程按其文件级配置检查
104+ 
105+## 八、常见问题
106+ 
107+**Q1: 首次提交 OAT 检查很慢?**
108+ 
109+首次运行需 `pip install oat-py`,属正常现象,后续很快。
110+ 
111+**Q2: 手动全量格式化 C/C++?**
112+ 
113+```bash
114+bash scripts/format_cpp.sh # 整个仓库
115+bash scripts/format_cpp.sh ops # 指定目录
116+```
117+ 
118+自动排除 `build/`、`build_out/`、`third_party/`、`.git/`。
119+ 
120+**Q3: 如何全局生效,让新 clone 的仓库自动继承?**
121+ 
122+```bash
123+mkdir -p ~/.git-templates
124+pre-commit init-templatedir ~/.git-templates
125+git config --global init.templatedir ~/.git-templates
126+ 
127+# 已有仓库需重新拷贝模板
128+cd /path/to/cannbot-sentry && git init
129+```
130+ 
131+> hook 只是启动器,实际检查仍读取当前仓库的 `.pre-commit-config.yaml`;未配置该文件的仓库会自动跳过,互不影响。
132+ 
133+取消:`git config --global --unset init.templatedir && rm -rf ~/.git-templates`
134+ 
135+**Q4: 何时需要安装本地 clang-format?**
136+ 
137+仅手动批量格式化(`bash scripts/format_cpp.sh`)时需要,建议 18.x;日常 `git commit` 触发的 pre-commit 不依赖此工具。
138+ 
139+```bash
140+sudo apt install clang-format
141+```
142+ 
143+## 相关文档
144+ 
145+- [pre-commit官方文档](https://pre-commit.com/)
146+- [clang-format配置](https://clang.llvm.org/docs/ClangFormatStyleOptions.html)
147+- [OAT工具](https://gitcode.com/openharmony-sig/tools_oat)
@@ -0,0 +1,68 @@
1+#!/bin/bash
2+# -----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# -----------------------------------------------------------------------------------------------------------
11+ 
12+set -euo pipefail
13+ 
14+SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
15+REPO_ROOT=$(dirname "$SCRIPT_DIR")
16+ 
17+FORMAT_DIR="${1:-${REPO_ROOT}}"
18+if [[ ! -d ${FORMAT_DIR} ]]; then
19+ echo "Error: directory not found: ${FORMAT_DIR}" >&2
20+ exit 1
21+fi
22+ 
23+CLANG_FORMAT="${CLANG_FORMAT:-clang-format}"
24+STYLE_FILE="$REPO_ROOT/.clang-format"
25+ 
26+if [ ! -f "$STYLE_FILE" ]; then
27+ echo "Error: .clang-format not found at $STYLE_FILE" >&2
28+ exit 1
29+fi
30+ 
31+if ! command -v "$CLANG_FORMAT" &>/dev/null; then
32+ echo "Error: $CLANG_FORMAT not found. Install clang-format or set CLANG_FORMAT env var." >&2
33+ exit 1
34+fi
35+ 
36+echo "Using $CLANG_FORMAT ..."
37+"$CLANG_FORMAT" --version
38+ 
39+EXCLUDE_DIRS=(
40+ "build"
41+ "build_out"
42+ "third_party"
43+ ".git"
44+)
45+ 
46+FIND_EXPR=()
47+for dir in "${EXCLUDE_DIRS[@]}"; do
48+ FIND_EXPR+=( -path "$REPO_ROOT/$dir" -prune -o )
49+done
50+ 
51+mapfile -d '' FILES < <(
52+ find "$FORMAT_DIR" "${FIND_EXPR[@]}" \
53+ \( -name '*.cpp' -o -name '*.h' -o -name '*.hpp' -o -name '*.cc' -o -name '*.hh' -o -name '*.cxx' -o -name '*.hxx' -o -name '*.asc' \) \
54+ -type f -print0
55+)
56+ 
57+TOTAL="${#FILES[@]}"
58+echo "Found $TOTAL files to format."
59+ 
60+if [ "$TOTAL" -eq 0 ]; then
61+ echo "No files to format."
62+ exit 0
63+fi
64+ 
65+"$CLANG_FORMAT" -i --style=file --verbose "${FILES[@]}" 2>&1
66+ 
67+echo ""
68+echo "Done! Formatted $TOTAL files."
@@ -0,0 +1,517 @@
1+#!/bin/sh
2+# -----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# -----------------------------------------------------------------------------------------------------------
11+# OAT Pre-commit Check Script — Python Edition (oat-py)
12+# Replaces the Java-binary-based oat_check.sh.
13+#
14+# Requires: Python 3.7+ (pip install oat-py>=1.0.0)
15+# Works on: Linux / macOS / Windows (Git Bash / MSYS2)
16+#
17+# Self-healing: strip Windows CRLF if present
18+_SCRIPT="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
19+if sed 's/\r//' "$_SCRIPT" 2>/dev/null | diff -q - "$_SCRIPT" >/dev/null 2>&1; then
20+ :
21+else
22+ echo "[OAT] CRLF detected, auto-fixing and re-running..."
23+ _TMP="${_SCRIPT}.lf"
24+ sed 's/\r//' "$_SCRIPT" > "$_TMP" && mv "$_TMP" "$_SCRIPT"
25+ exec sh "$_SCRIPT" "$@"
26+fi
27+ 
28+set -e
29+ 
30+# ---------------------------------------------------------------------------
31+# 0. Locate Python interpreter
32+# ---------------------------------------------------------------------------
33+_PYTHON=""
34+for _candidate in python3 python py; do
35+ if command -v "$_candidate" >/dev/null 2>&1; then
36+ _VER=$("$_candidate" -c "import sys; print(sys.version_info >= (3,7))" 2>/dev/null || echo "False")
37+ if [ "$_VER" = "True" ]; then
38+ _PYTHON="$_candidate"
39+ break
40+ fi
41+ fi
42+done
43+ 
44+if [ -z "$_PYTHON" ]; then
45+ echo "[OAT] [WARNING] Python 3.7+ is required but not found. Please install Python 3.7 or later."
46+ echo "[OAT] Skipping OAT check, continuing commit..."
47+ exit 0
48+fi
49+ 
50+# ---------------------------------------------------------------------------
51+# 1. Ensure oat-py is installed (serialized via flock to prevent parallel pip conflicts)
52+# ---------------------------------------------------------------------------
53+_OAT_OK=$("$_PYTHON" -c "import importlib.util; print('ok' if importlib.util.find_spec('oat') else 'missing')" 2>/dev/null || echo "missing")
54+if [ "$_OAT_OK" != "ok" ]; then
55+ echo "[OAT] oat-py not found. Installing oat-py>=1.0.1 ..."
56+ _LOCK_FILE="/tmp/oat_pip_install.lock"
57+ # flock ensures only one concurrent invocation runs pip install at a time;
58+ # re-check inside the lock so processes that waited skip redundant installs
59+ if command -v flock >/dev/null 2>&1; then
60+ (
61+ flock -w 120 9
62+ _RECHECK=$("$_PYTHON" -c "import importlib.util; print('ok' if importlib.util.find_spec('oat') else 'missing')" 2>/dev/null || echo "missing")
63+ if [ "$_RECHECK" != "ok" ]; then
64+ "$_PYTHON" -m pip install --quiet "oat-py>=1.0.1"
65+ fi
66+ ) 9>"$_LOCK_FILE"
67+ else
68+ "$_PYTHON" -m pip install --quiet "oat-py>=1.0.1"
69+ fi
70+ _OAT_OK=$("$_PYTHON" -c "import importlib.util; print('ok' if importlib.util.find_spec('oat') else 'missing')" 2>/dev/null || echo "missing")
71+ if [ "$_OAT_OK" != "ok" ]; then
72+ echo "[OAT] [WARNING] Failed to install oat-py. Please run: pip install oat-py>=1.0.1"
73+ echo "[OAT] Skipping OAT check, continuing commit..."
74+ exit 0
75+ fi
76+ echo "[OAT] oat-py installed successfully."
77+fi
78+ 
79+# ---------------------------------------------------------------------------
80+# 2. Determine repo root and name
81+# ---------------------------------------------------------------------------
82+REPO_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
83+REPO_NAME=$(basename "$REPO_ROOT")
84+OAT_REPORT_DIR="${TMPDIR:-/tmp}/oat_reports_$$"
85+OAT_RESULT_DIR="$REPO_ROOT/pre-commit_reports"
86+ 
87+# ---------------------------------------------------------------------------
88+# 2a. PR-range deduplication (pure git, no CI env vars required)
89+#
90+# Strategy:
91+# 1. Find the merge-base between HEAD and the upstream branch (origin/master
92+# or similar). If found, this is a feature-branch context →collect ALL
93+# files changed since the branch diverged (full PR diff).
94+# 2. Key a done-marker on the HEAD SHA. The first invocation runs the scan;
95+# every subsequent invocation for the same HEAD exits 0 immediately.
96+# This eliminates redundant scans when CI calls the hook once per commit.
97+# 3. If no merge-base is found (e.g. committing directly on master) fall back
98+# to scanning only the currently staged files, with no done-marker.
99+# ---------------------------------------------------------------------------
100+_PR_MERGE_BASE=""
@@ -0,0 +1,15 @@
1+#!/bin/bash
2+# -----------------------------------------------------------------------------------------------------------
3+# Copyright (c) 2026 Huawei Technologies Co., Ltd.
4+# This program is free software, you can redistribute it and/or modify it under the terms and conditions of
5+# CANN Open Software License Agreement Version 2.0 (the "License").
6+# Please refer to the License for details. You may not use this file except in compliance with the License.
7+# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
8+# INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
9+# See LICENSE in the root of the software repository for the full text of the License.
10+# -----------------------------------------------------------------------------------------------------------
11+# codespell 输出留证:终端打印的同时写入 pre-commit_reports/codespell.log
12+REPORT_DIR="pre-commit_reports"
13+mkdir -p "$REPORT_DIR"
14+codespell "$@" 2>&1 | tee "$REPORT_DIR/codespell.log"
15+exit "${PIPESTATUS[0]}"