Skip to content

Commit eae86d4

Browse files
committed
feat(java): 默认检查等级不含测试执行(-DskipTests/-x test)+ ruff 基线钉扎
对应 OpenSpec change 2026-09-23-java-gate-default-level(language-gate-commands delta): - java_project.analyze:Maven 默认 argv 追加 -DskipTests(测试代码仍编译, 仅跳过执行);Gradle 默认 argv 追加 -x test——大仓全量测试普遍超门禁 300s 超时预算,跑满只剩 UNVERIFIED,门禁给不出真结论(P2-6 收口) - languages.json java.lint 同步 + docs/LANGUAGES.md 重新生成(可复现) - README(en + zh-CN parity 对齐):默认等级语义、codeguard.json java.commands 升级含测试完整 verify 的路径、ruff 版本基线(CI 钉扎 ruff==0.16.8,项目无自有配置时注入默认规则集——P2-7 收口) 验证:unittest discover 221 通过(3 skipped=ruff 工具缺失守卫); run_all 141/141;ruff 全绿;validate_languages_json 11 规则通过
1 parent 7eb457a commit eae86d4

10 files changed

Lines changed: 97 additions & 13 deletions

File tree

‎README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ codeguard check --lang java /path/to/project
4848

4949
The planner reads Maven POM / Gradle Groovy or Kotlin DSL, prefers project wrappers, maps files to modules and computes reverse transitive dependencies. Changing api can require checking service and app even if their files did not change. Deletions, resources and build descriptors are included.
5050

51-
Maven plans use verify, with -pl and -am for a safe subset. Gradle plans use root check or affected :module:check tasks. Profiles, unresolved properties, inherited dependencies or recognized dynamic/composite Gradle builds expand the plan conservatively. Planning never executes a build, downloads dependencies, installs tools or initializes CodeGraph.
51+
Maven plans use verify with -DskipTests (test code still compiles; only execution is skipped), with -pl and -am for a safe subset. Gradle plans use root check or affected :module:check tasks with -x test. The default level checks compilation, packaging and lifecycle-bound static checks; test execution belongs to CI or an explicit java.commands declaration. Profiles, unresolved properties, inherited dependencies or recognized dynamic/composite Gradle builds expand the plan conservatively. Planning never executes a build, downloads dependencies, installs tools or initializes CodeGraph.
5252

5353
### Explicit project commands
5454

@@ -64,7 +64,7 @@ A root codeguard.json can declare authoritative argv lists:
6464
}
6565
```
6666

67-
Commands run in order, stopping on failure. They are trusted project configuration, not shell strings. Running check or the Git gate may execute project plugins/tests and access package registries; this is **not a sandbox**.
67+
Commands run in order, stopping on failure. They are trusted project configuration, not shell strings. Declaring commands is also how a project opts into a stronger level than the default — for example the full verify including test execution shown above. Running check or the Git gate executes project builds and may run project plugins; the default level skips test execution (-DskipTests / -x test), but configured commands or plugin-bound tasks may run tests, and package registries may be accessed — this is **not a sandbox**.
6868

6969
Coverage is module-level, not a symbol call graph or business-semantic proof. A successful verify/check does not establish that Checkstyle, PMD, SpotBugs or tests are configured comprehensively. Inspect the plan's gaps and reasons.
7070

@@ -117,7 +117,7 @@ Output logs default to <project>/out/.codeguard-last.log; CLI --quiet disables l
117117

118118
Root codeguard.json may set gate_scope to delta or repo and customize extension/exclusion detection. User settings retain enabled_languages, auto_fix_on_save and lint_timeout_seconds. strict_mode is reserved and does not make PostToolUse block. See the [hook protocol](hooks/__protocol__.md).
119119

120-
The registry contains **54 Stable adapters and 3 Planned entries**. “Stable” does not certify every toolchain or project. Markdown/YAML require project configuration; missing configuration is UNVERIFIED. Markdown findings are advisory. Generated and dependency directories are excluded from ordinary lint scope, not automatically accepted for commit. Full command inventory: [languages](docs/LANGUAGES.md).
120+
The registry contains **54 Stable adapters and 3 Planned entries**. “Stable” does not certify every toolchain or project. Markdown/YAML require project configuration; missing configuration is UNVERIFIED. Markdown findings are advisory. Generated and dependency directories are excluded from ordinary lint scope, not automatically accepted for commit. Python checks honor the project's own ruff configuration (ruff.toml / .ruff.toml / [tool.ruff]); when none exists, codeguard injects a default rule set pinned to the CI baseline (ruff==0.16.8) so verdicts do not drift with whichever ruff version a machine happens to have. Full command inventory: [languages](docs/LANGUAGES.md).
121121

122122
The explicit escape hatch git config codeguard.skipGate true bypasses the hook gate and is recorded in session summaries. Shared hook state lives under CODEGUARD_HOME (default ~/.codeguard).
123123

‎README.zh-CN.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ codeguard check --lang java /path/to/project
4848

4949
规划器读取 Maven POM / Gradle Groovy、Kotlin DSL,优先项目 wrapper,将文件映射到模块,再计算反向传递依赖。修改 api 可能要求复查 service 和 app,即使调用方文件没有变化。删除、资源与构建描述变更均纳入分析。
5050

51-
Maven 使用 verify,安全子集增加 -pl、-am;Gradle 使用根 check 或受影响的 :module:check。profiles、未解析属性、父依赖继承或识别到的动态/复合 Gradle 构建会保守扩大范围。规划不会执行构建、下载依赖、安装工具或初始化 CodeGraph。
51+
Maven 使用 verify 并追加 -DskipTests(测试代码仍编译,仅跳过执行),安全子集增加 -pl、-am;Gradle 使用根 check 或受影响的 :module:check 并追加 -x test。默认等级检查编译、打包与生命周期绑定的静态检查;测试执行属于 CI 或显式 java.commands 声明的职责边界。profiles、未解析属性、父依赖继承或识别到的动态/复合 Gradle 构建会保守扩大范围。规划不会执行构建、下载依赖、安装工具或初始化 CodeGraph。
5252

5353
### 项目权威命令
5454

@@ -64,7 +64,7 @@ Maven 使用 verify,安全子集增加 -pl、-am;Gradle 使用根 check 或
6464
}
6565
```
6666

