mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 05:02:15 +03:00
* feat(quality): no-new-warnings per PR via native ESLint bulk suppressions Pacote 4 do plano mestre testes+CI (aprovado 2026-07-04). O ratchet de eslintWarnings so rodava no CI pesado (release-PR) -> o drift acumulava invisivel e explodia na release (+41/+37/+88 por ciclo, rebaselinado as cegas — historico no proprio quality-baseline.json). Modelo novo (SonarSource Clean-as-You-Code + ESLint bulk suppressions nativo >=9.24): - config/quality/eslint-suppressions.json congela a divida existente por arquivo+regra: 476 arquivos / 4.273 violacoes. - npm run lint + lint-staged (pre-commit) + novo job lint-guard no quality.yml rodam suppressions-aware: violacao NOVA fica vermelha NO PR que a introduz (bulk suppressions ainda eleva estouros de baseline por arquivo a error). - 3 regras warn promovidas a error em src/** (react-hooks/exhaustive-deps, @next/next/no-img-element, import/no-anonymous-default-export) — divida existente congelada, ocorrencia nova = erro imediato. - collect-metrics mede sob o baseline congelado -> a metrica eslintWarnings vira 'divida liquida nova' (~0 em regime); baseline apertado 4279->0 no mesmo PR (exigencia do require-tighten). Aperto do ESTOQUE congelado: npx eslint . --prune-suppressions na reconciliacao da release. - Principio Zero: lint-guard usa continue-on-error para PR de FORK (report-only; a campanha /green-prs aplica o fix via co-autoria) — bloqueante so para branches internas, a origem real do drift. Validacao: negativo (any novo em tests/) exit 1; negativo (img em src/, regra promovida) exit 1; positivo escopado exit 0; baseline gerado por --suppress-all no tip (tree inteiro passa por construcao); YAML js-yaml ok. * fix(quality): clear the 6 residual warnings so lint-guard runs clean at --max-warnings 0 The committed baseline still let 6 warnings through the lint-guard gate: 5 now-unused inline eslint-disable directives (the file-level suppressions made them redundant — removed via eslint --fix, suppressions regenerated to absorb the re-exposed occurrences) and 1 anonymous default export in tests/load/k6-soak.js (outside the src/** severity-override scope — named the k6 scenario function instead). Verified on the clean tree: lint-guard exit=0; any-canary (new 'const x: any' in open-sse) exit=1 — the gate bites on NEW violations while the 4,273 frozen ones stay suppressed (476 files). * fix(ci): lint-guard continue-on-error must be boolean on non-PR events github.event.pull_request is undefined on workflow_dispatch — the bare property expression made the job fail at plan time (run 28722888456: 4 jobs green, run red, lint-guard never materialized). Guard with event_name check so the expression is always boolean: PR de fork = report-only (Principio Zero), resto = blocking.
120 lines
4.1 KiB
JavaScript
120 lines
4.1 KiB
JavaScript
import nextVitals from "eslint-config-next/core-web-vitals";
|
||
import tseslint from "typescript-eslint";
|
||
|
||
/** @type {import("eslint").Linter.Config[]} */
|
||
const eslintConfig = [
|
||
...nextVitals,
|
||
// Pacote 4 (plano mestre testes+CI, 2026-07-04) — zero-warning policy: TODA regra roda
|
||
// como "error" e a dívida pré-existente vive congelada por arquivo+regra em
|
||
// config/quality/eslint-suppressions.json (ESLint bulk suppressions nativo). Violação
|
||
// NOVA = vermelho no ato (lint-staged no pre-commit + job lint-guard no fast path);
|
||
// o drift de +41/+88 warnings/ciclo que era rebaselinado às cegas na release morre no
|
||
// PR que o introduz. Aperto do baseline: npx eslint . --prune-suppressions
|
||
// --suppressions-location config/quality/eslint-suppressions.json (na release).
|
||
{
|
||
// Escopo = onde os presets do next registram estes plugins (bloco global sem `files`
|
||
// atingiria scripts/*.mjs sem o plugin react-hooks e explodiria o flat config).
|
||
files: ["src/**/*.{ts,tsx,js,jsx}"],
|
||
rules: {
|
||
"react-hooks/exhaustive-deps": "error",
|
||
"@next/next/no-img-element": "error",
|
||
"import/no-anonymous-default-export": "error",
|
||
},
|
||
},
|
||
// FASE-02: Security rules (strict everywhere)
|
||
{
|
||
rules: {
|
||
"no-eval": "error",
|
||
"no-implied-eval": "error",
|
||
"no-new-func": "error",
|
||
"no-restricted-imports": [
|
||
"error",
|
||
{
|
||
paths: [
|
||
{
|
||
name: "prop-types",
|
||
message: "PropTypes are deprecated. Use TypeScript types/interfaces instead.",
|
||
},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
},
|
||
// i18n: ham toLowerCase().includes() arama pattern'ini engelle
|
||
// (Türkçe İ/ı karakterlerini bozar — matchesSearch kullanılmalı).
|
||
// "warn" (error değil): kuralın eklendiği anda kod tabanında zaten bu pattern'i
|
||
// kullanan ~19 satır var; aşamalı temizlik için uyarı seviyesinde tutuluyor
|
||
// (proje politikası: 0 error, warning'ler tolere edilir).
|
||
{
|
||
files: ["src/app/**/*.{ts,tsx}", "src/components/**/*.{ts,tsx}"],
|
||
rules: {
|
||
"no-restricted-syntax": [
|
||
"error",
|
||
{
|
||
selector:
|
||
"CallExpression[callee.property.name='includes'][callee.object.callee.property.name='toLowerCase']",
|
||
message:
|
||
"Türkçe-güvenli arama için matchesSearch() kullan (@/shared/utils/turkishText). Ham toLowerCase().includes() İ/ı karakterlerini bozar.",
|
||
},
|
||
],
|
||
},
|
||
},
|
||
// Relaxed rules for open-sse and tests (incremental adoption)
|
||
{
|
||
files: ["open-sse/**/*.ts", "tests/**/*.mjs", "tests/**/*.ts"],
|
||
plugins: {
|
||
"@typescript-eslint": tseslint.plugin,
|
||
},
|
||
rules: {
|
||
"@typescript-eslint/no-explicit-any": "error",
|
||
"@next/next/no-assign-module-variable": "off",
|
||
"react-hooks/rules-of-hooks": "off",
|
||
},
|
||
},
|
||
// Global ignores — keep ESLint scoped to source files only
|
||
{
|
||
ignores: [
|
||
// Next.js build output (distDir now .build/next; keep .next for legacy)
|
||
".next/**",
|
||
".build/**",
|
||
"src/.next/**",
|
||
"out/**",
|
||
"build/**",
|
||
"dist/**",
|
||
"coverage/**",
|
||
"next-env.d.ts",
|
||
// Scripts and binaries
|
||
"scripts/**",
|
||
"bin/**",
|
||
// Dependencies
|
||
"node_modules/**",
|
||
".worktrees/**",
|
||
// Nested git worktrees created by review/resolve skills live under
|
||
// .claude/ (gitignored). They hold other sessions' in-progress work and
|
||
// their files move mid-scan, so never lint them from the main checkout.
|
||
".claude/**",
|
||
".omnivscodeagent/**",
|
||
// VS Code extension and its large test fixtures
|
||
"vscode-extension/**",
|
||
"_references/**",
|
||
"_mono_repo/**",
|
||
// Electron app
|
||
"electron/**",
|
||
// Docs
|
||
"docs/**",
|
||
// Open-SSE compiled/bundled output
|
||
"open-sse/mcp-server/dist/**",
|
||
// Playwright test output
|
||
"playwright-report/**",
|
||
"test-results/**",
|
||
// Legacy app/ and QA backup dirs (renamed to dist/ in Layer 1)
|
||
"app/**",
|
||
"app.__qa_backup/**",
|
||
// CLI package copy directory
|
||
"clipr/**",
|
||
],
|
||
},
|
||
];
|
||
|
||
export default eslintConfig;
|