chore(git-hooks): make pre-push checks non-blocking

Replace the local pre-push quality gate with a reminder-only hook, keep manual quality commands documented, and update the contract test to prevent reintroducing a blocking push gate.

Tested: bash .git/hooks/pre-push </dev/null
Tested: bun test scripts/git-hooks/install.test.ts scripts/pr/quality-contract.test.ts
Tested: bun run check:policy
Tested: bun run check:docs
Tested: git diff --check
Confidence: high
Scope-risk: narrow
This commit is contained in:
程序员阿江(Relakkes) 2026-06-03 19:22:14 +08:00
parent e8bc9e9342
commit 12c67f7137
7 changed files with 51 additions and 120 deletions

View File

@ -58,7 +58,7 @@ Use the narrowest meaningful verification while iterating, then run the correct
| Docs, README, release notes, or docs workflow changes | `bun run check:docs` | This runs `npm ci`; run it sequentially, not in parallel with commands that depend on root `node_modules`. |
| Persistence shape changes | `bun run check:persistence-upgrade` | Required for local JSON, `localStorage`, app config migrations, and old-fixture upgrade behavior. |
| Coverage during handoff | `bun run check:coverage` or `bun run verify` | Changed executable production lines must meet the changed-line threshold. |
| Fast local pre-push hook | `bun run quality:push` | Path-aware PR mode with coverage skipped by default; useful for human push latency, not a substitute for PR-ready verification. |
| Optional fast local check | `bun run quality:push` | Path-aware PR mode with coverage skipped by default; run manually when useful, not as a push-time blocker or substitute for PR-ready verification. |
| PR-ready / final agent handoff for code changes | `bun run verify` | Unified local entrypoint: `bun run verify` is equivalent to `bun run quality:pr` and is the default non-live quality gate. |
| Live agent/provider confidence | `bun run quality:providers`, then `bun run quality:smoke --provider-model <provider:model[:label]>` | Quick live provider/proxy and desktop agent-browser smoke when provider access exists. |
| High-risk pre-merge confidence | `bun run quality:gate --mode baseline --allow-live --provider-model <provider:model[:label]>` | Use for agent-loop, provider routing, model selection, tool execution, session resume, desktop chat, or other core Coding Agent paths. |

View File

@ -10,7 +10,7 @@ bun run verify
`bun run verify` 会一键检查 policy、desktop、server、adapters、native、docs、quarantine 和 coverage。非 0 退出就说明当前分支还不能提交 PR。
已安装本地 hook 的仓库在 `git push` 前会自动运行快速门禁 `bun run quality:push`;它复用 PR 门禁但默认跳过耗时的 coverage lane完整覆盖率仍以 `bun run verify` 为准。
`git push` 不再自动运行本地质量门禁。需要质量检查时请手动运行 `bun run quality:push``bun run verify`完整覆盖率仍以 `bun run verify` 为准。
只改了某个模块时可以用窄命令快速迭代:

View File

@ -89,39 +89,35 @@ Every feature, bugfix, and behavior change must ship with verifiable evidence. T
- Do not lower `coverage-baseline.json` or `coverage-thresholds.json` just to pass the gate; real baseline/threshold changes require `allow-coverage-baseline-change` and a reason. Legacy low-coverage areas are debt; new PRs must leave touched areas better than they found them.
- The PR description must record changed files, tests added, coverage report path, E2E/live report path or blocker, and remaining risk.
## Local Pre-Push Gate
## Local Pre-Push Reminder
Git hooks are local, so each clone needs to install the hook once:
push no longer runs a local quality gate. Run checks manually when needed:
```bash
bun run quality:push
```
`bun run quality:push` reuses the PR gate impact, policy, and path-aware checks, but skips the expensive coverage lane by default; full coverage remains in `bun run verify`, `bun run quality:pr`, and CI.
You can still install the local pre-push hook, but it only prints a non-blocking reminder and never blocks `git push`:
```bash
bun run hooks:install
```
After installation, every `git push` runs the fast local gate internally (`bun run quality:push`). It reuses the PR gate impact, policy, and path-aware checks, but skips the expensive coverage lane by default; full coverage remains in `bun run verify`, `bun run quality:pr`, and CI. If unit tests, docs/native/adapter checks, or any other selected fast path-aware lane fails, the local hook blocks the push.
Maintainers or contributors with model quota can also add real provider smoke and desktop agent-browser smoke to the pre-push hook:
Maintainers or contributors with model quota can run real provider smoke and desktop agent-browser smoke manually:
```bash
bun run quality:providers
bun run hooks:install -- --live-provider-model minimax:main:minimax-main
bun run quality:smoke -- --provider-model minimax:main:minimax-main
```
To run the full live baseline before every push, use:
To run the full live baseline, use:
```bash
bun run hooks:install -- --live-provider-model minimax:main:minimax-main --live-mode baseline
bun run quality:gate --mode baseline --allow-live --provider-model minimax:main:minimax-main
```
These options are stored in local `.git/config` as `quality.prePush*` keys, so provider selectors and secrets are not committed. `smoke` mode covers real provider connectivity plus the desktop UI chat smoke; `baseline` mode also runs every real Coding Agent baseline case.
Maintainer-level overrides must also be explicit local config before the hook passes them to `quality:push`:
```bash
bun run hooks:install -- --allow-cli-core-change --allow-coverage-baseline-change
```
This only affects the current clone and is not committed; PR CI still requires the matching labels.
## PR CI Merge Gate
`.github/workflows/pr-quality.yml` runs for PR `opened`, `synchronize`, `reopened`, `ready_for_review`, `labeled`, and `unlabeled` events. It starts with `change-policy`, which maps changed files to the desktop, server, adapter, native, docs, and coverage lanes. The final `pr-quality-gate` job aggregates every selected job: failed selected jobs fail `pr-quality-gate`, while unselected jobs may be skipped.

