feat: 原生 Git hooks 方案,增强版本升迁工作流
This commit is contained in:
167
openspec/specs/git-hooks/spec.md
Normal file
167
openspec/specs/git-hooks/spec.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# git-hooks
|
||||
|
||||
## Purpose
|
||||
|
||||
定义仓库原生 Git hooks 的安装、校验、测试与跨平台执行规则,确保提交前快速检查和提交信息格式校验符合项目规范。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: pre-commit hook 快速检查
|
||||
|
||||
pre-commit hook SHALL 在 `git commit` 执行前对 staged files 进行快速检查,仅检查本次提交涉及的文件。
|
||||
|
||||
#### Scenario: 无 Go 和前端文件变更时跳过
|
||||
|
||||
- **WHEN** staged files 中既无 `.go` 文件也无 `.ts`/`.tsx`/`.scss` 文件
|
||||
- **THEN** pre-commit hook SHALL 直接通过,不运行任何 linter
|
||||
|
||||
#### Scenario: 冲突标记检测
|
||||
|
||||
- **WHEN** staged files 中包含 `<<<<<<<`、`=======` 或 `>>>>>>>` 冲突标记
|
||||
- **THEN** pre-commit hook SHALL 报告错误并列出包含冲突的文件名
|
||||
- **THEN** commit SHALL 被阻止
|
||||
|
||||
#### Scenario: Go 文件 lint 检查
|
||||
|
||||
- **WHEN** staged files 中包含 `.go` 文件
|
||||
- **THEN** pre-commit hook SHALL 对 staged `.go` 文件运行 `golangci-lint run`(复用 `backend/.golangci.yml` 配置)
|
||||
- **THEN** 若 lint 报告任何错误,commit SHALL 被阻止
|
||||
|
||||
#### Scenario: 前端文件 lint 检查
|
||||
|
||||
- **WHEN** staged files 中包含 `.ts` 或 `.tsx` 文件
|
||||
- **THEN** pre-commit hook SHALL 对 staged 前端文件运行 ESLint(复用 `frontend/eslint.config.js` 配置)
|
||||
- **THEN** 若 ESLint 报告任何错误,commit SHALL 被阻止
|
||||
|
||||
#### Scenario: 前端文件格式检查
|
||||
|
||||
- **WHEN** staged files 中包含 `.ts`、`.tsx` 或 `.scss` 文件
|
||||
- **THEN** pre-commit hook SHALL 对 staged 前端文件运行 Prettier 格式检查(复用 `frontend/.prettierrc` 配置)
|
||||
- **THEN** 若存在格式不符合规范的文件,commit SHALL 被阻止
|
||||
|
||||
#### Scenario: 大文件告警
|
||||
|
||||
- **WHEN** staged files 中存在超过 500KB 的文本文件
|
||||
- **THEN** pre-commit hook SHALL 输出警告信息(不阻止提交),提示检查是否误提交
|
||||
|
||||
#### Scenario: commit 被阻止时显示修复提示
|
||||
|
||||
- **WHEN** pre-commit hook 检查失败
|
||||
- **THEN** hook SHALL 输出明确的修复提示(如 `bun run fix`、手动解决冲突标记等)
|
||||
|
||||
### Requirement: commit-msg hook 校验提交信息格式
|
||||
|
||||
commit-msg hook SHALL 在 `git commit` 输入提交信息后校验格式,确保符合项目规范。提交描述 SHALL 使用中文;版本号、英文专有名词可与中文描述混用。
|
||||
|
||||
#### Scenario: 合法格式通过
|
||||
|
||||
- **WHEN** 提交信息首行格式为 `<类型>: <描述>`,类型为 `feat`、`fix`、`refactor`、`docs`、`style`、`test`、`chore` 之一
|
||||
- **THEN** commit-msg hook SHALL 通过,commit 正常执行
|
||||
|
||||
#### Scenario: 非法类型被拒绝
|
||||
|
||||
- **WHEN** 提交信息首行使用的类型不在允许列表中(如 `update: xxx`)
|
||||
- **THEN** commit-msg hook SHALL 报告错误,显示允许的类型列表,commit SHALL 被阻止
|
||||
|
||||
#### Scenario: 英文描述被拒绝
|
||||
|
||||
- **WHEN** 提交信息首行为 `feat: add auth`
|
||||
- **THEN** commit-msg hook SHALL 报告错误,提示提交描述需使用中文
|
||||
- **THEN** commit SHALL 被阻止
|
||||
|
||||
#### Scenario: 缺少冒号空格被拒绝
|
||||
|
||||
- **WHEN** 提交信息首行为 `feat:xxx`(冒号后无空格)或 `feat xxx`
|
||||
- **THEN** commit-msg hook SHALL 报告格式错误,commit SHALL 被阻止
|
||||
|
||||
#### Scenario: 首行过长告警
|
||||
|
||||
- **WHEN** 提交信息首行超过 72 个字符
|
||||
- **THEN** commit-msg hook SHALL 输出警告(不阻止提交),提示首行应简短
|
||||
|
||||
#### Scenario: Merge commit 自动放行
|
||||
|
||||
- **WHEN** 提交信息首行以 `Merge` 开头
|
||||
- **THEN** commit-msg hook SHALL 直接通过,不进行格式校验
|
||||
|
||||
#### Scenario: 格式错误时显示示例
|
||||
|
||||
- **WHEN** commit-msg hook 检查失败
|
||||
- **THEN** hook SHALL 输出包含正确格式示例的错误信息(如 `feat: 添加供应商批量管理功能`)
|
||||
|
||||
### Requirement: hooks-install 安装命令
|
||||
|
||||
`make hooks-install` SHALL 将 `scripts/git-hooks/` 下的 hook 脚本安装到 `.git/hooks/`,不覆盖 Git LFS 管理的 hook。
|
||||
|
||||
#### Scenario: 安装 pre-commit 和 commit-msg
|
||||
|
||||
- **WHEN** 执行 `make hooks-install`
|
||||
- **THEN** `scripts/git-hooks/pre-commit` SHALL 被复制到 `.git/hooks/pre-commit`
|
||||
- **THEN** `scripts/git-hooks/commit-msg` SHALL 被复制到 `.git/hooks/commit-msg`
|
||||
- **THEN** 两个文件 SHALL 被设置为可执行(`chmod +x`)
|
||||
|
||||
#### Scenario: 不覆盖 LFS 管理的 hook
|
||||
|
||||
- **WHEN** `.git/hooks/post-checkout`、`.git/hooks/post-commit`、`.git/hooks/post-merge`、`.git/hooks/pre-push` 已由 Git LFS 管理
|
||||
- **THEN** `make hooks-install` SHALL NOT 覆盖或修改这些文件
|
||||
|
||||
#### Scenario: 重复安装幂等
|
||||
|
||||
- **WHEN** `make hooks-install` 被执行多次
|
||||
- **THEN** hook 文件 SHALL 被正确覆盖更新,不会产生重复或损坏
|
||||
|
||||
#### Scenario: hooks-check 验证安装状态
|
||||
|
||||
- **WHEN** 执行 `make hooks-check`
|
||||
- **THEN** 命令 SHALL 检查 `.git/hooks/pre-commit` 和 `.git/hooks/commit-msg` 是否存在且可执行
|
||||
- **THEN** SHALL 输出每个 hook 的安装状态
|
||||
|
||||
### Requirement: hooks-test 回归测试命令
|
||||
|
||||
`make hooks-test` SHALL 运行仓库内 hook 回归测试,覆盖 commit-msg 格式校验和 pre-commit staged-file 检查,不污染真实 git index。
|
||||
|
||||
#### Scenario: 运行 hook 回归测试
|
||||
|
||||
- **WHEN** 执行 `make hooks-test`
|
||||
- **THEN** SHALL 运行 `scripts/git-hooks/test-hooks.sh`
|
||||
- **THEN** 测试 SHALL 使用临时 `GIT_INDEX_FILE` 构造 staged fixture
|
||||
- **THEN** 若任一 hook 行为不符合预期,命令 SHALL 返回非零退出码
|
||||
|
||||
### Requirement: 跨平台可用
|
||||
|
||||
pre-commit 和 commit-msg hook 脚本 SHALL 可在 macOS 和 Windows(Git Bash)上正常执行。
|
||||
|
||||
#### Scenario: macOS 上正常执行
|
||||
|
||||
- **WHEN** hook 脚本在 macOS 上被 git 调用
|
||||
- **THEN** `#!/bin/sh` shebang SHALL 被系统正确解析
|
||||
- **THEN** `exec make` SHALL 正确调用 Makefile target
|
||||
|
||||
#### Scenario: Windows Git Bash 上正常执行
|
||||
|
||||
- **WHEN** hook 脚本在 Windows 的 Git Bash 环境中被 git 调用
|
||||
- **THEN** Git for Windows 自带的 sh.exe SHALL 正确解析 `#!/bin/sh`
|
||||
- **THEN** `exec make` SHALL 正确调用 Makefile target(依赖 Git Bash/MINGW64 环境中 `make` 可用)
|
||||
- **THEN** Go 和 Bun 工具链 SHALL 通过 PATH 可被 Makefile 调用
|
||||
|
||||
### Requirement: pre-commit 核心逻辑在 Makefile 中复用
|
||||
|
||||
pre-commit hook 的检查逻辑 SHALL 通过 Makefile target 调用项目已有工具链,不重复实现 hook 框架逻辑。commit-msg hook SHALL 在脚本内直接完成格式校验。
|
||||
|
||||
#### Scenario: Go lint 复用后端配置
|
||||
|
||||
- **WHEN** pre-commit 需要检查 Go 文件
|
||||
- **THEN** SHALL 调用 Makefile 逻辑,在 `backend/` 目录对 staged `.go` 文件运行 `go tool golangci-lint run`
|
||||
- **THEN** SHALL 复用 `backend/.golangci.yml` 中的 lint 配置
|
||||
|
||||
#### Scenario: 前端 lint 使用 staged 文件参数
|
||||
|
||||
- **WHEN** pre-commit 需要检查前端文件
|
||||
- **THEN** SHALL 调用 Makefile 逻辑,在 `frontend/` 目录对 staged 前端文件运行 ESLint 和 Prettier 的文件参数模式
|
||||
- **THEN** SHALL NOT 在 pre-commit 阶段运行全量 `bun run check`
|
||||
|
||||
#### Scenario: 终端直接调试
|
||||
|
||||
- **WHEN** 开发者执行 `make _hooks-pre-commit`
|
||||
- **THEN** SHALL 执行与 pre-commit hook 完全相同的检查逻辑
|
||||
- **THEN** 输出 SHALL 与 hook 触发时一致
|
||||
@@ -90,22 +90,34 @@
|
||||
|
||||
### Requirement: 版本升迁 Makefile 编排
|
||||
|
||||
`make version-bump` SHALL 编排完整的版本升迁流程:工作区干净检查 → `version bump`(含 sync/check/倒退检查)→ git add → git commit → git tag。不传 `BUMP` 参数时 SHALL 默认执行 `BUMP=patch`。
|
||||
`make version-bump` SHALL 编排完整的版本升迁流程:全量 lint 检查 → 全量单元测试 → 工作区干净检查 → `version bump`(含 sync/check/倒退检查)→ git add → git commit → git tag。不传 `BUMP` 参数时 SHALL 默认执行 `BUMP=patch`。lint/test 前置检查 SHALL NOT 替代工作区干净检查。
|
||||
|
||||
#### Scenario: 完整升迁流程
|
||||
|
||||
- **WHEN** 执行 `make version-bump BUMP=minor`,工作区干净,当前版本 `0.1.0`
|
||||
- **THEN** Makefile SHALL 依次执行:工作区检查 → `version bump minor` → `git add VERSION frontend/` → `git commit -m "chore: 版本升迁 v0.2.0"` → `git tag v0.2.0`
|
||||
- **THEN** Makefile SHALL 依次执行:`make lint` → `make test` → 工作区检查 → `version bump minor` → `git add VERSION frontend/` → `git commit -m "chore: 版本升迁 v0.2.0"` → `git tag v0.2.0`
|
||||
|
||||
#### Scenario: 不传 BUMP 默认 patch
|
||||
|
||||
- **WHEN** 执行 `make version-bump`,工作区干净,当前版本 `0.1.0`
|
||||
- **THEN** Makefile SHALL 等效于执行 `make version-bump BUMP=patch`,将版本更新为 `0.1.1`
|
||||
|
||||
#### Scenario: lint 失败时终止
|
||||
|
||||
- **WHEN** 执行 `make version-bump`,但 `make lint` 报告错误
|
||||
- **THEN** Makefile SHALL 以非零退出码失败,SHALL NOT 执行 `version bump`、git commit、git tag
|
||||
- **THEN** SHALL 输出错误信息提示修复 lint 问题后重试
|
||||
|
||||
#### Scenario: test 失败时终止
|
||||
|
||||
- **WHEN** 执行 `make version-bump`,但 `make test` 报告测试失败
|
||||
- **THEN** Makefile SHALL 以非零退出码失败,SHALL NOT 执行 `version bump`、git commit、git tag
|
||||
- **THEN** SHALL 输出错误信息提示修复测试失败后重试
|
||||
|
||||
#### Scenario: 工作区不干净
|
||||
|
||||
- **WHEN** 执行 `make version-bump BUMP=minor`,但工作区有未提交的改动
|
||||
- **THEN** Makefile SHALL 以非零退出码失败并提示先提交或暂存改动
|
||||
- **THEN** Makefile SHALL 以非零退出码失败并提示先提交或清理改动
|
||||
|
||||
#### Scenario: 支持指定版本号
|
||||
|
||||
|
||||
Reference in New Issue
Block a user