mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 03:43:11 +08:00
ci: make the full quality sweep manual instead of nightly
4f9fec876 added this workflow with `cron: '0 18 * * *'`. That was the wrong call
to make unilaterally: the repository had no scheduled workflow at all before it,
so this was not one more cron among several but the introduction of recurring CI
spend — about ninety minutes per run — on a schedule nobody asked for.
The reasoning for the sweep still holds: a per-PR gate only covers what the diff
reaches, so it is blind to checks no recent PR selected and to failures that only
appear when the whole suite runs together. Keeping the workflow on
`workflow_dispatch` keeps that one click away without deciding for the maintainer
when to spend the time.
pr-quality-workflow.test.ts now asserts the absence of `schedule:` and `cron:`
rather than their presence, so a schedule cannot drift back in unnoticed —
verified by adding the cron back and watching the test go red. The docs' four-tier
table renames the tier accordingly; calling it "Nightly" when nothing runs nightly
is exactly the kind of comment that outlives its code.
This commit is contained in:
+1
-1
@@ -5,7 +5,7 @@ These rules apply to `.github/` changes in addition to the root instructions.
|
||||
- Treat workflow changes as product changes. Run `bun run check:policy`; run `actionlint` when available.
|
||||
- `scripts/pr/change-policy.ts` is the source of truth for path-to-check routing. Do not duplicate the routing graph in advisory automation.
|
||||
- Routing is path prefixes plus the import graph from `scripts/pr/module-graph.ts`. Prefixes decide areas and every blocking rule; the graph only widens which surface checks run. When the graph cannot be built the run selects every surface rather than silently falling back to prefixes.
|
||||
- `nightly-quality.yml` runs every deterministic lane unconditionally and re-proves the module graph. It exists because per-PR selection can only ever cover what a diff reaches; do not move its jobs into the required PR gate.
|
||||
- `nightly-quality.yml` runs every deterministic lane unconditionally and re-proves the module graph. It exists because per-PR selection can only ever cover what a diff reaches; do not move its jobs into the required PR gate. It is `workflow_dispatch` only — when to spend ~90 minutes of CI is the maintainer's call, and `pr-quality-workflow.test.ts` fails if a `schedule:` is added back.
|
||||
- Keep `pr-quality-gate` as the stable required status. Selected jobs must succeed and unselected jobs must be explicitly skipped; never convert failures into success.
|
||||
- Required PR jobs must remain offline and must not receive provider credentials or depend on paid/live services.
|
||||
- A `pull_request_target` workflow may inspect PR metadata using trusted base code, but must never execute the PR head, install PR-controlled dependencies, or expose secrets to contributor code.
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
name: Nightly Quality
|
||||
name: Full Quality
|
||||
|
||||
# The PR gate is intentionally scoped: it runs only the surfaces a diff can reach.
|
||||
# That leaves two blind spots no per-PR run can close — regressions that only appear
|
||||
# when the whole suite runs together, and drift in checks no recent PR happened to
|
||||
# select. This workflow closes them on a schedule, off the contributor's critical
|
||||
# path, and still without any model, provider, or repository secret.
|
||||
# select. This workflow closes them, without any model, provider, or repository
|
||||
# secret.
|
||||
#
|
||||
# Manual only, on purpose. It is here so the full sweep is one click away, not so
|
||||
# it runs on its own — deciding when to spend an hour and a half of CI is the
|
||||
# maintainer's call, not this file's.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 18:00 UTC = 02:00 Asia/Shanghai, after the working day.
|
||||
- cron: '0 18 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
skip_coverage:
|
||||
|
||||
@@ -39,12 +39,12 @@ Do not commit local artifacts such as `artifacts/quality-runs/`, `node_modules/`
|
||||
| --- | --- | --- | --- |
|
||||
| Local | manual | The narrowest relevant tests, then whatever `bun run check:impact` selects | seconds |
|
||||
| PR (required) | `pull_request` | The deterministic lanes the impact report selects, including `check:agent-flow` | no model, no provider, no secret, runs on an untrusted fork |
|
||||
| Nightly | `schedule` + manual | Every deterministic lane with no path selection, plus module-graph health and `check:desktop-ui-smoke` | still no model, no secret |
|
||||
| Full sweep | Maintainer-triggered (`workflow_dispatch`) | Every deterministic lane with no path selection, plus module-graph health and `check:desktop-ui-smoke` | still no model, no secret |
|
||||
| Release | maintainer-run `bun run quality:release` (**not** `release-desktop.yml`) | Everything above, plus native/packaging smoke and maintainer-authorized live provider baselines | live models only here, only with explicit authorization |
|
||||
|
||||
Note: `release-desktop.yml` deliberately runs no quality gate — tagging must not be blocked by `bun run verify`, and `scripts/pr/release-workflow.test.ts` guards that decision. Release-time evidence therefore comes from the PRs that were merged, plus nightly, plus the maintainer's manual `quality:release`. That makes nightly load-bearing rather than optional: it is the only recurring check between a merge and a tag.
|
||||
Note: `release-desktop.yml` deliberately runs no quality gate — tagging must not be blocked by `bun run verify`, and `scripts/pr/release-workflow.test.ts` guards that decision. Release-time evidence therefore comes from the PRs that were merged, plus whatever full sweeps the maintainer ran, plus the manual `quality:release`. The full sweep is deliberately not scheduled: spending ~90 minutes of CI is a decision, not a default, and `pr-quality-workflow.test.ts` fails if a `schedule:` is added back.
|
||||
|
||||
The split follows from what each tier can prove. A per-PR gate only ever covers what the diff reaches, so it is structurally blind to checks no recent PR selected and to failures that only appear when the whole suite runs together — nightly closes both. Live model quota is spent only at release time, so every contributor can pass the required gate with no provider at all.
|
||||
The split follows from what each tier can prove. A per-PR gate only ever covers what the diff reaches, so it is structurally blind to checks no recent PR selected and to failures that only appear when the whole suite runs together — the full sweep closes both, when the maintainer asks for it. Live model quota is spent only at release time, so every contributor can pass the required gate with no provider at all.
|
||||
|
||||
## Path-Aware PR Checks
|
||||
|
||||
|
||||
@@ -39,12 +39,12 @@ bun install
|
||||
| --- | --- | --- | --- |
|
||||
| 本地迭代 | 手动 | 最窄的相关测试;`bun run check:impact` 选中的命令 | 秒级反馈 |
|
||||
| PR(必过) | `pull_request` | impact 选中的确定性 lane,含 `check:agent-flow` | 无模型、无 provider、无 secret、fork 可跑 |
|
||||
| Nightly | `schedule` + 手动 | 全部确定性 lane(不做路径选择)+ 模块图健康度 + `check:desktop-ui-smoke` | 仍然无模型、无 secret |
|
||||
| Release | 维护者手动 `bun run quality:release`(**不是** `release-desktop.yml`) | PR + Nightly 全部内容 + native/打包 smoke + 维护者授权的真实 provider baseline | 真实模型只在此层,且需显式授权 |
|
||||
| 全量 | 维护者手动触发(`workflow_dispatch`) | 全部确定性 lane(不做路径选择)+ 模块图健康度 + `check:desktop-ui-smoke` | 仍然无模型、无 secret |
|
||||
| Release | 维护者手动 `bun run quality:release`(**不是** `release-desktop.yml`) | PR + 全量层全部内容 + native/打包 smoke + 维护者授权的真实 provider baseline | 真实模型只在此层,且需显式授权 |
|
||||
|
||||
注意:`release-desktop.yml` 按设计**不跑任何质量门禁**——打 tag 不应被 `bun run verify` 阻塞,`scripts/pr/release-workflow.test.ts` 有守卫测试锁定这一点。因此发版前的质量证据来自「合并进来的那些 PR」+ Nightly + 维护者手动执行的 `quality:release`。这意味着 Nightly 不是可选项:它是 tag 与门禁之间唯一的定期兜底。
|
||||
注意:`release-desktop.yml` 按设计**不跑任何质量门禁**——打 tag 不应被 `bun run verify` 阻塞,`scripts/pr/release-workflow.test.ts` 有守卫测试锁定这一点。因此发版前的质量证据来自「合并进来的那些 PR」+ 维护者手动跑的全量层 + `quality:release`。全量层刻意不设定时:跑不跑、什么时候跑由维护者决定,`pr-quality-workflow.test.ts` 会拦住重新加回 `schedule:` 的改动。
|
||||
|
||||
分层原则:**PR 只跑改动能影响到的范围**,因此它天然无法覆盖"没有 PR 碰过的检查"和"只有全套一起跑才暴露的问题"——这两个盲区交给 Nightly;**真实模型/额度只出现在 Release 与维护者手动 smoke**,任何贡献者在没有 provider 的情况下都必须能跑通 PR 层的全部门禁。
|
||||
分层原则:**PR 只跑改动能影响到的范围**,因此它天然无法覆盖"没有 PR 碰过的检查"和"只有全套一起跑才暴露的问题"——这两个盲区交给手动触发的全量层;**真实模型/额度只出现在 Release 与维护者手动 smoke**,任何贡献者在没有 provider 的情况下都必须能跑通 PR 层的全部门禁。
|
||||
|
||||
## 普通 PR 的影响面检查
|
||||
|
||||
|
||||
@@ -134,14 +134,18 @@ describe('PR quality workflow', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('nightly quality workflow', () => {
|
||||
test('runs every deterministic lane on a schedule without secrets or live providers', () => {
|
||||
describe('full quality workflow', () => {
|
||||
test('runs every deterministic lane on demand without secrets or live providers', () => {
|
||||
const workflow = readFileSync('.github/workflows/nightly-quality.yml', 'utf8')
|
||||
const jobs = workflowJobs(workflow)
|
||||
const runs = (jobs['full-deterministic'].steps ?? []).map((step) => step.run ?? '')
|
||||
|
||||
expect(workflow).toContain('schedule:')
|
||||
expect(workflow).toContain('workflow_dispatch:')
|
||||
// Manual only, and it stays that way. This sweep costs about ninety minutes of
|
||||
// CI; when to spend that is the maintainer's decision, so a schedule must not
|
||||
// reappear here without one.
|
||||
expect(workflow).not.toContain('schedule:')
|
||||
expect(workflow).not.toContain('cron:')
|
||||
for (const command of [
|
||||
'bun run check:policy',
|
||||
'bun run check:agent-flow',
|
||||
@@ -155,7 +159,7 @@ describe('nightly quality workflow', () => {
|
||||
'bun run check:quarantine',
|
||||
'bun run check:coverage',
|
||||
]) {
|
||||
expect(runs, `nightly must run ${command}`).toContain(command)
|
||||
expect(runs, `full sweep must run ${command}`).toContain(command)
|
||||
}
|
||||
|
||||
expect(workflow).not.toContain('--allow-live')
|
||||
|
||||
Reference in New Issue
Block a user