View File

@ -89,39 +89,35 @@ Agent 应按这个顺序处理失败:
- 不要为了过门禁随便降低 `coverage-baseline.json``coverage-thresholds.json`;确实要改时必须有 `allow-coverage-baseline-change` 和原因。历史低覆盖区域是技术债,新 PR 至少要让触达区域更好。
- PR 描述必须写清楚改了哪些文件、补了哪些测试、coverage 报告路径、E2E/live 报告路径或 blocker、剩余风险。
## 本机 Push 前门禁
## 本机 Push 前提醒
Git hook 不能提交到远端自动生效,所以每个 clone 需要安装一次:
push 不再自动运行本地质量门禁。需要质量检查时,请手动运行:
```bash
bun run quality:push
```
`bun run quality:push` 复用 PR gate 的 impact/policy/路径检查,但默认跳过耗时的 coverage lane完整覆盖率仍保留在 `bun run verify``bun run quality:pr` 和 CI。
仍然可以安装本机 pre-push hook但它只打印非阻塞提醒不会卡住 `git push`
```bash
bun run hooks:install
```
安装后,每次 `git push` 都会先运行快速本地门禁(内部是 `bun run quality:push`)。它复用 PR gate 的 impact/policy/路径检查,但默认跳过耗时的 coverage lane完整覆盖率仍保留在 `bun run verify``bun run quality:pr` 和 CI。如果单元测试、文档/native/adapter 等按路径选中的快速门禁失败push 会被本机 hook 阻断。
维护者或有模型额度的贡献者可以把真实 provider smoke 和桌面 agent-browser smoke 也纳入 push 前门禁:
维护者或有模型额度的贡献者可以手动运行真实 provider smoke 和桌面 agent-browser smoke
```bash
bun run quality:providers
bun run hooks:install -- --live-provider-model minimax:main:minimax-main
bun run quality:smoke -- --provider-model minimax:main:minimax-main
```
需要每次 push 前跑完整 live baseline 时使用:
需要完整 live baseline 时使用:
```bash
bun run hooks:install -- --live-provider-model minimax:main:minimax-main --live-mode baseline
bun run quality:gate --mode baseline --allow-live --provider-model minimax:main:minimax-main
```
这些选项写入本机 `.git/config``quality.prePush*` 配置,不会提交 provider selector 或密钥。`smoke` 模式覆盖真实 provider 连通性和桌面 UI 聊天 smoke`baseline` 模式会额外跑全部真实 Coding Agent baseline case。
维护者级 override 也必须显式写入本机配置hook 才会传给 `quality:push`
```bash
bun run hooks:install -- --allow-cli-core-change --allow-coverage-baseline-change
```
这只影响当前 clone不会提交到仓库PR 侧仍然需要对应 label。
## PR CI 合并门禁
`.github/workflows/pr-quality.yml` 会在 PR `opened``synchronize``reopened``ready_for_review``labeled``unlabeled` 时触发。流程先运行 `change-policy`,根据变更文件决定是否需要 desktop、server、adapter、native、docs 和 coverage lane。最后的 `pr-quality-gate` 会汇总所有被选中的 job选中 job 失败则 `pr-quality-gate` 失败,未选中的 job 可以是 skipped。

View File

