Escrevendo Regras
Regras são funções TypeScript que verificam seu codebase quanto à conformidade com ADRs. Elas ficam em arquivos .rules.ts complementares ao lado dos arquivos markdown dos ADRs e são executadas quando você roda archgate check.
.archgate/adrs/ ARCH-001-command-structure.md # The decision ARCH-001-command-structure.rules.ts # The automated checksConfiguração básica
Seção intitulada “Configuração básica”Todo arquivo de regras exporta por padrão um objeto simples tipado com satisfies RuleSet. Cada chave no objeto rules se torna um ID de regra, e cada regra tem uma description e uma função async check que recebe um objeto de contexto.
/// <reference path="../rules.d.ts" />
export default { rules: { "my-rule-id": { description: "What this rule checks", async check(ctx) { // Your check logic here }, }, },} satisfies RuleSet;Um único arquivo de regras pode definir múltiplas regras:
/// <reference path="../rules.d.ts" />
export default { rules: { "first-rule": { description: "Checks one thing", async check(ctx) { // ... }, }, "second-rule": { description: "Checks another thing", async check(ctx) { // ... }, }, },} satisfies RuleSet;A API de Contexto
Seção intitulada “A API de Contexto”O objeto ctx passado para toda função check fornece capacidades de leitura de arquivos, busca e relatório. Aqui está uma referência detalhada com exemplos.
ctx.scopedFiles
Seção intitulada “ctx.scopedFiles”Um array de caminhos de arquivo que correspondem ao glob files do frontmatter do ADR. Se o ADR não tiver o campo files, isso inclui todos os arquivos do projeto.
for (const file of ctx.scopedFiles) { const content = await ctx.readFile(file); // Check content...}Use ctx.scopedFiles quando sua regra deve se aplicar apenas aos arquivos que o ADR governa. Por exemplo, uma regra de estrutura de comandos com escopo src/commands/**/*.ts receberá apenas arquivos de comandos.
ctx.changedFiles
Seção intitulada “ctx.changedFiles”Um array de caminhos de arquivo que foram modificados (staged ou alterados no git). Útil para verificação incremental — valide apenas os arquivos que foram efetivamente alterados.
const filesToCheck = ctx.scopedFiles.filter((f) => ctx.changedFiles.includes(f));for (const file of filesToCheck) { // Only check changed files}ctx.readFile(path)
Seção intitulada “ctx.readFile(path)”Lê o conteúdo de um arquivo como string. O caminho é relativo à raiz do projeto.
const content = await ctx.readFile("src/cli.ts");ctx.readJSON(path)
Seção intitulada “ctx.readJSON(path)”Lê e faz o parse de um arquivo JSON. Retorna unknown — faça cast para o formato esperado.
const pkg = (await ctx.readJSON("package.json")) as { dependencies?: Record<string, string>;};ctx.grep(file, pattern)
Seção intitulada “ctx.grep(file, pattern)”Busca em um único arquivo com uma expressão regular. Retorna um array de objetos GrepMatch, cada um com as propriedades file, line, column e content.
const matches = await ctx.grep(file, /console\.error\(/);for (const match of matches) { ctx.report.violation({ message: "Use logError() instead of console.error()", file: match.file, line: match.line, });}ctx.grepFiles(pattern, fileGlob)
Seção intitulada “ctx.grepFiles(pattern, fileGlob)”Busca em múltiplos arquivos que correspondem a um padrão glob. Retorna um array plano de objetos GrepMatch de todos os arquivos correspondentes.
const matches = await ctx.grepFiles(/TODO:/, "src/**/*.ts");for (const match of matches) { ctx.report.warning({ message: "TODO comment found", file: match.file, line: match.line, });}ctx.glob(pattern)
Seção intitulada “ctx.glob(pattern)”Encontra arquivos por padrão glob. Retorna um array de caminhos de arquivo relativos à raiz do projeto.
const testFiles = await ctx.glob("tests/**/*.test.ts");ctx.report
Seção intitulada “ctx.report”A interface de relatório com três métodos de severidade:
ctx.report.violation(detail)— severidade error (código de saída 1, bloqueia CI)ctx.report.warning(detail)— severidade warning (registrado, mas não bloqueia)ctx.report.info(detail)— informacional (registrado para visibilidade)
Cada método aceita um objeto com:
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
message | string | Sim | Qual é a violação |
file | string | Não | Caminho do arquivo com a violação |
line | number | Não | Número da linha da violação |
fix | string | Não | Correção sugerida (exibida ao desenvolvedor) |
ctx.report.violation({ message: "Command file must export a register*Command function", file: "src/commands/check.ts", line: 5, fix: "Add: export function registerCheckCommand(program: Command) { ... }",});ctx.projectRoot
Seção intitulada “ctx.projectRoot”O caminho absoluto para o diretório raiz do projeto. Útil quando você precisa construir caminhos absolutos.
Níveis de severidade
Seção intitulada “Níveis de severidade”Cada regra pode definir uma severidade padrão em sua configuração. A severidade determina como as violações são tratadas:
| Severidade | Código de saída | Comportamento |
|---|---|---|
error | 1 | Bloqueia CI, deve ser corrigido |
warning | 0 | Registrado, mas não bloqueia |
info | 0 | Informacional, registrado para visibilidade |
Defina a severidade na definição da regra:
export default { rules: { "my-rule": { description: "...", severity: "warning", async check(ctx) { // Violations from this rule are warnings, not errors ctx.report.violation({ message: "..." }); }, }, },} satisfies RuleSet;Se severity for omitido, o padrão é error.
Você também pode reportar com diferentes severidades dentro da mesma regra usando ctx.report.violation(), ctx.report.warning() e ctx.report.info() diretamente.
Timeout de regras
Seção intitulada “Timeout de regras”Cada regra tem um timeout de execução de 30 segundos. Se uma regra exceder esse limite, ela é tratada como erro. Isso impede que verificações descontroladas bloqueiem o pipeline.
Mantenha as regras rápidas:
- Usando
ctx.grepFiles()ao invés de ler cada arquivo manualmente - Usando
Promise.all()para verificar arquivos em paralelo - Delimitando regras com o campo
filesdo frontmatter para limitar o número de arquivos processados
O campo fix
Seção intitulada “O campo fix”O campo fix é uma string opcional exibida ao desenvolvedor junto com a mensagem de violação. Ele descreve qual ação tomar para resolver o problema. Correções não são aplicadas automaticamente — são orientações.
ctx.report.violation({ message: `Unapproved dependency: "chalk"`, file: "package.json", fix: "Use styleText() from node:util instead of chalk",});Quando exibido, o fix aparece abaixo da mensagem de violação:
ARCH-006/no-unapproved-deps package.json Unapproved dependency: "chalk" Fix: Use styleText() from node:util instead of chalkDicas para escrever regras
Seção intitulada “Dicas para escrever regras”-
Use
Promise.all()para verificações de arquivo em paralelo. Ao verificar múltiplos arquivos independentes, processe-os em paralelo ao invés de sequencialmente.// Good: parallelconst checks = files.map(async (file) => {const content = await ctx.readFile(file);// ...});await Promise.all(checks);// Avoid: sequentialfor (const file of files) {const content = await ctx.readFile(file);// ...} -
Use
ctx.changedFilespara verificação incremental. Ao executararchgate check --staged,ctx.changedFilescontém apenas os arquivos staged no git. Filtrectx.scopedFilescom ele para verificar apenas o que mudou. -
Mantenha as regras focadas em uma única preocupação. Uma regra que verifica tanto convenções de nomenclatura quanto padrões de import deve ser dividida em duas regras com IDs separados.
-
Use
ctx.grepFiles()ao invés de iteração manual. Ao buscar um padrão em muitos arquivos,ctx.grepFiles()é mais eficiente do que ler cada arquivo e executar uma regex. -
Forneça mensagens de
fixacionáveis. Um fix como “Não faça isso” não é útil. Diga ao desenvolvedor exatamente o que fazer. -
Filtre arquivos não-aplicáveis cedo. Se sua regra se aplica apenas a certos arquivos dentro do escopo, filtre
ctx.scopedFilesantes de processar:const commandFiles = ctx.scopedFiles.filter((f) => !f.endsWith("index.ts")); -
Trate arquivos ausentes com elegância. Se sua regra lê um arquivo específico como
package.json, envolva a leitura em um try/catch e retorne antecipadamente se o arquivo não existir.
Próximos passos
Seção intitulada “Próximos passos”- Padrões Comuns de Regras — Padrões prontos para copiar e colar, organizados por categoria: gerenciamento de dependências, restrições de import, estrutura de arquivos, qualidade de código, esquema de banco de dados e limites de arquitetura.
- Referência da API de Regras — Referência completa de todos os tipos e funções da API de regras.
- Integração com CI — Integre
archgate checkao seu pipeline para aplicar regras em cada PR.