67-
命令顺序执行,失败停止。它们是可信项目配置,不是 shell 字符串。执行 check 或 Git 门禁可能运行项目插件、测试并访问依赖仓库;**这不是沙箱**。
67+
命令顺序执行,失败停止。它们是可信项目配置,不是 shell 字符串。声明命令也是项目升级检查等级的途径——例如上例执行含测试的完整 verify。执行 check 或 Git 门禁会运行项目构建、可能触发项目插件;默认等级跳过测试执行(-DskipTests / -x test),但配置命令或插件绑定任务可能运行测试,并可能访问依赖仓库;**这不是沙箱**。
6868

6969
本轮覆盖模块级,不是符号调用图或业务语义证明。verify/check 成功不代表 Checkstyle、PMD、SpotBugs 或测试配置完整;应查看计划的 gaps 和 reasons。
7070

@@ -117,7 +117,7 @@ python3 scripts/run_check.py --mcp /path/to/project
117117

118118
仓根 codeguard.json 的 gate_scope 可选 delta/repo,也可定制扩展名和排除规则。用户设置保留 enabled_languages、auto_fix_on_save、lint_timeout_seconds。strict_mode 是保留字段,不会令 PostToolUse 阻断,详见[钩子协议](hooks/__protocol__.md)。
119119

120-
注册表含 **54 个 Stable 适配器和 3 个 Planned 项**。“Stable” 不证明全部工具链或项目已验证。Markdown/YAML 需要项目配置,缺配置为 UNVERIFIED;Markdown 违规只告警。生成物和依赖目录从普通 lint 范围排除,不等于允许入库。完整命令见[语言清单](docs/LANGUAGES.md)。
120+
注册表含 **54 个 Stable 适配器和 3 个 Planned 项**。“Stable” 不证明全部工具链或项目已验证。Markdown/YAML 需要项目配置,缺配置为 UNVERIFIED;Markdown 违规只告警。生成物和依赖目录从普通 lint 范围排除,不等于允许入库。Python 检查优先使用项目自有 ruff 配置(ruff.toml / .ruff.toml / [tool.ruff]);项目无自有配置时注入钉扎在 CI 基线(ruff==0.16.8)的默认规则集,判定不随机器上 ruff 版本漂移。完整命令见[语言清单](docs/LANGUAGES.md)。
121121

122122
显式逃生门 git config codeguard.skipGate true 会绕过钩子门禁,并在会话总结中记录。共享状态位于 CODEGUARD_HOME(默认 ~/.codeguard)。
123123