@ -160,7 +160,7 @@ function parseArgs(argv: string[]): ParsedArgs {
}
function printHelp() {
console.log(`Install the repository pre-push quality gate.
console.log(`Install the repository non-blocking pre-push reminder.
Usage:
bun run hooks:install [-- --force] [-- --no-live] [-- --live-provider-model <selector>] [-- --live-mode smoke|baseline]
@ -169,10 +169,10 @@ Usage:
Examples:
bun run hooks:install
bun run hooks:install -- --no-live
bun run quality:providers
bun run hooks:install -- --live-provider-model codingplan:main:codingplan-main
bun run hooks:install -- --live-provider-model codingplan:main:codingplan-main --live-mode baseline
bun run hooks:install -- --allow-cli-core-change --allow-coverage-baseline-change
The installed hook never blocks git push. Run quality gates manually with
bun run quality:push, bun run verify, or the relevant narrow check.
`)
}
@ -195,20 +195,20 @@ if (import.meta.main) {
live: args.live,
})
console.log(`Installed pre-push quality gate: ${result.hookPath}`)
console.log('Every git push now runs: bun run quality:push')
console.log('Coverage remains in bun run verify, quality:pr, and CI.')
console.log(`Installed non-blocking pre-push reminder: ${result.hookPath}`)
console.log('git push no longer runs local quality gates.')
console.log('Run checks manually with bun run quality:push, bun run verify, or the relevant narrow check.')
if (args.liveProviderModels.length > 0) {
console.log(`Live ${args.liveMode ?? 'smoke'} gate is enabled for ${args.liveProviderModels.length} provider selector(s).`)
console.log(`Legacy live ${args.liveMode ?? 'smoke'} selector option was accepted, but pre-push is non-blocking and will not run it.`)
} else if (args.live === false) {
console.log('Live model gate is disabled in local git config.')
console.log('Legacy live model gate config is disabled in local git config.')
} else {
console.log('Live model gate is disabled. Enable it with --live-provider-model after running bun run quality:providers.')
console.log('Live model gates remain manual; use bun run quality:providers and bun run quality:smoke when needed.')
}
if (args.allowCliCoreChange || args.allowCoverageBaselineChange || args.allowMissingTests) {
console.log('Local maintainer override config was updated for this clone.')
console.log('Legacy local maintainer override config was updated for this clone.')
}
} catch (error) {
console.error(error instanceof Error ? error.message : String(error))

View File

@ -1,73 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
git_config() {
git config --local --get "$1" 2>/dev/null || true
}
echo "[pre-push] non-blocking: git push no longer runs local quality gates."
echo "[pre-push] Run manually before PR/release work: bun run quality:push or bun run verify"
is_enabled() {
case "${1:-}" in
1|true|TRUE|yes|YES|on|ON)
return 0
;;
*)
return 1
;;
esac
}
echo "[pre-push] Running fast pre-push quality gate: bun run quality:push"
if is_enabled "$(git_config quality.allowCliCoreChange)"; then
export ALLOW_CLI_CORE_CHANGE=1
fi
if is_enabled "$(git_config quality.allowMissingTests)"; then
export ALLOW_MISSING_TESTS=1
fi
if is_enabled "$(git_config quality.allowCoverageBaselineChange)"; then
export ALLOW_COVERAGE_BASELINE_CHANGE=1
fi
bun run quality:push
live="${QUALITY_PRE_PUSH_LIVE:-$(git_config quality.prePushLive)}"
if ! is_enabled "$live"; then
echo "[pre-push] Live model smoke is disabled. Configure it with: bun run hooks:install -- --live-provider-model <selector>"
exit 0
fi
provider_models="${QUALITY_PRE_PUSH_PROVIDER_MODELS:-${QUALITY_PRE_PUSH_PROVIDER_MODEL:-$(git_config quality.prePushProviderModels)}}"
if [[ -z "${provider_models// }" ]]; then
echo "[pre-push] Live model smoke is enabled, but no provider selector is configured."
echo "[pre-push] Run: bun run quality:providers"
echo "[pre-push] Then: bun run hooks:install -- --live-provider-model <selector>"
exit 1
fi
live_mode="${QUALITY_PRE_PUSH_LIVE_MODE:-$(git_config quality.prePushLiveMode)}"
live_mode="${live_mode:-smoke}"
case "$live_mode" in
smoke)
cmd=(bun run quality:smoke)
;;
baseline)
cmd=(bun run quality:gate --mode baseline --allow-live)
;;
*)
echo "[pre-push] Unsupported live mode: $live_mode"
echo "[pre-push] Use smoke or baseline."
exit 1
;;
esac
for selector in $provider_models; do
cmd+=(--provider-model "$selector")
done
echo "[pre-push] Running live $live_mode gate with configured provider selector(s)."
"${cmd[@]}"
exit 0

View File

@ -48,16 +48,21 @@ describe('feature quality contract', () => {
expect(packageJson.scripts?.['check:native']).toContain('electron:package:dir')
expect(packageJson.scripts?.['check:native']).toContain('test:package-smoke:current')
expect(packageJson.scripts?.['test:package-smoke:current']).toBe('bun run scripts/quality-gate/package-smoke/current.ts')
expect(prePushHook).toContain('bun run quality:push')
expect(prePushHook).not.toContain('\nbun run quality:pr\n')
expect(prePushHook).toContain('non-blocking')
expect(prePushHook).not.toContain('\nbun run quality:push\n')
expect(prePushHook).not.toContain('quality:gate')
expect(prePushHook).not.toContain('quality:smoke')
expect(contributing).toContain('bun run verify')
expect(contributing).toContain('bun run quality:push')
expect(contributing).toContain('push 不再自动运行本地质量门禁')
expect(contributing).toContain('AI Coding Agent 修复循环')
expect(englishContributing).toContain('bun run verify')
expect(englishContributing).toContain('bun run quality:push')
expect(englishContributing).toContain('push no longer runs a local quality gate')
expect(englishContributing).toContain('AI Coding Agent Fix Loop')
expect(rootContributing).toContain('bun run verify')
expect(rootContributing).toContain('bun run quality:push')
expect(rootContributing).toContain('不再自动运行本地质量门禁')
})
test('keeps desktop native CI aligned with Electron packaging', () => {