mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
Report the full quality gate impact
Release quality gates are used as maintainer evidence, so a live baseline failure must not hide later provider or desktop-smoke lanes. Run every lane and summarize all pass/fail statuses so reviewers can tell whether the whole matrix was exercised. Constraint: Live model lanes can fail independently and may be slow, but release evidence needs complete coverage. Rejected: Keep fail-fast behavior | it makes reports ambiguous after the first live failure. Confidence: high Scope-risk: narrow Directive: Do not reintroduce fail-fast behavior for release gates without adding explicit unexecuted-lane reporting. Tested: bun test scripts/quality-gate/runner.test.ts scripts/quality-gate/baseline/cases.test.ts scripts/quality-gate/providerTargets.test.ts Tested: bun run quality:gate --mode release --allow-live --provider-model codingplan:main:codingplan-main --provider-model minimax:main:minimax-main Not-tested: clean-worktree release rerun before this commit; run immediately after committing.
This commit is contained in:
parent
9ed4588db3
commit
6b83ffa4f5
@ -4,8 +4,8 @@ import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { lanesForMode } from './modes'
|
||||
import { renderMarkdownReport } from './reporter'
|
||||
import { runQualityGate } from './runner'
|
||||
import type { QualityGateReport } from './types'
|
||||
import { runQualityGate, runQualityGateLanes } from './runner'
|
||||
import type { LaneDefinition, QualityGateReport } from './types'
|
||||
|
||||
describe('quality gate modes', () => {
|
||||
test('pr mode includes existing path-aware PR checks', () => {
|
||||
@ -70,6 +70,56 @@ describe('runQualityGate', () => {
|
||||
rmSync(artifactsDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('continues through later lanes after a failure so reports show full impact', async () => {
|
||||
const artifactsDir = mkdtempSync(join(tmpdir(), 'quality-gate-test-'))
|
||||
const lanes: LaneDefinition[] = [
|
||||
{
|
||||
id: 'first-lane',
|
||||
title: 'First lane',
|
||||
description: 'Fails intentionally',
|
||||
kind: 'command',
|
||||
command: ['false'],
|
||||
requiredForModes: ['pr'],
|
||||
},
|
||||
{
|
||||
id: 'second-lane',
|
||||
title: 'Second lane',
|
||||
description: 'Still runs',
|
||||
kind: 'command',
|
||||
command: ['true'],
|
||||
requiredForModes: ['pr'],
|
||||
},
|
||||
]
|
||||
|
||||
try {
|
||||
const { report } = await runQualityGateLanes({
|
||||
mode: 'pr',
|
||||
dryRun: false,
|
||||
allowLive: false,
|
||||
baselineTargets: [],
|
||||
rootDir: process.cwd(),
|
||||
artifactsDir,
|
||||
runId: 'continue-after-failure-test',
|
||||
}, lanes, async (lane) => ({
|
||||
id: lane.id,
|
||||
title: lane.title,
|
||||
status: lane.id === 'first-lane' ? 'failed' : 'passed',
|
||||
command: lane.command,
|
||||
durationMs: 1,
|
||||
exitCode: lane.id === 'first-lane' ? 1 : 0,
|
||||
}))
|
||||
|
||||
expect(report.results.map((result) => result.id)).toEqual(['first-lane', 'second-lane'])
|
||||
expect(report.summary).toEqual({
|
||||
passed: 1,
|
||||
failed: 1,
|
||||
skipped: 0,
|
||||
})
|
||||
} finally {
|
||||
rmSync(artifactsDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('renderMarkdownReport', () => {
|
||||
|
||||
@ -7,6 +7,8 @@ import { lanesForMode } from './modes'
|
||||
import { writeReport } from './reporter'
|
||||
import type { LaneDefinition, LaneResult, QualityGateOptions, QualityGateReport } from './types'
|
||||
|
||||
type LaneExecutor = (lane: LaneDefinition, options: QualityGateOptions) => Promise<LaneResult>
|
||||
|
||||
function nowId() {
|
||||
return new Date().toISOString().replace(/[:.]/g, '-')
|
||||
}
|
||||
@ -140,6 +142,14 @@ function summarize(results: LaneResult[]) {
|
||||
}
|
||||
|
||||
export async function runQualityGate(options: QualityGateOptions) {
|
||||
return runQualityGateLanes(options, lanesForMode(options.mode, options.baselineTargets))
|
||||
}
|
||||
|
||||
export async function runQualityGateLanes(
|
||||
options: QualityGateOptions,
|
||||
lanes: LaneDefinition[],
|
||||
executeLane: LaneExecutor = runLane,
|
||||
) {
|
||||
const runId = options.runId ?? nowId()
|
||||
const startedAt = new Date().toISOString()
|
||||
const artifactsRoot = options.artifactsDir ?? join(options.rootDir, 'artifacts', 'quality-runs')
|
||||
@ -148,12 +158,9 @@ export async function runQualityGate(options: QualityGateOptions) {
|
||||
|
||||
const runOptions = { ...options, runId, runOutputDir: outputDir }
|
||||
const results: LaneResult[] = []
|
||||
for (const lane of lanesForMode(options.mode, options.baselineTargets)) {
|
||||
const result = await runLane(lane, runOptions)
|
||||
for (const lane of lanes) {
|
||||
const result = await executeLane(lane, runOptions)
|
||||
results.push(result)
|
||||
if (result.status === 'failed') {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const report: QualityGateReport = {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user