diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fb28177a3b..a557427c97 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/package.json b/package.json index 8b2dda9b28..4d2c801e06 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/quality-baseline.json b/quality-baseline.json index 347dc1aaf7..96ef62ad83 100644 --- a/quality-baseline.json +++ b/quality-baseline.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'." } diff --git a/scripts/quality/check-quality-ratchet.mjs b/scripts/quality/check-quality-ratchet.mjs index 09142f9d9c..791c194817 100644 --- a/scripts/quality/check-quality-ratchet.mjs +++ b/scripts/quality/check-quality-ratchet.mjs @@ -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"; diff --git a/tests/unit/quality-ratchet.test.ts b/tests/unit/quality-ratchet.test.ts index 6c94c4ddc7..9e37afc56c 100644 --- a/tests/unit/quality-ratchet.test.ts +++ b/tests/unit/quality-ratchet.test.ts @@ -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); +});