‎docs/LANGUAGES.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
| 语言 | 扩展名 | Lint 命令 | Format 命令 | 安装说明 |
1111
|---|---|---|---|---|
12-
| Java | `.java` | `mvn -B verify` | `mvn -q spotless:apply` | — |
12+
| Java | `.java` | `mvn -B -DskipTests verify` | `mvn -q spotless:apply` | — |
1313
| Rust | `.rs` | `cargo clippy --all-targets -- -D warnings` | `cargo fmt` | — |
1414
| TypeScript / JavaScript | `.ts` `.tsx` `.js` `.jsx` `.mjs` `.cjs` | `npx --no-install eslint . --max-warnings 0` | `npx eslint . --fix` | 项目需安装 eslint |
1515
| Python | `.py` | `ruff check .` | `ruff check . --fix` | pip install ruff |
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# 2026-09-23-java-gate-default-level
2+
3+
## Why
4+
5+
java 门禁当前默认执行完整 `mvn verify`(含测试执行)。实测:easy4j 线上仓
6+
reactor + 全量测试普遍超 300s(lint_timeout_seconds 默认),超时按 timeout
7+
路由归 UNVERIFIED——门禁跑满超时预算也给不出真结论,而 Java 冷缓存
8+
`install -DskipTests` 就普遍超 2 分钟(user_config.py 注释实录)。门禁的
9+
职责是提交面的编译/打包/静态正确性;测试执行是 CI 的职责边界(P2-6 收口)。
10+
11+
P2-7 残余:codeguard 在项目无自有 ruff 配置时注入默认配置,其规则集以特定
12+
ruff 版本为基准,README 未记录基线,机器间判定可能漂移。
13+
14+
## What Changes
15+
16+
- `scripts/java_project.py`:Maven 默认 argv 追加 `-DskipTests`(测试代码仍
17+
编译,仅跳过执行);Gradle 默认 argv 追加 `-x test`(check 任务图剔除
18+
test 任务)。
19+
- `scripts/languages.json`:java.lint 同步为 `mvn -B -DskipTests verify`
20+
(注册表与实际命令一致,docs/LANGUAGES.md 可复现)。
21+
- README:①Java 章节记录默认等级语义与 `codeguard.json java.commands`
22+
升级到含测试完整 verify 的路径;②记录 ruff 版本基线(CI 钉扎
23+
`ruff==0.16.8`,项目无自有 ruff 配置时注入的默认配置以该版本为基准)。
24+
25+
## Impact
26+
27+
- Affected specs: `language-gate-commands`
28+
- 受益场景:多模块/大测试面的 Maven/Gradle 仓(easy4j 全系三分支)——
29+
门禁从「超时 UNVERIFIED」变为真实结论,每提交不再重复执行全量测试。
30+
- 行为变更:默认检查等级不含测试执行;需要测试执行的项目用
31+
`codeguard.json` `java.commands` 显式声明(既有权威覆盖机制)。
32+
- 不改:`java.commands` 权威覆盖语义、模块影响分析(-pl/-am 反向闭包)、
33+
UNVERIFIED 判定契约、wrapper 探测(./mvnw 优先)、静态检查生命周期
34+
绑定的 gaps 说明。
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# language-gate-commands(增量):默认检查等级不含测试执行
2+
3+
## ADDED Requirements
4+
5+
### Requirement: Default build-check level SHALL exclude test execution
6+
7+
java 门禁的默认检查命令 MUST 跳过测试**执行**(Maven 追加 `-DskipTests`、
8+
Gradle 追加 `-x test`)——测试代码仍参与编译,编译错误照常拦截。门禁职责
9+
是提交面的编译/打包/静态正确性;测试执行由 CI 或项目显式声明承担。项目
10+
MUST 能通过 `codeguard.json` 的 `java.commands` 权威覆盖声明含测试执行的
11+
完整 verify。
12+
13+
#### Scenario: Maven default skips test execution
14+
- **WHEN** 项目有 pom.xml 且无 `codeguard.json` `java.commands`
15+
- **THEN** 生成的命令含 `-DskipTests` 且目标为 `verify`(测试编译仍执行)
16+
17+
#### Scenario: Gradle default excludes the test task
18+
- **WHEN** 项目有 Gradle 构建描述且无 `codeguard.json` `java.commands`
19+
- **THEN** 生成的命令含 `-x test`
20+
21+
#### Scenario: Project can opt into full verification
22+
- **WHEN** `codeguard.json` 声明 `java.commands: [["./mvnw", "-B", "verify"]]`
23+
- **THEN** 门禁按声明执行完整 verify(含测试执行),默认等级不再适用
24+
25+
#### Scenario: Test code still must compile
26+
- **WHEN** 本次改动使测试源码编译失败
27+
- **THEN** 默认等级的 `verify` 生命周期编译测试代码,门禁照常 FAIL
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Tasks: 2026-09-23-java-gate-default-level
2+
3+
## 1. 默认等级(门禁 ≠ 测试执行)
4+
5+
- [x] 1.1 `java_project.analyze`:Maven argv 追加 `-DskipTests`(测试代码仍编译)
6+
- [x] 1.2 `java_project.analyze`:Gradle argv 追加 `-x test`
7+
- [x] 1.3 `languages.json` java.lint 同步 + `gen_language_docs` 重新生成
8+
9+
## 2. 文档(P2-6 等级语义 / P2-7 ruff 基线)
10+
11+
- [x] 2.1 README:默认等级语义 + `java.commands` 升级完整 verify 路径
12+
- [x] 2.2 README:ruff 版本基线说明(CI 钉扎 ruff==0.16.8)
13+
14+
## 3. 测试与验证
15+
16+
- [x] 3.1 `test_java_project_impact.py` 断言更新(maven/gradle 默认等级)
17+
- [x] 3.2 unittest discover + run_all + ruff + validate_languages_json 全绿

