Como configurar monorepo no GitHub com proteções de branch por pacote
1. Introdução ao Monorepo e Proteções de Branch por Pacote
Um monorepo (monolithic repository) é uma estratégia de versionamento onde múltiplos projetos ou pacotes são armazenados em um único repositório Git. Essa abordagem oferece benefícios significativos como compartilhamento de código entre pacotes, versionamento unificado, refatoração cross-package simplificada e padronização de ferramentas de build e CI/CD.
No entanto, monorepos apresentam desafios específicos de segurança e integridade. Uma mudança em um pacote crítico (como autenticação) pode impactar toda a aplicação, e sem controles granulares, qualquer desenvolvedor poderia introduzir alterações sensíveis sem a devida revisão. As proteções de branch no GitHub, quando configuradas por pacote, resolvem esse problema ao exigir revisão de times específicos para mudanças em diretórios específicos.
2. Estruturando o Monorepo para Controle Granular
A base para proteções por pacote é uma estrutura de diretórios bem definida. Considere esta organização:
meu-monorepo/
├── packages/
│ ├── auth/ # Pacote de autenticação
│ ├── ui/ # Componentes de interface
│ ├── api/ # API backend
│ └── shared/ # Código compartilhado
├── apps/
│ ├── web/ # Aplicação web
│ └── mobile/ # Aplicação mobile
├── .github/
│ ├── CODEOWNERS
│ └── workflows/
└── package.json
Cada pacote deve ter seu próprio package.json com nome e escopo definidos. Para ferramentas como Lerna ou Nx, configure:
// lerna.json
{
"version": "independent",
"packages": ["packages/*", "apps/*"]
}
// nx.json
{
"tasksRunnerOptions": {
"default": {
"runner": "nx/tasks-runners/default"
}
}
}
3. Configurando Regras de Proteção de Branch no GitHub
Acesse as configurações do repositório no GitHub em Settings > Branches > Add branch protection rule. Configure para main e develop:
Branch name pattern: main
✅ Require a pull request before merging
✅ Require approvals: 2
✅ Dismiss stale pull request approvals when new commits are pushed
✅ Require status checks to pass before merging
✅ Require branches to be up to date
✅ Include administrators
✅ Require conversation resolution before merging
Para branches de release (ex: release/*), crie regras similares mas com menos exigências:
Branch name pattern: release/*
✅ Require a pull request before merging
✅ Require approvals: 1
✅ Require status checks to pass before merging
4. Implementando Proteções por Pacote com CODEOWNERS
O arquivo .github/CODEOWNERS é a peça central para proteções por pacote. Ele mapeia caminhos de diretório para times ou usuários que devem revisar mudanças naqueles arquivos.
Exemplo completo:
# Proteção global: todos os arquivos precisam de revisão do tech-lead
* @tech-lead
# Pacote de autenticação: revisão obrigatória do time de segurança
packages/auth/ @team-security @auth-leads
# Pacote de API: revisão do time de backend
packages/api/ @team-backend @api-architects
# Pacote de UI: revisão do time de frontend
packages/ui/ @team-frontend @design-system
# Código compartilhado: revisão de todos os leads
packages/shared/ @tech-lead @arch-leads
# Workflows do GitHub: revisão do time de DevOps
.github/workflows/ @team-devops
# Documentação: qualquer um pode revisar
docs/ @team-all
Quando um PR altera arquivos em packages/auth/, o GitHub automaticamente solicita revisão de @team-security e @auth-leads. Se o PR também altera packages/ui/, os revisores de frontend são adicionados.
5. Automatizando Status Checks por Pacote com GitHub Actions
Para garantir que mudanças em pacotes específicos passem por testes antes do merge, crie workflows com filtros de caminho:
# .github/workflows/test-auth.yml
name: Test Auth Package
on:
pull_request:
paths:
- 'packages/auth/**'
- 'packages/shared/**' # Dependências compartilhadas
branches:
- main
- develop
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npx jest packages/auth --coverage
- run: npx eslint packages/auth
Workflow para pacote de API:
# .github/workflows/test-api.yml
name: Test API Package
on:
pull_request:
paths:
- 'packages/api/**'
- 'packages/shared/**'
branches:
- main
- develop
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: npm ci
- run: npx jest packages/api --forceExit
Combine múltiplos status checks nas regras de proteção de branch. Se um PR altera packages/auth e packages/api, ambos os workflows devem passar.
6. Integração de Proteções com Ferramentas de Monorepo
Ferramentas como Lerna, Nx e Turborepo permitem executar comandos apenas nos pacotes afetados, otimizando a CI:
Com Lerna:
# .github/workflows/ci.yml
name: CI
on:
pull_request:
branches:
- main
jobs:
test-affected:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # Necessário para detectar mudanças
- uses: actions/setup-node@v4
- run: npm ci
- run: npx lerna run test --since origin/main
- run: npx lerna run lint --since origin/main
Com Nx:
# .github/workflows/ci-nx.yml
name: CI Nx
on:
pull_request:
branches:
- main
jobs:
affected:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
- run: npm ci
- run: npx nx affected:test --base=origin/main --parallel=3
- run: npx nx affected:lint --base=origin/main
- run: npx nx affected:build --base=origin/main
Esses comandos detectam automaticamente quais pacotes foram alterados em relação à branch base e executam apenas os testes relevantes, reduzindo significativamente o tempo de CI.
7. Boas Práticas e Monitoramento Contínuo
Para manter o sistema funcionando:
-
Documente as regras: Crie um
CONTRIBUTING.mdexplicando como as proteções funcionam e como os times devem revisar PRs. -
Revise periodicamente: A cada sprint, verifique se os CODEOWNERS ainda refletem a estrutura atual da equipe.
-
Exceções emergenciais: Configure um processo para bypass de proteções em situações críticas (ex: hotfix em produção). Use branches especiais como
hotfix/*com regras menos restritivas. -
Monitore conflitos: Se um pacote depende de outro, ambos os CODEOWNERS devem revisar. Use
paths-ignorepara evitar duplicação desnecessária de status checks. -
Eduque a equipe: Realize workshops sobre como criar PRs que respeitem as proteções e como resolver conflitos de revisão.
Referências
-
GitHub Docs: About branch protection rules — Documentação oficial do GitHub sobre regras de proteção de branch, incluindo configuração de status checks e aprovações.
-
GitHub Docs: About code owners — Guia completo sobre o arquivo CODEOWNERS, sintaxe e como funciona a revisão automática por caminho.
-
Lerna Documentation: Running commands — Documentação oficial do Lerna sobre execução de comandos em escopo, incluindo
--sincepara pacotes afetados. -
Nx Documentation: Affected commands — Guia do Nx sobre comandos affected para executar testes e builds apenas nos pacotes modificados.
-
GitHub Actions: Workflow syntax for on pull request paths — Documentação sobre filtros de caminho em workflows, essencial para acionar jobs por pacote.
-
Turborepo Documentation: CI Integration — Guia oficial do Turborepo para integração com CI, incluindo detecção de mudanças e cache.
-
Atlassian: Monorepo vs Polyrepo — Artigo técnico comparando monorepo e polyrepo, com boas práticas de estruturação e segurança.