chore(config): frente D — ESLint boundaries + Husky + commitlint + gitleaks
Higiene de PR antes da primeira feature de domínio. - Tags Nx canônicas (scope/type/domain) em todos os 5 projetos, incluindo e2e - depConstraints ESLint: scope:api|web|shared + type:app|e2e|feature|util|data - Husky 9 + lint-staged: eslint --max-warnings=0 + prettier --check em pre-commit - commitlint @conventional: tipo obrigatório, scope enum warn, body ilimitado - gitleaks via Docker: zero leaks no tree completo; allowlist .agents/,.claude/,tmp/ - tmp/ adicionado ao .gitignore (relatórios de scan locais) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,6 +7,7 @@ node_modules
|
|||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
out
|
out
|
||||||
|
tmp
|
||||||
.nx
|
.nx
|
||||||
.next
|
.next
|
||||||
.turbo
|
.turbo
|
||||||
|
|||||||
32
.gitleaks.toml
Normal file
32
.gitleaks.toml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Gitleaks — SAR Força de Vendas
|
||||||
|
# Documentação: https://github.com/gitleaks/gitleaks
|
||||||
|
|
||||||
|
title = "SAR gitleaks config"
|
||||||
|
|
||||||
|
[extend]
|
||||||
|
useDefault = true # herda todas as regras padrão
|
||||||
|
|
||||||
|
[allowlist]
|
||||||
|
description = "Arquivos e padrões seguros conhecidos"
|
||||||
|
|
||||||
|
paths = [
|
||||||
|
# Arquivos de exemplo — contêm placeholders, nunca segredos reais
|
||||||
|
".env.example",
|
||||||
|
".env.test",
|
||||||
|
# Lock files gerados pelo pnpm — nunca contêm segredos
|
||||||
|
"pnpm-lock.yaml",
|
||||||
|
# Ferramentas de agente (BMad skills, Claude config) — docs/templates, não código de produto
|
||||||
|
'''.agents/''',
|
||||||
|
'''.claude/''',
|
||||||
|
# Arquivos temporários / relatórios de CI gerados localmente
|
||||||
|
'''tmp/''',
|
||||||
|
]
|
||||||
|
|
||||||
|
regexes = [
|
||||||
|
# Hashes de commit no design log e docs
|
||||||
|
'''[0-9a-f]{7,40}''',
|
||||||
|
# UUIDs canônicos usados em testes (requestId, workspaceId)
|
||||||
|
'''[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}''',
|
||||||
|
# Valores placeholder explícitos em .env.example
|
||||||
|
'''(your-|change-me|placeholder|CHANGE_ME|YOUR_)''',
|
||||||
|
]
|
||||||
2
.husky/commit-msg
Executable file
2
.husky/commit-msg
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
pnpm exec commitlint --edit "$1"
|
||||||
21
.husky/pre-commit
Executable file
21
.husky/pre-commit
Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# SAR pre-commit: lint-staged → gitleaks
|
||||||
|
|
||||||
|
pnpm exec lint-staged
|
||||||
|
|
||||||
|
# Gitleaks — detecta segredos antes de empurrar pro Gitea.
|
||||||
|
# Roda via Docker para não exigir instalação local.
|
||||||
|
# Fallback silencioso se Docker não estiver disponível (CI tem o binário nativo).
|
||||||
|
if command -v docker > /dev/null 2>&1 && docker info > /dev/null 2>&1; then
|
||||||
|
docker run --rm \
|
||||||
|
-v "$(pwd)":/path \
|
||||||
|
-w /path \
|
||||||
|
zricethezav/gitleaks:latest detect \
|
||||||
|
--config .gitleaks.toml \
|
||||||
|
--source . \
|
||||||
|
--no-git \
|
||||||
|
--redact \
|
||||||
|
--exit-code 1
|
||||||
|
else
|
||||||
|
echo "[pre-commit] Docker indisponível — gitleaks pulado (rode manualmente antes de push)"
|
||||||
|
fi
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
"name": "api-e2e",
|
"name": "api-e2e",
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
|
"tags": ["scope:api", "type:e2e", "domain:shared"],
|
||||||
"implicitDependencies": ["api"],
|
"implicitDependencies": ["api"],
|
||||||
"targets": {
|
"targets": {
|
||||||
"e2e": {
|
"e2e": {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"sourceRoot": "apps/web-e2e/src",
|
"sourceRoot": "apps/web-e2e/src",
|
||||||
"tags": [],
|
"tags": ["scope:web", "type:e2e", "domain:shared"],
|
||||||
"implicitDependencies": ["web"],
|
"implicitDependencies": ["web"],
|
||||||
"// targets": "to see all targets run: nx show project web-e2e --web",
|
"// targets": "to see all targets run: nx show project web-e2e --web",
|
||||||
"targets": {}
|
"targets": {}
|
||||||
|
|||||||
16
commitlint.config.js
Normal file
16
commitlint.config.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Commitlint — Conventional Commits canon JCS SAR.
|
||||||
|
// Tipos extras: docs, perf, ci, revert (além dos convencionais).
|
||||||
|
export default {
|
||||||
|
extends: ['@commitlint/config-conventional'],
|
||||||
|
rules: {
|
||||||
|
// Escopo opcional mas encorajado (api | web | shared | infra | docs)
|
||||||
|
'scope-enum': [
|
||||||
|
1, // warn, não error — novo escopo pode surgir legitimamente
|
||||||
|
'always',
|
||||||
|
['api', 'web', 'shared', 'infra', 'docs', 'e2e', 'config'],
|
||||||
|
],
|
||||||
|
// Corpo pode ter qualquer comprimento (mensagens longas são bem-vindas)
|
||||||
|
'body-max-line-length': [0, 'always', Infinity],
|
||||||
|
'footer-max-line-length': [0, 'always', Infinity],
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -21,10 +21,33 @@ export default [
|
|||||||
enforceBuildableLibDependency: true,
|
enforceBuildableLibDependency: true,
|
||||||
allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'],
|
allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'],
|
||||||
depConstraints: [
|
depConstraints: [
|
||||||
|
// ── scope ────────────────────────────────────────────────────────
|
||||||
|
// api só usa libs api ou shared; web só usa libs web ou shared
|
||||||
|
{ sourceTag: 'scope:api', onlyDependOnLibsWithTags: ['scope:api', 'scope:shared'] },
|
||||||
|
{ sourceTag: 'scope:web', onlyDependOnLibsWithTags: ['scope:web', 'scope:shared'] },
|
||||||
|
// shared não pode importar código de app-scope
|
||||||
|
{ sourceTag: 'scope:shared', onlyDependOnLibsWithTags: ['scope:shared'] },
|
||||||
|
|
||||||
|
// ── type ─────────────────────────────────────────────────────────
|
||||||
|
// apps só dependem de libs (feature/util/data), nunca de outro app
|
||||||
{
|
{
|
||||||
sourceTag: '*',
|
sourceTag: 'type:app',
|
||||||
onlyDependOnLibsWithTags: ['*'],
|
onlyDependOnLibsWithTags: ['type:feature', 'type:util', 'type:data'],
|
||||||
},
|
},
|
||||||
|
// e2e depende do seu app-par e de utils; nunca de outro app
|
||||||
|
{
|
||||||
|
sourceTag: 'type:e2e',
|
||||||
|
onlyDependOnLibsWithTags: ['type:app', 'type:util'],
|
||||||
|
},
|
||||||
|
// features dependem de features, utils e dados — não de apps
|
||||||
|
{
|
||||||
|
sourceTag: 'type:feature',
|
||||||
|
onlyDependOnLibsWithTags: ['type:feature', 'type:util', 'type:data'],
|
||||||
|
},
|
||||||
|
// utils são folha — não importam features nem apps
|
||||||
|
{ sourceTag: 'type:util', onlyDependOnLibsWithTags: ['type:util', 'type:data'] },
|
||||||
|
// data é camada mais baixa — só pode depender de outra camada data
|
||||||
|
{ sourceTag: 'type:data', onlyDependOnLibsWithTags: ['type:data'] },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -23,9 +23,12 @@
|
|||||||
"graph": "nx graph",
|
"graph": "nx graph",
|
||||||
"affected": "nx affected",
|
"affected": "nx affected",
|
||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format:check": "prettier --check ."
|
"format:check": "prettier --check .",
|
||||||
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@commitlint/cli": "^21.0.1",
|
||||||
|
"@commitlint/config-conventional": "^21.0.1",
|
||||||
"@eslint/js": "^9.8.0",
|
"@eslint/js": "^9.8.0",
|
||||||
"@nestjs/schematics": "^11.0.0",
|
"@nestjs/schematics": "^11.0.0",
|
||||||
"@nestjs/testing": "^11.0.0",
|
"@nestjs/testing": "^11.0.0",
|
||||||
@@ -66,12 +69,14 @@
|
|||||||
"eslint-plugin-playwright": "^1.6.2",
|
"eslint-plugin-playwright": "^1.6.2",
|
||||||
"eslint-plugin-react": "7.35.0",
|
"eslint-plugin-react": "7.35.0",
|
||||||
"eslint-plugin-react-hooks": "5.0.0",
|
"eslint-plugin-react-hooks": "5.0.0",
|
||||||
|
"husky": "^9.1.7",
|
||||||
"jest": "~30.3.0",
|
"jest": "~30.3.0",
|
||||||
"jest-environment-node": "~30.3.0",
|
"jest-environment-node": "~30.3.0",
|
||||||
"jest-util": "~30.3.0",
|
"jest-util": "~30.3.0",
|
||||||
"jiti": "2.4.2",
|
"jiti": "2.4.2",
|
||||||
"jsdom": "~22.1.0",
|
"jsdom": "~22.1.0",
|
||||||
"jsonc-eslint-parser": "^2.1.0",
|
"jsonc-eslint-parser": "^2.1.0",
|
||||||
|
"lint-staged": "^17.0.5",
|
||||||
"nx": "^22.7.4",
|
"nx": "^22.7.4",
|
||||||
"prettier": "^3.8.3",
|
"prettier": "^3.8.3",
|
||||||
"ts-jest": "^29.4.0",
|
"ts-jest": "^29.4.0",
|
||||||
@@ -82,6 +87,10 @@
|
|||||||
"vitest": "~4.1.0",
|
"vitest": "~4.1.0",
|
||||||
"webpack-cli": "^5.1.4"
|
"webpack-cli": "^5.1.4"
|
||||||
},
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{ts,tsx,js,jsx,mts,mjs,cts,cjs}": "eslint --max-warnings=0",
|
||||||
|
"*.{ts,tsx,js,jsx,mts,mjs,cts,cjs,json,md,yaml,yml,css}": "prettier --check"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^11.0.0",
|
"@nestjs/common": "^11.0.0",
|
||||||
"@nestjs/config": "^4.0.4",
|
"@nestjs/config": "^4.0.4",
|
||||||
|
|||||||
555
pnpm-lock.yaml
generated
555
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user