agent-master-devsecops 3.9.0 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +205 -371
- package/agents/AGENT_DEVSECOPS.md +32 -417
- package/agents/AGENT_INFRA.md +31 -0
- package/agents/AGENT_ORCHESTRATOR.md +39 -264
- package/agents/AGENT_QA.md +58 -186
- package/agents/AGENT_SCRUM.md +23 -190
- package/agents/AGENT_SRE.md +22 -173
- package/agents/AGENT_UIUX.md +26 -162
- package/docs/ARCHITECTURE.md +152 -148
- package/docs/CALCULATIONS.md +17 -17
- package/docs/CHANGELOG.md +112 -49
- package/docs/ENGINEERING_RULES.md +56 -252
- package/docs/IDEAS.md +17 -17
- package/docs/LOCAL_BACKLOG.md +20 -0
- package/docs/STATE.md +111 -43
- package/docs/playbooks/PLAYBOOK_CIRCUIT_BREAKER_LLM.md +58 -58
- package/docs/playbooks/PLAYBOOK_OPENTELEMETRY_TRACING.md +58 -58
- package/package.json +59 -47
- package/scripts/auditors/a11y_auditor.js +462 -0
- package/scripts/auditors/code_quality_auditor.js +444 -0
- package/scripts/auditors/contract_test_validator.js +396 -0
- package/scripts/auditors/eval_harness.js +213 -0
- package/scripts/auditors/git_hygiene_linter.js +391 -0
- package/scripts/auditors/pr_reviewer.js +627 -0
- package/scripts/auditors/sast_scanner.js +442 -0
- package/scripts/auditors/sca_dependency_auditor.js +673 -0
- package/scripts/{bootstrap_cli.js → bin/bootstrap_cli.js} +597 -468
- package/scripts/bin/domain_idea_miner.js +658 -0
- package/scripts/bin/smart_onboarding.js +905 -0
- package/scripts/{web_inspector.js → bin/web_inspector.js} +578 -355
- package/scripts/core/architecture_advisor.js +378 -0
- package/scripts/core/ast_skeletonizer.js +413 -0
- package/scripts/core/autonomous_remediation_engine.js +305 -0
- package/scripts/{circuit_breaker.js → core/circuit_breaker.js} +546 -330
- package/scripts/core/context_compressor.js +237 -0
- package/scripts/core/fast_feature_scaffold.js +840 -0
- package/scripts/core/hot_state_cache.js +238 -0
- package/scripts/core/knowledge_gateway.js +888 -0
- package/scripts/{magic_prompt_generator.js → core/magic_prompt_generator.js} +250 -205
- package/scripts/{multi_agent_orchestrator.js → core/multi_agent_orchestrator.js} +421 -258
- package/scripts/{otel_tracer.js → core/otel_tracer.js} +335 -228
- package/scripts/{perf_benchmark.js → core/perf_benchmark.js} +212 -153
- package/scripts/core/sarif_engine.js +299 -0
- package/scripts/{sre_remediation_engine.js → core/sre_remediation_engine.js} +236 -174
- package/scripts/{universal_packager.js → core/universal_packager.js} +357 -313
- package/scripts/integrations/atlassian_client.js +600 -0
- package/scripts/integrations/atlassian_sync_action.js +576 -0
- package/scripts/integrations/confluence_sync.js +684 -0
- package/scripts/integrations/credentials_guard.js +500 -0
- package/scripts/integrations/jira_manager.js +722 -0
- package/scripts/mcp/handlers/atlassian_handlers.js +300 -0
- package/scripts/mcp/handlers/core_handlers.js +419 -0
- package/scripts/mcp/handlers/qa_handlers.js +502 -0
- package/scripts/mcp/handlers/rules_handlers.js +379 -0
- package/scripts/mcp/handlers/scaffold_handlers.js +334 -0
- package/scripts/mcp/handlers/sre_handlers.js +443 -0
- package/scripts/mcp/resources_registry.js +453 -0
- package/scripts/mcp/server.js +408 -0
- package/scripts/mcp/tool_handlers.js +444 -0
- package/scripts/mcp/tools_registry.js +1123 -0
- package/scripts/mcp_server.js +236 -1884
- package/.cursorignore +0 -23
- package/agents/compact/AGENT_ORCHESTRATOR_COMPACTO.md +0 -53
- package/scripts/a11y_auditor.js +0 -187
- package/scripts/ast_skeletonizer.js +0 -220
- package/scripts/atlassian_client.js +0 -238
- package/scripts/atlassian_sync_action.js +0 -269
- package/scripts/code_quality_auditor.js +0 -142
- package/scripts/confluence_sync.js +0 -337
- package/scripts/context_compressor.js +0 -139
- package/scripts/contract_test_validator.js +0 -166
- package/scripts/credentials_guard.js +0 -216
- package/scripts/domain_idea_miner.js +0 -364
- package/scripts/eval_harness.js +0 -100
- package/scripts/fast_feature_scaffold.js +0 -338
- package/scripts/git_hygiene_linter.js +0 -186
- package/scripts/hot_state_cache.js +0 -95
- package/scripts/jira_manager.js +0 -408
- package/scripts/pr_reviewer.js +0 -373
- package/scripts/sast_scanner.js +0 -180
- package/scripts/sca_dependency_auditor.js +0 -167
- package/scripts/smart_onboarding.js +0 -610
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/auditors/git_hygiene_linter.js
|
|
3
|
+
* Auditor Local de Commits & Branches — Regra 11 de Engenharia & Princípio 3 (v5.1 Enterprise)
|
|
4
|
+
*
|
|
5
|
+
* Implementa validação estrita de nomenclatura de branches e sintaxe de commits:
|
|
6
|
+
* - Commit Canônico: [<TICKET>] - <Ação em português>
|
|
7
|
+
* - Proibição total de Conventional Commits (feat:, fix:, chore:, etc.)
|
|
8
|
+
* - Proibição de prefixos legados em branches (feature/**, fix/**, hotfix/**)
|
|
9
|
+
* - Emissão de relatórios oficiais no padrão OASIS SARIF v2.1.0 (auditToSarif)
|
|
10
|
+
* - Instalação de hooks locais nativos (.git/hooks/commit-msg) sem dependências externas.
|
|
11
|
+
*/
|
|
12
|
+
'use strict';
|
|
13
|
+
|
|
14
|
+
const fs = require('node:fs');
|
|
15
|
+
const path = require('node:path');
|
|
16
|
+
const { execFileSync } = require('node:child_process');
|
|
17
|
+
const { createSarifReport } = require('../core/sarif_engine.js');
|
|
18
|
+
|
|
19
|
+
const TICKET_REGEX = /^[A-Z][A-Z0-9_]+-\d+$/;
|
|
20
|
+
const BRANCH_CANONICAL_REGEX = /^([A-Z][A-Z0-9_]+-\d+)(?:-[a-z0-9-]+)?$/;
|
|
21
|
+
const COMMIT_REGEX = /^\[([A-Z][A-Z0-9_]+-\d+|GOV|CD|SETUP|RELEASE|INIT)\]\s*-\s*(.+)$/;
|
|
22
|
+
const FORBIDDEN_COMMIT_PREFIXES = /^(feat|fix|chore|docs|style|refactor|perf|test|build|ci|wip)(\(.*\))?:\s*/i;
|
|
23
|
+
const FORBIDDEN_BRANCH_PREFIXES = /^(feature|feat|fix|bugfix|hotfix|chore|refactor)\//i;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Classe principal do Auditor de Higiene de Git e Rastreabilidade.
|
|
27
|
+
*/
|
|
28
|
+
class GitHygieneLinter {
|
|
29
|
+
constructor(options = {}) {
|
|
30
|
+
this.options = options;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Valida sintaxe da mensagem de commit contra a Regra 11 e Princípio 3.
|
|
35
|
+
*
|
|
36
|
+
* @param {string} message
|
|
37
|
+
* @returns {{ valid: boolean, ticket?: string, action?: string, cleanMessage?: string, reason?: string }}
|
|
38
|
+
*/
|
|
39
|
+
lintCommitMessage(message) {
|
|
40
|
+
if (!message || typeof message !== 'string') {
|
|
41
|
+
return { valid: false, reason: 'Mensagem de commit vazia ou indefinida.' };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const cleanMsg = message.trim().split('\n')[0].trim();
|
|
45
|
+
if (!cleanMsg) {
|
|
46
|
+
return { valid: false, reason: 'Mensagem de commit vazia ou em branco.' };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 0. Permitir commits de Merge/Revert gerados pelo Git/GitHub
|
|
50
|
+
if (/^(Merge\b|Revert\b)/i.test(cleanMsg)) {
|
|
51
|
+
return { valid: true, ticket: 'MERGE', cleanMessage: cleanMsg };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 1. Proibir Conventional Commits
|
|
55
|
+
if (FORBIDDEN_COMMIT_PREFIXES.test(cleanMsg)) {
|
|
56
|
+
return {
|
|
57
|
+
valid: false,
|
|
58
|
+
reason: 'Violação da Regra 11: Conventional Commits (feat:, fix:, chore:, etc.) são terminantemente proibidos. Use exclusivamente [<TICKET>] - <Ação em português>.'
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 2. Verificar formato canônico [<TICKET>] - <Ação>
|
|
63
|
+
const match = cleanMsg.match(COMMIT_REGEX);
|
|
64
|
+
if (!match) {
|
|
65
|
+
return {
|
|
66
|
+
valid: false,
|
|
67
|
+
reason: 'Violação da Regra 11: A mensagem de commit deve seguir estritamente o formato: [<TICKET>] - <Ação em português>. Ex: [PROJ-123] - Implementar rate limit defensivo'
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const ticket = match[1];
|
|
72
|
+
const action = match[2].trim();
|
|
73
|
+
|
|
74
|
+
if (action.length < 3) {
|
|
75
|
+
return {
|
|
76
|
+
valid: false,
|
|
77
|
+
reason: 'Violação da Regra 11: A descrição da ação no commit deve conter pelo menos 3 caracteres substantivos.'
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
valid: true,
|
|
83
|
+
ticket,
|
|
84
|
+
action,
|
|
85
|
+
cleanMessage: cleanMsg
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Valida a nomenclatura de uma branch contra a Regra 11 e Princípio 3.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} branchName
|
|
93
|
+
* @returns {{ valid: boolean, branch?: string, ticket?: string, reason?: string }}
|
|
94
|
+
*/
|
|
95
|
+
lintBranchName(branchName) {
|
|
96
|
+
if (!branchName || typeof branchName !== 'string') {
|
|
97
|
+
return { valid: false, reason: 'Nome de branch vazio ou indefinido.' };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const branch = branchName.trim().replace(/^refs\/heads\//, '');
|
|
101
|
+
if (!branch) {
|
|
102
|
+
return { valid: false, reason: 'Nome de branch vazio após sanitização.' };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Permitir branches primárias canônicas
|
|
106
|
+
if (['main', 'master', 'develop'].includes(branch)) {
|
|
107
|
+
return { valid: true, branch, ticket: null };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Proibir prefixos legados
|
|
111
|
+
if (FORBIDDEN_BRANCH_PREFIXES.test(branch)) {
|
|
112
|
+
return {
|
|
113
|
+
valid: false,
|
|
114
|
+
reason: `Violação da Regra 11: Prefixos legados (feature/**, fix/**, etc.) são proibidos. A branch deve conter a chave do ticket (ex: PROJ-123 ou PROJ-123-descricao). Recebido: "${branch}".`
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// A branch de tarefa deve se chamar o Ticket ou Ticket com slug descritivo
|
|
119
|
+
const match = branch.match(BRANCH_CANONICAL_REGEX);
|
|
120
|
+
if (!match) {
|
|
121
|
+
return {
|
|
122
|
+
valid: false,
|
|
123
|
+
reason: `Violação da Regra 11: O nome da branch de tarefa deve conter a chave do ticket (ex: PROJ-123 ou PROJ-123-descricao-semantica). Recebido: "${branch}".`
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const ticket = match[1];
|
|
128
|
+
return {
|
|
129
|
+
valid: true,
|
|
130
|
+
branch,
|
|
131
|
+
ticket
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Inspeciona o estado atual do repositório Git local com timeouts seguros.
|
|
137
|
+
*
|
|
138
|
+
* @param {string} [repoDir=process.cwd()]
|
|
139
|
+
* @returns {{ compliant: boolean, currentBranch: string, lastCommit: string, branchAudit: object, commitAudit: object }}
|
|
140
|
+
*/
|
|
141
|
+
auditGitState(repoDir = process.cwd()) {
|
|
142
|
+
let currentBranch = 'unknown';
|
|
143
|
+
let lastCommit = '';
|
|
144
|
+
|
|
145
|
+
if (process.env.GITHUB_HEAD_REF) {
|
|
146
|
+
currentBranch = process.env.GITHUB_HEAD_REF;
|
|
147
|
+
} else {
|
|
148
|
+
try {
|
|
149
|
+
currentBranch = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], {
|
|
150
|
+
cwd: repoDir,
|
|
151
|
+
encoding: 'utf8',
|
|
152
|
+
timeout: 3000,
|
|
153
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
154
|
+
}).trim();
|
|
155
|
+
} catch (_err) {
|
|
156
|
+
currentBranch = '';
|
|
157
|
+
}
|
|
158
|
+
if (currentBranch === 'HEAD' && process.env.GITHUB_REF_NAME) {
|
|
159
|
+
currentBranch = process.env.GITHUB_REF_NAME;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
lastCommit = execFileSync('git', ['log', '-1', '--pretty=%B'], {
|
|
165
|
+
cwd: repoDir,
|
|
166
|
+
encoding: 'utf8',
|
|
167
|
+
timeout: 3000,
|
|
168
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
169
|
+
}).trim();
|
|
170
|
+
} catch (_err) {
|
|
171
|
+
lastCommit = '';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const branchAudit = this.lintBranchName(currentBranch);
|
|
175
|
+
const commitAudit = lastCommit ? this.lintCommitMessage(lastCommit) : { valid: true, reason: 'Nenhum commit encontrado no histórico.' };
|
|
176
|
+
const compliant = branchAudit.valid && commitAudit.valid;
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
compliant,
|
|
180
|
+
currentBranch,
|
|
181
|
+
lastCommit,
|
|
182
|
+
branchAudit,
|
|
183
|
+
commitAudit
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Exporta os apontamentos de higiene Git em OASIS SARIF v2.1.0 oficial.
|
|
189
|
+
*
|
|
190
|
+
* @param {object} params
|
|
191
|
+
* @param {string} [params.branchName]
|
|
192
|
+
* @param {string} [params.commitMessage]
|
|
193
|
+
* @param {string} [params.repoDir]
|
|
194
|
+
* @returns {object} SARIF v2.1.0 JSON object
|
|
195
|
+
*/
|
|
196
|
+
auditToSarif(params = {}) {
|
|
197
|
+
const findings = [];
|
|
198
|
+
let branchAudit;
|
|
199
|
+
let commitAudit;
|
|
200
|
+
|
|
201
|
+
if (params.branchName) {
|
|
202
|
+
branchAudit = this.lintBranchName(params.branchName);
|
|
203
|
+
if (!branchAudit.valid) {
|
|
204
|
+
findings.push({
|
|
205
|
+
ruleId: 'GIT-HYGIENE-BRANCH',
|
|
206
|
+
name: 'Nome de Branch Fora do Padrão Canônico',
|
|
207
|
+
severity: 'HIGH',
|
|
208
|
+
filename: '.git/HEAD',
|
|
209
|
+
line: 1,
|
|
210
|
+
column: 1,
|
|
211
|
+
snippet: params.branchName,
|
|
212
|
+
message: branchAudit.reason,
|
|
213
|
+
remediation: 'Renomeie a branch para seguir o padrão: <TICKET> ou <TICKET>-slug (ex: git branch -m PROJ-123-descricao).'
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (params.commitMessage) {
|
|
219
|
+
commitAudit = this.lintCommitMessage(params.commitMessage);
|
|
220
|
+
if (!commitAudit.valid) {
|
|
221
|
+
findings.push({
|
|
222
|
+
ruleId: 'GIT-HYGIENE-COMMIT',
|
|
223
|
+
name: 'Mensagem de Commit Não Rastreável ou Conventional Commit Proibido',
|
|
224
|
+
severity: 'HIGH',
|
|
225
|
+
filename: '.git/COMMIT_EDITMSG',
|
|
226
|
+
line: 1,
|
|
227
|
+
column: 1,
|
|
228
|
+
snippet: params.commitMessage.split('\n')[0],
|
|
229
|
+
message: commitAudit.reason,
|
|
230
|
+
remediation: 'Formate a mensagem do commit seguindo estritamente: [<TICKET>] - <Ação em português>.'
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!params.branchName && !params.commitMessage) {
|
|
236
|
+
const state = this.auditGitState(params.repoDir);
|
|
237
|
+
if (!state.branchAudit.valid) {
|
|
238
|
+
findings.push({
|
|
239
|
+
ruleId: 'GIT-HYGIENE-BRANCH',
|
|
240
|
+
name: 'Nome de Branch Atual Fora do Padrão Canônico',
|
|
241
|
+
severity: 'HIGH',
|
|
242
|
+
filename: '.git/HEAD',
|
|
243
|
+
line: 1,
|
|
244
|
+
column: 1,
|
|
245
|
+
snippet: state.currentBranch,
|
|
246
|
+
message: state.branchAudit.reason,
|
|
247
|
+
remediation: 'Renomeie a branch para o padrão: <TICKET> ou <TICKET>-slug.'
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
if (!state.commitAudit.valid) {
|
|
251
|
+
findings.push({
|
|
252
|
+
ruleId: 'GIT-HYGIENE-COMMIT',
|
|
253
|
+
name: 'Último Commit Fora do Padrão Canônico',
|
|
254
|
+
severity: 'HIGH',
|
|
255
|
+
filename: '.git/COMMIT_EDITMSG',
|
|
256
|
+
line: 1,
|
|
257
|
+
column: 1,
|
|
258
|
+
snippet: state.lastCommit.split('\n')[0],
|
|
259
|
+
message: state.commitAudit.reason,
|
|
260
|
+
remediation: 'Reescreva a mensagem com git commit --amend.'
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return createSarifReport({
|
|
266
|
+
toolName: 'agent-master-git-hygiene-linter',
|
|
267
|
+
toolVersion: '5.1.0',
|
|
268
|
+
findings,
|
|
269
|
+
properties: {
|
|
270
|
+
compliant: findings.length === 0,
|
|
271
|
+
branchAudit: branchAudit || null,
|
|
272
|
+
commitAudit: commitAudit || null
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Formata relatório executivo em Markdown para os Agentes e PR Reviewer.
|
|
279
|
+
*
|
|
280
|
+
* @param {object} auditResult
|
|
281
|
+
* @returns {string}
|
|
282
|
+
*/
|
|
283
|
+
formatReport(auditResult) {
|
|
284
|
+
let md = `## 🌿 Relatório de Higiene de Git & Rastreabilidade (Regra 11 — Princípio 3)\n\n`;
|
|
285
|
+
const isCompliant = (auditResult.commitAudit ? auditResult.commitAudit.valid : true) &&
|
|
286
|
+
(auditResult.branchAudit ? auditResult.branchAudit.valid : true) &&
|
|
287
|
+
(auditResult.compliant !== false);
|
|
288
|
+
|
|
289
|
+
md += `**Status de Conformidade:** ${isCompliant ? '✅ CONFORME' : '❌ VIOLAÇÃO DETECTADA'}\n\n`;
|
|
290
|
+
|
|
291
|
+
if (auditResult.branchAudit) {
|
|
292
|
+
md += `### 📌 Auditoria de Branch\n`;
|
|
293
|
+
md += `- **Branch:** \`${auditResult.currentBranch || auditResult.branchAudit.branch || 'N/A'}\`\n`;
|
|
294
|
+
md += `- **Válida:** ${auditResult.branchAudit.valid ? '✅ SIM' : '❌ NÃO'}\n`;
|
|
295
|
+
if (!auditResult.branchAudit.valid) {
|
|
296
|
+
md += `- **Motivo:** ${auditResult.branchAudit.reason}\n`;
|
|
297
|
+
} else if (auditResult.branchAudit.ticket) {
|
|
298
|
+
md += `- **Ticket Vinculado:** \`${auditResult.branchAudit.ticket}\`\n`;
|
|
299
|
+
}
|
|
300
|
+
md += '\n';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (auditResult.commitAudit) {
|
|
304
|
+
md += `### 📝 Auditoria de Commit\n`;
|
|
305
|
+
md += `- **Válido:** ${auditResult.commitAudit.valid ? '✅ SIM' : '❌ NÃO'}\n`;
|
|
306
|
+
if (!auditResult.commitAudit.valid) {
|
|
307
|
+
md += `- **Motivo:** ${auditResult.commitAudit.reason}\n`;
|
|
308
|
+
} else if (auditResult.commitAudit.ticket) {
|
|
309
|
+
md += `- **Ticket Vinculado:** \`${auditResult.commitAudit.ticket}\`\n`;
|
|
310
|
+
md += `- **Mensagem Limpa:** \`${auditResult.commitAudit.cleanMessage}\`\n`;
|
|
311
|
+
}
|
|
312
|
+
md += '\n';
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return md;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const defaultLinter = new GitHygieneLinter();
|
|
320
|
+
|
|
321
|
+
function lintCommitMessage(message) {
|
|
322
|
+
return defaultLinter.lintCommitMessage(message);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function lintBranchName(branchName) {
|
|
326
|
+
return defaultLinter.lintBranchName(branchName);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function auditGitState(repoDir) {
|
|
330
|
+
return defaultLinter.auditGitState(repoDir);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Instala hooks nativos de Git (.git/hooks/commit-msg) sem dependências externas.
|
|
335
|
+
*
|
|
336
|
+
* @param {string} [repoDir=process.cwd()]
|
|
337
|
+
* @returns {{ installed: boolean, hooksPath?: string, message: string }}
|
|
338
|
+
*/
|
|
339
|
+
function installGitHooks(repoDir = process.cwd()) {
|
|
340
|
+
const hooksDir = path.join(repoDir, '.git', 'hooks');
|
|
341
|
+
if (!fs.existsSync(path.join(repoDir, '.git'))) {
|
|
342
|
+
return { installed: false, message: 'Diretório .git não encontrado. Repositório não inicializado.' };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (!fs.existsSync(hooksDir)) {
|
|
346
|
+
fs.mkdirSync(hooksDir, { recursive: true });
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const commitMsgHook = path.join(hooksDir, 'commit-msg');
|
|
350
|
+
const hookScript = `#!/usr/bin/env node
|
|
351
|
+
'use strict';
|
|
352
|
+
const fs = require('fs');
|
|
353
|
+
const msgFile = process.argv[2];
|
|
354
|
+
if (!msgFile) process.exit(0);
|
|
355
|
+
|
|
356
|
+
const msg = fs.readFileSync(msgFile, 'utf8');
|
|
357
|
+
const COMMIT_REGEX = /^\\[([A-Z][A-Z0-9_]+-\\d+|GOV|CD|SETUP|RELEASE|INIT)\\]\\s*-\\s*.+$/;
|
|
358
|
+
const FORBIDDEN = /^(feat|fix|chore|docs|style|refactor|perf|test|build|ci|wip)(\\(.*\\))?:\\s*/i;
|
|
359
|
+
|
|
360
|
+
const firstLine = msg.trim().split('\\n')[0].trim();
|
|
361
|
+
if (FORBIDDEN.test(firstLine)) {
|
|
362
|
+
console.error('\\n❌ [REGRA 11] ERRO NO COMMIT: Conventional Commits são proibidos!');
|
|
363
|
+
console.error('Use estritamente: [<TICKET>] - <Ação em português>\\n');
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (!COMMIT_REGEX.test(firstLine)) {
|
|
368
|
+
console.error('\\n❌ [REGRA 11] ERRO NO COMMIT: Formato inválido!');
|
|
369
|
+
console.error('Esperado: [<TICKET>] - <Ação em português>');
|
|
370
|
+
console.error('Recebido: ' + firstLine + '\\n');
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
373
|
+
`;
|
|
374
|
+
|
|
375
|
+
fs.writeFileSync(commitMsgHook, hookScript, { mode: 0o755 });
|
|
376
|
+
return {
|
|
377
|
+
installed: true,
|
|
378
|
+
hooksPath: commitMsgHook,
|
|
379
|
+
message: 'Git hook commit-msg instalado com sucesso para conformidade da Regra 11.'
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
module.exports = {
|
|
384
|
+
GitHygieneLinter,
|
|
385
|
+
lintCommitMessage,
|
|
386
|
+
lintBranchName,
|
|
387
|
+
auditGitState,
|
|
388
|
+
installGitHooks,
|
|
389
|
+
TICKET_REGEX,
|
|
390
|
+
COMMIT_REGEX
|
|
391
|
+
};
|