‎scripts/java_project.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,10 +167,15 @@ def analyze(project_root: str | Path, changed: list[str] | None = None) -> dict:
167167
argv = [executable, "-B"]
168168
if not full and "." not in affected:
169169
argv += ["-pl", ",".join(sorted(affected)), "-am"]
170-
argv += ["verify"]
170+
# 默认等级不含测试执行(-DskipTests 只跳执行,测试代码仍编译):
171+
# 门禁管提交面的编译/打包/静态正确性,测试执行交 CI 或
172+
# codeguard.json java.commands 显式声明——大仓全量测试普遍超
173+
# 门禁超时预算,跑满也只剩 UNVERIFIED,反而给不出结论。
174+
argv += ["-DskipTests", "verify"]
171175
else:
172176
argv = [executable] + (["check"] if full or "." in affected else
173177
[":" + p.replace("/", ":") + ":check" for p in sorted(affected)])
178+
argv += ["-x", "test"]
174179
commands = [{"kind": "verify", "argv": argv}]
175180
config = root / "codeguard.json"
176181
if config.exists():

‎scripts/languages.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"lint": [
2727
"mvn",
2828
"-B",
29+
"-DskipTests",
2930
"verify"
3031
],
3132
"format": [

‎tests/test_java_project_impact.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def test_maven_reverse_transitive_closure_and_wrapper(self):
5353
plan = self.analyze(["api/src/main/java/Api.java"])
5454
self.assertEqual(plan["affected_modules"], ["api", "app", "service"])
5555
self.assertEqual(plan["commands"][0]["argv"],
56-
["./mvnw", "-B", "-pl", "api,app,service", "-am", "verify"])
56+
["./mvnw", "-B", "-pl", "api,app,service", "-am", "-DskipTests", "verify"])
5757
self.assertEqual(plan["status"], "PLANNED")
5858
self.assertIn("静态规则", " ".join(plan["gaps"]))
5959

@@ -94,14 +94,14 @@ def test_gradle_dependencies_use_gradle_not_maven(self):
9494
plan = self.analyze(["api/src/main/java/Api.java"])
9595
self.assertEqual(plan["build_system"], "gradle")
9696
self.assertEqual(plan["affected_modules"], ["api", "service"])
97-
self.assertEqual(plan["commands"][0]["argv"], ["./gradlew", ":api:check", ":service:check"])
97+
self.assertEqual(plan["commands"][0]["argv"], ["./gradlew", ":api:check", ":service:check", "-x", "test"])
9898

9999
def test_dynamic_gradle_uses_full_root_check(self):
100100
self.put("settings.gradle", 'include(moduleNames)')
101101
self.put("build.gradle", 'apply from: "shared.gradle"')
102102
plan = self.analyze(["api/src/main/java/A.java"])
103103
self.assertTrue(plan["conservative"])
104-
self.assertEqual(plan["commands"][0]["argv"], ["gradle", "check"])
104+
self.assertEqual(plan["commands"][0]["argv"], ["gradle", "check", "-x", "test"])
105105
self.assertTrue(plan["reasons"])
106106

107107
def test_missing_or_malformed_build_is_unverified(self):
@@ -134,7 +134,7 @@ def test_check_executes_gradle_plan(self):
134134
import run_per_language
135135
results = run_per_language.run_check(["java"], self.root)
136136
self.assertTrue(results[0]["passed"], results)
137-
self.assertEqual(results[0]["command"], ["./gradlew", "check"])
137+
self.assertEqual(results[0]["command"], ["./gradlew", "check", "-x", "test"])
138138

139139
def test_java_plan_cli_returns_machine_readable_plan(self):
140140
self.pom()

‎tests/test_mcp_server.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def test_mcp_boot_lists_four_tools(self):
9999
result = _read_until(proc, 3)
100100
plan = json.loads(result["result"]["content"][0]["text"])
101101
self.assertEqual(plan["status"], "PLANNED")
102-
self.assertEqual(plan["commands"][0]["argv"], ["mvn", "-B", "verify"])
102+
self.assertEqual(plan["commands"][0]["argv"], ["mvn", "-B", "-DskipTests", "verify"])
103103
self.assertFalse((root / "target").exists())
104104
finally:
105105
try:

0 commit comments

Comments
 (0)