1
0
Files
nex/openspec/changes/backend-code-lint/specs/code-lint/spec.md

175 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Code Lint
## Purpose
定义后端 Go 代码静态分析规则,将编码规范从人工约定升级为机器可检查的硬约束,通过 golangci-lint 在开发和提交阶段自动拦截违规代码。
## ADDED Requirements
### Requirement: golangci-lint 配置
系统 SHALL 通过 `.golangci.yml` 配置 golangci-lint启用 13 个 linter。
#### Scenario: 配置文件位置
- **WHEN** 配置 lint 规则
- **THEN** 配置文件 SHALL 位于 `backend/.golangci.yml`
#### Scenario: 启用的 linter 列表
- **WHEN** 运行 golangci-lint
- **THEN** SHALL 启用以下 linterforbidigo、errorlint、errcheck、staticcheck、revive、gocritic、gosec、bodyclose、noctx、nilerr、gofumpt、goimports、gocyclo
### Requirement: forbidigo 日志输出约束
系统 SHALL 通过 forbidigo 禁止在正式代码中使用直接输出函数。
#### Scenario: 禁止 fmt.Print 系列
- **WHEN** 正式代码中调用 fmt.Print、fmt.Println、fmt.Printf
- **THEN** lint SHALL 报错,提示使用 zap logger
#### Scenario: 禁止 fmt.Fprint 到 Stdout/Stderr
- **WHEN** 正式代码中调用 fmt.Fprintf(os.Stdout, ...) 或 fmt.Fprintf(os.Stderr, ...)
- **THEN** lint SHALL 报错,提示使用 zap logger
#### Scenario: 禁止标准库 log
- **WHEN** 正式代码中调用 log.Print、log.Fatal、log.Panic、log.Printf 等
- **THEN** lint SHALL 报错,提示使用 zap logger
#### Scenario: 禁止 zap.L() 全局 logger
- **WHEN** 正式代码中调用 zap.L()
- **THEN** lint SHALL 报错,提示通过 DI 注入 *zap.Logger
#### Scenario: 禁止 zap.S() Sugar logger
- **WHEN** 代码中调用 zap.S()
- **THEN** lint SHALL 报错,不使用 Sugar logger
#### Scenario: 允许 fmt.Sprintf 和 fmt.Errorf
- **WHEN** 代码中使用 fmt.Sprintf 或 fmt.Errorf
- **THEN** lint SHALL NOT 报错
#### Scenario: 测试代码放宽
- **WHEN** 测试文件(*_test.go或 tests/ 目录中使用 fmt.Print 系列
- **THEN** forbidigo SHALL NOT 报错
### Requirement: errorlint 错误比较约束
系统 SHALL 通过 errorlint 强制使用类型安全的错误比较方式。
#### Scenario: 禁止 err == sentinel 比较
- **WHEN** 代码中使用 `err == someError` 直接比较错误
- **THEN** lint SHALL 报错,要求使用 errors.Is()
#### Scenario: 禁止直接类型断言
- **WHEN** 代码中使用 `err.(SomeType)` 直接类型断言
- **THEN** lint SHALL 报错,要求使用 errors.As()
### Requirement: errcheck 错误返回值检查
系统 SHALL 通过 errcheck 禁止忽略函数返回的错误。
#### Scenario: 启用 check-blank
- **WHEN** 代码中使用 `_ = someFuncReturnsError()`
- **THEN** lint SHALL 报错(除非排除列表中的函数)
#### Scenario: 启用 check-type-assertions
- **WHEN** 代码中使用未检查的类型断言 `v := x.(Type)`
- **THEN** lint SHALL 报错
#### Scenario: 排除 fmt.Fprintf
- **WHEN** 代码中忽略 fmt.Fprintf 的返回值
- **THEN** errcheck SHALL NOT 报错io.Writer 场景合理)
#### Scenario: 测试代码放宽
- **WHEN** 测试文件中忽略错误返回值
- **THEN** errcheck 的 check-blank SHALL 放宽
### Requirement: revive 代码风格规则
系统 SHALL 通过 revive 启用精选的 8 条代码风格规则。
#### Scenario: 启用的规则
- **WHEN** 运行 revive
- **THEN** SHALL 启用exported、var-naming、indent-error-flow、error-strings、error-return、blank-imports、context-as-argument、unexported-return
#### Scenario: 测试代码排除 exported
- **WHEN** 测试文件中的导出符号缺少文档注释
- **THEN** revive SHALL NOT 报错
### Requirement: gosec 安全检查
系统 SHALL 通过 gosec 检查常见安全问题。
#### Scenario: 正式代码全部启用
- **WHEN** 正式代码中存在安全隐患硬编码凭证、SQL 注入等)
- **THEN** gosec SHALL 报错
#### Scenario: 测试代码排除部分规则
- **WHEN** 测试文件中触发 G101硬编码密钥、G401/G501弱密码算法
- **THEN** gosec SHALL NOT 报错
### Requirement: gocyclo 圈复杂度控制
系统 SHALL 通过 gocyclo 控制函数复杂度。
#### Scenario: 正式代码复杂度阈值
- **WHEN** 正式代码中函数圈复杂度超过 10
- **THEN** gocyclo SHALL 报错
#### Scenario: 测试代码复杂度阈值
- **WHEN** 测试代码中函数圈复杂度超过 20
- **THEN** gocyclo SHALL 报错
### Requirement: goimports import 排序
系统 SHALL 通过 goimports 统一 import 分组排序。
#### Scenario: 三组格式
- **WHEN** 格式化 import
- **THEN** SHALL 按标准库、第三方库、本地包nex/backend三组排序
- **THEN** local-prefixes SHALL 配置为 nex/backend
### Requirement: 生成代码排除
系统 SHALL 排除自动生成的代码的 lint 检查。
#### Scenario: mocks 目录排除
- **WHEN** lint 扫描 tests/mocks/ 目录
- **THEN** SHALL 排除该目录(由 mockgen 生成的代码)
#### Scenario: Code generated 标记自动检测
- **WHEN** 文件包含 `// Code generated by` 标记
- **THEN** golangci-lint SHALL 自动排除该文件
### Requirement: embedfs 编译兼容
系统 SHALL 确保 golangci-lint 能正常加载 embedfs 模块。
#### Scenario: 空目录占位
- **WHEN** embedfs 模块的 assets/ 和 frontend-dist/ 目录不存在
- **THEN** SHALL 通过 .gitkeep 文件确保目录存在
- **THEN** go:embed 指令 SHALL 能正常匹配