mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 06:42:12 +03:00
feat(quality): Fase 4b — coverage ratchet (conservative floors, CI consumes merged coverage)
- quality-baseline.json: coverage.{statements,lines,functions,branches} floors (80/80/82/73, real ~82.58/82.58/84.23/75.22 with margin; tighten via --update after a green main run)
- check-quality-ratchet.mjs: --allow-missing (local quality:gate skips coverage.* without a coverage run; CI runs strict)
- ci.yml quality-gate job: needs test-coverage + downloads merged coverage-report so the ratchet enforces 'coverage cannot drop'
- TDD +1 test (6 total)
This commit is contained in:
7
.github/workflows/ci.yml
vendored
7
.github/workflows/ci.yml
vendored
@@ -52,6 +52,8 @@ jobs:
|
||||
quality-gate:
|
||||
name: Quality Ratchet
|
||||
runs-on: ubuntu-latest
|
||||
needs: test-coverage
|
||||
if: ${{ always() && needs.test-coverage.result == 'success' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/setup-node@v6
|
||||
@@ -59,6 +61,11 @@ jobs:
|
||||
node-version: ${{ env.CI_NODE_VERSION }}
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
# Coverage mergeada (coverage-summary.json) p/ o ratchet de cobertura.
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: coverage-report
|
||||
path: coverage/
|
||||
- run: npm run quality:collect
|
||||
# Catraca: falha se qualquer métrica regredir vs quality-baseline.json (commitado).
|
||||
# Hoje: contagem de warnings do ESLint. Fase 4 estende com cobertura (lida do
|
||||
|
||||
@@ -121,7 +121,7 @@
|
||||
"check:test-masking": "node scripts/check/check-test-masking.mjs",
|
||||
"quality:collect": "node scripts/quality/collect-metrics.mjs",
|
||||
"quality:ratchet": "node scripts/quality/check-quality-ratchet.mjs",
|
||||
"quality:gate": "npm run quality:collect && npm run quality:ratchet",
|
||||
"quality:gate": "npm run quality:collect && npm run quality:ratchet -- --allow-missing",
|
||||
"audit:deps": "npm audit --audit-level=critical && (npm audit --audit-level=high || echo '::warning::high-severity advisories present (non-blocking)') && npm run audit:electron",
|
||||
"audit:electron": "npm --prefix electron audit --audit-level=moderate",
|
||||
"typecheck:core": "tsc --pretty false -p tsconfig.typecheck-core.json",
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
{
|
||||
"_comment": "Catraca de qualidade. 'down' = nao pode aumentar; 'up' = nao pode cair. Atualize via 'npm run quality:ratchet -- --update' (somente quando melhora). Cada valor e um numero REAL medido, nunca um chute. Cobertura entra na Fase 4 a partir de um run de cobertura mergeada no CI.",
|
||||
"metrics": {
|
||||
"eslintWarnings": { "value": 3482, "direction": "down" }
|
||||
}
|
||||
"eslintWarnings": { "value": 3482, "direction": "down" },
|
||||
"coverage.statements": { "value": 80, "direction": "up" },
|
||||
"coverage.lines": { "value": 80, "direction": "up" },
|
||||
"coverage.functions": { "value": 82, "direction": "up" },
|
||||
"coverage.branches": { "value": 73, "direction": "up" }
|
||||
},
|
||||
"_coverage_note": "Pisos conservadores (real ~82,58/82,58/84,23/75,22 em COVERAGE_PLAN.md 2026-05-13, ~2pt de margem p/ evitar false-fail antes de calibrar no CI). Apos o 1o run verde de coverage mergeada na main, aperte com 'npm run quality:ratchet -- --update'."
|
||||
}
|
||||
|
||||
@@ -14,6 +14,10 @@ const BASELINE = path.resolve(getArg("--baseline", path.join(cwd, "quality-basel
|
||||
const METRICS = path.resolve(getArg("--metrics", path.join(cwd, "quality-metrics.json")));
|
||||
const SUMMARY = getArg("--summary", null);
|
||||
const UPDATE = process.argv.includes("--update");
|
||||
// --allow-missing: pula métricas do baseline ausentes do metrics (em vez de falhar).
|
||||
// Uso local: cobertura só existe no CI; localmente quality:gate roda com este flag.
|
||||
// No CI o job quality-gate roda SEM o flag (estrito — baixa o coverage mergeado antes).
|
||||
const ALLOW_MISSING = process.argv.includes("--allow-missing");
|
||||
const EPS = 0.01;
|
||||
|
||||
function load(p) {
|
||||
@@ -35,8 +39,12 @@ for (const [key, spec] of Object.entries(baseline.metrics)) {
|
||||
const base = spec.value;
|
||||
const dir = spec.direction; // "down" = menor-é-melhor | "up" = maior-é-melhor
|
||||
if (current === undefined) {
|
||||
failures.push(`métrica "${key}" ausente em ${path.basename(METRICS)}`);
|
||||
rows.push([key, base, "—", "MISSING"]);
|
||||
if (ALLOW_MISSING) {
|
||||
rows.push([key, base, "—", "SKIP (ausente)"]);
|
||||
} else {
|
||||
failures.push(`métrica "${key}" ausente em ${path.basename(METRICS)}`);
|
||||
rows.push([key, base, "—", "MISSING"]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
let status = "ok";
|
||||
|
||||
@@ -58,3 +58,13 @@ test("fails when a baseline metric is missing from collected metrics", () => {
|
||||
const b = { metrics: { eslintWarnings: { value: 100, direction: "down" } } };
|
||||
assert.equal(run(b, {}).code, 1);
|
||||
});
|
||||
|
||||
test("--allow-missing skips absent metrics instead of failing", () => {
|
||||
const b = {
|
||||
metrics: {
|
||||
eslintWarnings: { value: 100, direction: "down" },
|
||||
"coverage.lines": { value: 80, direction: "up" },
|
||||
},
|
||||
};
|
||||
assert.equal(run(b, { eslintWarnings: 100 }, ["--allow-missing"]).code, 0);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user