PR Guard
Uma CLI open source que diz quantas pessoas devem revisar um pull request, com um modelo pequeno no runner do seu CI.
O pr-guard lê o diff e a descrição de um pull request, faz ao modelo
TS-PRGuard cinco perguntas sobre ele, soma verificações determinísticas
(segredos, SQL destrutivo, caminhos sensíveis, tamanho) e deixa uma política em YAML decidir: nenhum revisor, um ou
dois, ou bloqueado. Ele comenta no pull request, aplica labels e define um status pr-guard, no GitHub, GitLab ou
Bitbucket.
É open source (Apache-2.0): typestrict-lab/pr-guard. O TS-PRGuard é um
modelo treinado na typestrict.ai (150M de parâmetros, um arquivo ONNX de 8 bits com 165 MB, também Apache-2.0). Num
runner ubuntu-latest padrão, uma verificação levou de 11 a 71 segundos e de 460 MB a 1,9 GB de memória em pull requests
de 48 a 1.937 linhas alteradas. A memória para de crescer quando o diff enche o que o modelo lê (5.000 caracteres por
pergunta, 14.000 para breaking change).
Zero revisores não é merge: a política não pede aprovação humana extra. O pr-guard comenta, põe labels e define um
status, e nunca faz merge; auto_merge: true só adiciona o label pr-guard:auto-merge para a sua própria automação.
Início rápido
# .github/workflows/pr-guard.yml
on: pull_request
permissions:
contents: read
pull-requests: write # o comentário e as labels
statuses: write # o status pr-guard
jobs:
pr-guard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: typestrict-lab/pr-guard@v1@v1 é a versão major da action; o binário que ela instala é v0.1.0. A action instala um binário de release, guarda o modelo em cache e roda pr-guard check com o token do workflow.
fetch-depth: 0 traz o histórico, para que exista a base comum com a branch de destino.
No notebook:
curl -fsSL https://raw.githubusercontent.com/typestrict-lab/pr-guard/main/install.sh | sh
pr-guard check # esta branch contra a base comum
pr-guard check --staged --no-model # as mudanças em stage, só as regras, na horaShadow e enforce
Ele começa em modo shadow: comenta em todo pull request, o status sempre passa e diz o que pediria. Quando as
respostas parecerem certas no seu código, escreva mode: enforce em .github/pr-guard.yml e torne pr-guard um status
obrigatório. No modo enforce o status falha quando a política bloqueia, e espera até o pull request ter as aprovações que
o veredito pede.
As perguntas e a política padrão
O modelo responde cada pergunta com a probabilidade de cada resposta, numa passada só na CPU. Macro-F1 em exemplos separados do treino, do card do modelo:
| O modelo responde | TS-PRGuard | Jev | Padrão na política |
|---|---|---|---|
| Faz o que a descrição diz? | 0,928 | 0,807 | avisa acima de 40% de que não faz, bloqueia acima de 70% |
| Qual o tamanho para revisar? | 0,660 | 0,383 | avisa quando é muito grande |
| Quebra quem já usa? | 0,870 | 0,910 | avisa acima de 50% |
| Os testes cobrem a mudança? | 0,921 | 0,879 | avisa acima de 50% de que não cobrem |
| Introduz uma vulnerabilidade? | 0,904 | 0,755 | avisa acima de 70% |
Ele supera o Jev em 4 de 5 perguntas pelo Cálculo de reembolso; em compatibilidade o Jev fica à frente.
As respostas entram numa nota: 85% ou mais não precisa de revisor, 60% ou mais precisa de um, abaixo disso de dois. Um
aviso pede pelo menos um revisor, um bloqueio pede dois. O comentário mostra a conta de cada pull request. Toda chave é
opcional; pr-guard init imprime o padrão inteiro, comentado:
mode: shadow # ou enforce
model: pr-guard:1
reviewers: { none: 85%, one: 60% }
questions:
vulnerability: { weight: 3, warn: 70%, block: 90% }
rules:
secrets: block
sensitive_paths: { level: warn, paths: [infra/**, "**/auth/**"] }Cada chave está explicada em docs/policy.md (em inglês).
Escolher o modelo
--model, senão $PR_GUARD_MODEL, senão model na política:
| Valor | O que é |
|---|---|
pr-guard:1 (padrão) | O TS-PRGuard v1 fixado: os arquivos exatos com que esta release do pr-guard foi testada, conferidos por SHA-256. |
pr-guard:latest | O TS-PRGuard mais novo publicado no Hugging Face, antes de uma release do pr-guard fixá-lo. |
hf:<owner>/<repo>[@<revision>] | Qualquer repositório do Hugging Face com os mesmos arquivos, como o seu próprio modelo. $HF_TOKEN para um privado. |
https://… | Uma URL base que serve decision_ai.json, tokenizer.json e onnx/model_8bit.onnx (uma CDN ou um espelho interno). |
| um diretório | Usado como está. |
O modelo é baixado uma vez, conferido arquivo por arquivo contra o SHA-256 e guardado em ~/.cache/pr-guard.
O seu próprio modelo
O TS-PRGuard aprendeu com pull requests públicos. Para ter um que aprendeu com os seus, treine um modelo na typestrict.ai com os seus pull requests como dados (veja Treinos) e aponte a política para ele:
model: hf:sua-org/seu-modelo-de-prPublicar um modelo coloca o pacote num repositório do Hugging Face com exatamente os
arquivos que o pr-guard lê. Para mantê-lo privado, baixe o pacote (typestrict pull <model>) e sirva de uma URL sua, ou
use o diretório direto.
Outros CIs e hosts
A configuração completa de cada um está em docs/ci.md e docs/hosts.md (em inglês):
- GitLab CI: um componente de CI/CD nos pipelines de merge request; os achados vão para o Code Quality e o relatório de testes.
- Bitbucket Pipelines: um pipe sobre a imagem Docker; o relatório JUnit aparece no pull request.
- CircleCI: um orb com
installecheck; passe a branch de destino comobase. - Bitrise: um step que grava o relatório JUnit na aba Tests.
- Jenkins: um exemplo de Jenkinsfile que roda a imagem Docker nos builds de pull request.
- pre-commit: um hook que roda as regras nas mudanças em stage, sem o modelo.
- Qualquer outro: o script de instalação, Homebrew, Docker (
ghcr.io/typestrict-lab/pr-guard:0.1.0) ou Cargo, e depoispr-guard check.
No GitLab ele precisa de um GITLAB_TOKEN com o escopo api, no Bitbucket de um BITBUCKET_TOKEN; o Bitbucket não tem
labels.
Privacidade
Sem API, sem chave, sem telemetria. O modelo roda no runner do CI, o código nunca sai dele, e as únicas chamadas que o pr-guard faz são o download do modelo e o comentário, as labels e o status no seu próprio host. A action nunca executa código do pull request: ela só lê o diff que o checkout trouxe.