已合并
infra(pre-commit): 引入 pre-commit/OAT 检查体系 #41
景明创建于 21 天前
infra(pre-commit): 引入 pre-commit/OAT 检查体系 #41
已合并
共 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 | ||
| @@ -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 of | 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). | 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 "License"). | 82 | CANN Open Software License Agreement Version 2.0 (the "License"). |
| 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 "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | 84 | THIS SOFTWARE IS PROVIDED ON AN "AS IS" 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]}" | ||