
CLI em Python que cria repositórios GitHub com padrões seguros — proteção de branch, Dependabot, varredura de segredos e varredura de segurança pré-execução — aplicados automaticamente.
Crie repositórios GitHub com configurações seguras aplicadas automaticamente. Substitui a lista de verificação de configurações pós-criação de cinco minutos por um único comando.``` gh-safe-repo create <owner/repo>
Proteção de branches, tags imutáveis, Dependabot, permissões restritas do Actions, varredura de segredos com proteção contra push, e wiki e projetos desativados — tudo configurado antes de você escrever sua primeira linha de código.
gh-safe-repo está em desenvolvimento ativo. Funciona bem para o caso de uso de criar um novo repositório com padrões seguros. Estou trabalhando no polimento das opções de CLI para melhor alinhamento com as expectativas dos usuários. Espere mudanças significativas até chegarmos a um ponto em que estarei fazendo releases e tenha CI/CD bem definido. ✌️
---
## Índice
- [Por quê](#por-quê)
- [O que ele altera](#o-que-ele-altera)
- [Requisitos](#requisitos)
- [Instalação](#instalação)
- [Início Rápido](#início-rápido)
- [Referência da CLI](#referência-da-cli)
- [Saída do Dry Run / Plano](#saída-do-dry-run--plano)
- [Modo Correção (Auditar Repositórios Existentes)](#modo-correção-auditar-repositórios-existentes)
- [Espelhando Repositórios (`--from`)](#espelhando-repositórios---from)
- [Criando um Repositório a partir de um Diretório Local (`--local`)](#criando-um-repositório-a-partir-de-um-diretório-local---local)
- [Scanner de Segurança Pré-voo](#scanner-de-segurança-pré-voo)
- [Escaneamento independente](#escaneamento-independente)
- [Suprimindo falsos positivos](#suprimindo-falsos-positivos)
- [Configuração](#configuração)
- [Limitações do Plano GitHub](#limitações-do-plano-github)
- [Como Funciona](#como-funciona)
- [Desenvolvimento](#desenvolvimento)
---
## Por quê
As configurações padrão do GitHub são otimizadas para descoberta e flexibilidade, não para segurança. Cada novo repositório vem com:
- Wiki e Projetos ativados (superfície de ataque, mesmo que não utilizados)
- Merge commits permitidos (histórico bagunçado, mas não é a principal preocupação)
- Sem proteção de branch (qualquer pessoa com acesso de escrita pode fazer push diretamente para `main`)
- Sem alertas do Dependabot
- GitHub Actions com permissões de escrita no repositório
- Actions autorizados a aprovar pull requests
Corrigir tudo isso manualmente leva minutos por repositório e é fácil de esquecer. `gh-safe-repo` aplica um conjunto de padrões opinativos, porém práticos, de uma só vez, com uma pré-visualização do plano para que você saiba exatamente o que será alterado antes que qualquer mudança aconteça.
---
## O que ele altera
### Configurações do repositório
| Configuração | Padrão GitHub | Padrão seguro | Observações |
|---|---|---|---|
| Visibilidade | Público | **Privado** | Use `--public` para substituir |
| Wiki | Ativado | **Desativado** | |
| Projetos | Ativado | **Desativado** | |
| Issues | Ativado | Ativado | |
| Excluir branch ao mesclar | Desligado | Desligado | Defina como `true` na configuração para limpeza automática |
| Permitir merge commits | Ativado | Ativado | Defina como `false` na configuração para apenas squash |
| Permitir squash merge | Ativado | Ativado | |
| Permitir rebase merge | Ativado | Ativado | |
### GitHub Actions
| Configuração | Padrão GitHub | Padrão seguro |
|---|---|---|
| Actions permitidas | Todas | **Selecionadas** (GitHub-próprias + criadores verificados; customizável) |
| Permissões padrão do fluxo de trabalho | Leitura/escrita | **Somente leitura** |
| Actions podem aprovar PRs | Sim | **Não** |
| Exigir fixação por SHA | Não | **Sim** (fluxos de trabalho devem fixar ações a um commit SHA, não a uma tag mutável) |
| Política de aprovação de fork PR | Contribuidores de primeira viagem novos no GitHub | **Todos os contribuidores externos** — exigir aprovação antes que forks PR executem CI. Opções: apenas contas novas do GitHub (padrão GitHub), primeiros contribuidores do repositório, ou todos os forks PR (mais seguro) |
### Proteção de branch (repositórios públicos, ou qualquer repositório em plano pago)
| Regra | Valor |
|---|---|
| Exigir pull request antes de mesclar | Sim |
| Revisões de aprovação necessárias | 1 |
| Descartar revisões obsoletas ao fazer push | Sim |
| Exigir resolução de conversas | Sim |
| Permitir force pushes | Não |
| Permitir exclusão de branch | Não |
| Aplicar a administradores | Não (permite que ferramentas do proprietário façam push) |
A proteção de branch é aplicada via **API de Rulesets** por padrão (`use_rulesets = true`): um único ruleset `gh-safe-repo defaults` cobre todas as branches configuradas e expressa "administradores podem contornar" através de um ator de bypass em vez da flag clássica `enforce_admins`. Defina `use_rulesets = false` para o caminho clássico legado por branch (mantido por um ciclo de release).
**Migrando um repositório existente da proteção clássica:** se `fix` encontrar proteção clássica de branch em um repositório, ele se recusa a convertê-la para um ruleset a menos que você passe `--migrate-branch-protection`. Regras exclusivas do clássico não têm equivalente no ruleset que esta ferramenta constrói e seriam descartadas silenciosamente — lacunas conhecidas:
- `required_status_checks` — verificações de CI obrigatórias não são modeladas no corpo do ruleset.
- `restrictions` (restrições de push por usuário/equipe) — Rulesets modelam isso de forma diferente através de atores de bypass; não é um mapeamento 1:1.
- Divergência por branch — um ruleset de condição compartilhada única não pode expressar regras diferentes para `master` vs `main`.
Com a flag, `fix` cria/atualiza o ruleset e então exclui a proteção clássica em cada branch para que as duas camadas não se acumulem.
### Proteção de tags (repositórios públicos, ou qualquer repositório em plano pago)
A proteção de tags cria um GitHub Ruleset direcionado a todas as tags (`*` por padrão, configurável via `protected_tags`). As seguintes regras são aplicadas:
| Regra do ruleset | Aplicada? | Observações |
|---|---|---|
| Restringir criações | Não | |
| **Restringir atualizações** | **Sim** | Impede reescrita / force-push de tags |
| **Restringir exclusões** | **Sim** | Impede `git push --delete` de tags |
| Exigir histórico linear | Não | |
| Exigir que implantações tenham sucesso | Não | |
| Exigir commits assinados | Não | |
| Exigir que verificações de status passem | Não | |
| Bloquear force pushes | Não | |
Os administradores do repositório estão na lista de bypass (consistente com o padrão `enforce_admins = false` da proteção de branch). Só funciona em repositórios públicos ou planos GitHub pagos (mesma restrição da proteção de branch). Repositórios privados de plano gratuito verão isso ignorado na saída do plano.
### Segurança
| Funcionalidade | Comportamento |
|---|---|
| Alertas do Dependabot | Ativado (repositórios públicos / planos pagos) |
| Atualizações de segurança do Dependabot | Ativado (abre PRs automaticamente para dependências vulneráveis) |
| Varredura de segredos | Automática em repositórios públicos; ativada em planos privados pagos |
| Proteção contra push | Ativada (bloqueia commits contendo segredos suportados) |
| Relato privado de vulnerabilidades | Ativado (permite que pesquisadores de segurança relatem privadamente) |
| Gráfico de dependências | Automático em repositórios públicos; sem API REST para privados (apenas interface) |
---
## Requisitos
- Python 3.8+
- [`gh` CLI](https://cli.github.com/) instalada e autenticada (`gh auth login`), **ou** `GITHUB_TOKEN` definido no seu ambiente
- Para `--local` / `--from` (que fazem push ou clonam código): suas credenciais git habituais devem estar configuradas — seja uma chave SSH carregada no `ssh-agent` (quando `gh config get git_protocol` é `ssh`) ou um helper de credenciais HTTPS (`gh auth setup-git` configura um automaticamente). O token OAuth **não** é usado para git push, portanto arquivos de fluxo de trabalho (`.github/workflows/*`) são enviados sem a necessidade do escopo OAuth `workflow`.
- [`uv`](https://docs.astral.sh/uv/) para instalação a partir do código-fonte (recomendado)
- `truffleHog` v3 (opcional — usado pelo scanner pré-voo; detectado automaticamente no PATH, ou executado via podman/docker; recai para regex se nenhum estiver disponível)
---
## Instalação
### A partir do código-fonte com uv (recomendado)```bash
git clone https://github.com/your-username/gh-safe-repo
cd gh-safe-repo
uv tool install .
Isto instala gh-safe-repo no ambiente de ferramentas do uv e o adiciona ao seu PATH.
git clone https://github.com/your-username/gh-safe-repo cd gh-safe-repo uv sync # creates .venv ./gh-safe-repo create <owner/repo>
### Verificar```bash
gh-safe-repo --help
gh-safe-repo create <owner/repo>
gh-safe-repo create <owner/repo> --dry-run
gh-safe-repo create <owner/repo> --public
gh-safe-repo create <owner/repo> --from <owner/source>
gh-safe-repo create <owner/pub> --from <owner/priv> --public
gh-safe-repo create <owner/repo> --local ~/projects/myapp
gh-safe-repo create <owner/repo> --local ~/projects/myapp --public
gh-safe-repo fix <owner/repo>
gh-safe-repo fix <owner/repo> --dry-run
gh-safe-repo fix <owner/repo> --yes
gh-safe-repo scan . gh-safe-repo scan ~/projects/myapp
---
## Referência da CLI```
gh-safe-repo create <owner/repo> [OPTIONS]
gh-safe-repo fix <owner/repo> [OPTIONS]
gh-safe-repo scan <path> [OPTIONS]
Todos os comandos que interagem com o GitHub exigem o formato owner/repo (por exemplo, myuser/my-repo). Para create, o proprietário é validado em relação à sua conta autenticada do GitHub para evitar erros em sistemas com várias contas. Para fix, são necessárias permissões de administrador no repositório alvo, permitindo que você corrija repositórios de propriedade de organizações ou outras contas onde você tem acesso de administrador.
create — Criar um novo repositórioUm create simples (sem --local/--from) inicializa o repositório para que uma branch padrão exista para proteção de branch, depois remove o README.md gerado automaticamente para que o novo repositório comece limpo. Defina auto_init = true na configuração para manter o README. --local/--from enviam seu próprio histórico e nunca criam um README.
fix — Auditar e corrigir um repositório existentescan — Verificação local de segredos| Opção | Descrição |
|---|---|
--config [PATH] | Caminho para o arquivo de configuração; --config simples usa apenas os padrões incorporados |
--debug | Mostra detalhes do scanner |
O código de saída é 0 se não houver achados críticos, 1 se forem encontrados críticos.
--dry-run mostra exatamente o que gh-safe-repo faria, sem fazer alterações ou chamadas de API. Use antes de executar de verdade. Combine com --json para saída do plano legível por máquina:
---```bash gh-safe-repo create <owner/repo> --dry-run --json gh-safe-repo fix <owner/repo> --dry-run --json
Quando `--json` está ativo, o plano é escrito para stdout como um objeto JSON e todas as outras mensagens (progresso, avisos, o rodapé "Dry run") vão para stderr, para que a saída fique limpa para pipe ou script.```
$ gh-safe-repo create <owner/repo> --dry-run
Plan for my-project (private)
Category Action Setting Value
──────────────────────────────────────────────────────────────────
Repository ADD repository my-project (private)
Repository ADD has_wiki false
Repository ADD has_projects false
Actions ADD default_workflow_permissions read
Actions ADD can_approve_pull_request_reviews false
Branch Protection SKIP branch_protection Not available for private repos on free plan
Security SKIP dependabot_alerts Not available for private repos on free plan
1 setting skipped (GitHub plan limitation).
Dry run — no changes made.
Cores das ações:
Saída JSON (--json):```json
{
"changes": [
{ "type": "add", "category": "repository", "key": "has_wiki", "old": null, "new": false, "reason": null },
{ "type": "skip", "category": "branch_protection", "key": "branch_protection", "old": null, "new": null, "reason": "Not available for private repos on free plan" }
],
"summary": { "add": 5, "skip": 2 }
}
`summary` inclui apenas os tipos presentes no plano. Os consumidores devem usar `.get("delete", 0)` etc. em vez de assumir que todas as quatro chaves estão presentes.
---
## Modo de Correção (Auditar Repositórios Existentes)
`fix` compara as configurações atuais de um repositório existente com os padrões seguros e aplica as correções. Nenhuma varredura de segredos — `fix` é puramente sobre configurações de repositório.```bash
# See what's out of compliance
gh-safe-repo fix <owner/repo> --dry-run
# Apply missing safe defaults
gh-safe-repo fix <owner/repo>
# Apply without confirmation prompt (scripting/batch use)
gh-safe-repo fix <owner/repo> --yes
Modo de correção:
UPDATE para configurações alteradas e SKIP para configurações já no valor desejado (detecção de no-op — nunca faz chamadas de API que não alterariam nada)--yes)Apenas mudanças reais são aplicadas — configurações já no valor desejado são exibidas como SKIP e não geram chamadas de API.
--from)--from espelha um repositório existente em um novo com padrões seguros. Funciona tanto para destinos privados quanto públicos:```bash
gh-safe-repo create <owner/repo> --from <owner/source>
gh-safe-repo create <owner/pub> --from <owner/priv> --public
**O que acontece, em ordem:**
1. Suas credenciais git para `github.com` são verificadas de antemão (sonda SSH quando `gh config get git_protocol` é `ssh`; HTTPS é confiável), então uma chave ausente falha rapidamente antes de qualquer repositório ser criado
2. O repositório de origem é clonado localmente (clone completo, sem `--depth`, para que o truffleHog possa percorrer todo o histórico de commits)
3. O [scanner de segurança de pré-voo](#pre-flight-security-scanner) é executado no clone local
4. Você revisa os resultados e confirma (ou aborta)
5. Um novo repositório é criado (privado por padrão, ou público com `--public`)
6. Permissões de Actions e configurações de segurança são aplicadas (Dependabot, varredura de segredos, proteção contra push)
7. O histórico completo é espelhado: `git clone --mirror` + `git push --mirror`
8. A proteção de branches e tags é aplicada (após o push do código, para que o branch de destino exista)
Se a varredura revelar um problema e você abortar, nenhum código é copiado para o GitHub.
> **Nota:** `--from` usa o formato `owner/repo` tanto para a origem quanto para o destino.
---
## Criando um Repositório a partir de um Diretório Local (`--local`)
`--local PATH` é a contraparte local-para-GitHub de `--from`. Ele cria um novo repositório no GitHub e envia código de um repositório git local. `PATH` deve ser um repositório git inicializado (`git init` ou um clone).```bash
gh-safe-repo create <owner/repo> --local ~/projects/myapp
gh-safe-repo create <owner/repo> --local ~/projects/myapp --public
O que acontece, em ordem:
github.com são verificadas de antemão (sonda SSH quando gh config get git_protocol é ssh; HTTPS é confiável), então uma chave ausente falha rapidamente antes de qualquer repositório ser criadopush --all --tags (todos os ramos e tags)origin é adicionado ao repositório local original apontando para a nova URL do GitHub, e o rastreamento upstream do ramo atual é configurado — então git push e git pull funcionam imediatamente sem configuração extra.Ambos --local e --from funcionam para repositórios privados e públicos. Eles são mutuamente exclusivos.
O ramo padrão local (através de git -C PATH symbolic-ref HEAD) é usado para direcionar as regras de proteção de ramo, então a proteção é aplicada no ramo correto mesmo que não seja main.
Dica: Execute
gh-safe-repo scan PATHprimeiro se quiser inspecionar as descobertas sem criar nada.
O verificador é executado localmente e nunca envia código para o GitHub. Use-o de forma independente antes de qualquer envio, ou ele é executado automaticamente como parte dos fluxos de trabalho --from e --local.
gh-safe-repo scan .
gh-safe-repo scan ~/projects/myapp
Código de saída é `0` se não houver achados críticos, `1` se forem encontrados críticos — então ele se compõe perfeitamente com outros comandos:```bash
gh-safe-repo scan . && git push
The full [pre_flight_scan] config applies: banned_strings, max_file_size_mb, trufflehog_mode, etc.
gh-safe-repo automaticamente escolhe o melhor scanner disponível usando uma cadeia de descoberta de três etapas:
trufflehog --version, verifica se é v3 e o utiliza. Uma instalação v2 ou uma versão não reconhecida exibe um aviso e cai para a etapa 2.ghcr.io/trufflesecurity/trufflehog:latest) usando podman run ou docker run, montando o caminho de varredura como somente leitura no mesmo caminho absoluto para que os caminhos de saída JSON sejam idênticos a uma execução nativa.O scanner selecionado é mostrado no cabeçalho "Running pre-flight security scan..." e na entrada SCAN da tabela de plano, por exemplo:``` Running pre-flight security scan... (truffleHog v3.93.4) Running pre-flight security scan... (truffleHog via podman) Running pre-flight security scan... (regex only — see warning above)
Variáveis de ambiente respeitadas pelo caminho do contêiner: `CONTAINER_RUNTIME` para substituir a seleção do runtime (por exemplo, `CONTAINER_RUNTIME=docker`), e `TRUFFLEHOG_IMAGE` para fixar uma tag de imagem específica.
### Executando truffleHog via podman ou Docker (sem instalação local)
Nenhuma configuração manual é necessária. O `gh-safe-repo` detecta podman ou docker automaticamente (passo 2 acima) e executa truffleHog em um contêiner com os mounts de volume corretos. As variáveis de ambiente `CONTAINER_RUNTIME` e `TRUFFLEHOG_IMAGE` são respeitadas.
Um wrapper de shell (`tools/trufflehog`) e um `Containerfile` para construir uma imagem local fixada são fornecidos em [`tools/`](https://github.com/ariesq/gh-safe-repo/blob/HEAD/tools/README.md) para usuários que desejam o truffleHog baseado em contêiner disponível em todo o sistema, ou que precisam de uma imagem isolada (air-gapped).
### Revisão interativa```
Pre-flight scan: my-private-project
CRITICAL my_private_project/config.py:12 AWS Access Key ID
[redacted]
WARNING my_private_project/setup.py:3 Email address
author_email="[email protected]"
1 critical finding, 1 warning.
Critical findings detected. Continue anyway? [y/N]:
N). Você deve digitar explicitamente y para continuar.Y). Pressione Enter para prosseguir ou digite n para abortar.Segredos são ocultados na saída. Endereços de e-mail e TODOs mostram a linha correspondente.
Diretórios de artefatos de construção (node_modules, __pycache__, .venv, venv, dist, build) são ignorados por padrão para manter as varreduras rápidas. Em repositórios git, esta omissão é condicional: antes de remover um diretório, o scanner executa git ls-files -- <dir> para verificar se algum arquivo dentro dele é rastreado. Se estiverem, o diretório é varrido normalmente.
Isso significa que árvores node_modules ou dist versionadas — incomuns, mas acontecem — não são perdidas silenciosamente. Diretórios não versionados (o caso normal) continuam a ser ignorados como antes.
Um aviso ainda é exibido quando subdiretórios SKIP_DIRS são encontrados em um repositório fonte clonado, pois sua presença pode indicar que mais conteúdo do que o esperado foi comitado.
Duas chaves de configuração permitem suprimir resultados conhecidamente seguros sem desabilitar categorias inteiras de verificação.
scan_exclude_paths — ignora arquivos ou diretórios completamente. Os valores são padrões regex separados por nova linha/vírgula que correspondem ao caminho relativo do arquivo. Um arquivo correspondente é excluído de todas as verificações: segredos, e-mails, TODOs, arquivos grandes e detecção de arquivos de contexto de IA. Os mesmos padrões também são passados para o truffleHog via --exclude-paths, para que a cobertura seja consistente independentemente de qual mecanismo de scanner está ativo.```ini
[pre_flight_scan]
scan_exclude_paths = docs/api.github.com.json tests/fixtures/
**`exclude_emails`** — suprimir achados de e-mail para endereços específicos ou domínios inteiros. Os valores são separados por nova linha/vírgula, insensíveis a maiúsculas/minúsculas. Entradas começando com `@` correspondem a todos os e-mails naquele domínio; caso contrário, a entrada deve corresponder exatamente ao endereço completo. Aplica-se tanto à árvore de trabalho quanto aos achados do histórico do git.```ini
[pre_flight_scan]
# Suppress bot addresses and placeholder domains
exclude_emails = [email protected], [email protected], @example.com
[pre_flight_scan] scan_for_secrets = true scan_for_emails = true scan_for_todos = true max_file_size_mb = 100
Quando strings proibidas ou arquivos de contexto de IA são encontrados, o scanner imprime um comando `git filter-repo` pronto para execução para removê-los do histórico do repositório de origem antes de re-executar.
---
## Configuração
`gh-safe-repo` procura a configuração nesta ordem (a primeira correspondência vence):
1. **`--config PATH`** — substituição explícita
2. **`./gh-safe-repo.ini`** — diretório de trabalho atual
3. **`$XDG_CONFIG_HOME/gh-safe-repo/gh-safe-repo.ini`** — padrão para `~/.config` quando `$XDG_CONFIG_HOME` não está definido
Apenas `--config` (sem caminho) ignora completamente a busca por arquivo e usa apenas os padrões internos.
Todos os valores têm padrões seguros — nenhum arquivo de configuração é necessário para começar.
Um exemplo de configuração totalmente anotado está incluído no repositório como `gh-safe-repo.ini.example`. Copie-o para começar:```bash
# User-level config (XDG)
mkdir -p "${XDG_CONFIG_HOME:-$HOME/.config}/gh-safe-repo"
cp gh-safe-repo.ini.example "${XDG_CONFIG_HOME:-$HOME/.config}/gh-safe-repo/gh-safe-repo.ini"
# Or project-level config (current directory)
cp gh-safe-repo.ini.example ./gh-safe-repo.ini
[repo]
private = true
has_wiki = false has_projects = false has_issues = true
delete_branch_on_merge = false
allow_squash_merge = true allow_merge_commit = true allow_rebase_merge = true
create leaves an initialized README in the new repo.auto_init = false
[actions]
allowed_actions = selected
github_owned_allowed = true # actions maintained by GitHub (e.g. actions/checkout) verified_allowed = true # actions from Marketplace verified creators
default_workflow_permissions = read
can_approve_pull_request_reviews = false
sha_pinning_required = true
[branch_protection]
protected_branch = main
require_pull_request = true
required_approving_reviews = 1
dismiss_stale_reviews = true
require_conversation_resolution = true
enforce_admins = false
allow_force_pushes = false
allow_deletions = false
use_rulesets = true
[tag_protection]
protected_tags = *
prevent_tag_deletion = true
prevent_tag_update = true
[security]
enable_dependabot_alerts = true
enable_dependabot_security_updates = true
enable_private_vulnerability_reporting = true
enable_secret_scanning_push_protection = true
[pre_flight_scan] scan_for_secrets = true scan_for_emails = true scan_for_todos = true
max_file_size_mb = 100
[git_transport]
workflow token scope to pushworkflow scope intentionally.---
## Limitações do Plano GitHub
Alguns recursos estão disponíveis apenas dependendo da visibilidade do repositório e do seu plano GitHub.
| Recurso | Grátis + Público | Grátis + Privado | Pro/Equipe + Privado |
|---|:---:|:---:|:---:|
| Proteção de branch / Regras | Sim | Não | Sim |
| Proteção de tag (Regras) | Sim | Não | Sim |
| Alertas do Dependabot | Sim | Não | Sim |
| Atualizações de segurança do Dependabot | Sim | Não | Sim |
| Varredura de segredos | Automático | Não | Sim |
| Proteção de push | Sim | Não | Sim |
| Relatório privado de vulnerabilidades | Sim | Sim | Sim |
| Gráfico de dependências | Automático | Não | Sim |
O `gh-safe-repo` detecta o nível do seu plano e a visibilidade do repositório em tempo real. Recursos indisponíveis aparecem como `SKIP` na saída do plano com um motivo claro — a ferramenta nunca falha silenciosamente.
---
## Como Funciona```
gh-safe-repo create <owner/repo>
│
├─ Parse owner/repo, validate owner matches authenticated user (create only)
├─ Load config (./gh-safe-repo.ini or $XDG_CONFIG_HOME/gh-safe-repo/gh-safe-repo.ini)
├─ Apply CLI flag overrides (--public, etc.)
├─ Authenticate via gh CLI or GITHUB_TOKEN
├─ GET /user → owner login + plan level (single cached call)
│
├─ Build plan (each plugin compares desired vs. current state)
│ ├─ RepositoryPlugin → repo creation + basic settings
│ ├─ ActionsPlugin → allowed actions, workflow permissions, SHA pinning
│ ├─ BranchProtectionPlugin → Rulesets API (default; classic if use_rulesets = false)
│ ├─ SecurityPlugin → Dependabot, secret scanning, push protection, private vuln reporting
│ └─ TagProtectionPlugin → immutable tags via Rulesets API
│
├─ Print plan table
│
└─ Apply (unless --dry-run)
├─ POST /user/repos
├─ PATCH /repos/{owner}/{repo} (settings)
├─ PUT /repos/{owner}/{repo}/actions/permissions/workflow
├─ POST/PATCH /repos/{owner}/{repo}/rulesets (branch protection; default)
│ or PUT /repos/{owner}/{repo}/branches/main/protection (if use_rulesets = false)
├─ PUT /repos/{owner}/{repo}/vulnerability-alerts
├─ PUT /repos/{owner}/{repo}/automated-security-fixes
├─ PUT /repos/{owner}/{repo}/private-vulnerability-reporting
├─ PATCH /repos/{owner}/{repo} (security_and_analysis: push protection)
├─ POST /repos/{owner}/{repo}/rulesets (tag protection ruleset)
├─ git clone --mirror + git push --mirror (if --from)
└─ git clone <local> + git push --all --tags (if --local, git repo)
or git init + add -A + commit + push (if --local, plain dir)
Cada categoria de configurações é uma classe de plugin autocontida (gh_safe_repo/plugins/). Cada plugin:
Plan (lista de objetos Change: ADD / UPDATE / DELETE / SKIP)Isso significa que o modo de auditoria e o modo de criação usam o mesmo caminho de planejamento/aplicação. A única diferença é se o estado atual é obtido de um repositório existente ou assumido como os padrões do GitHub.
As chamadas de API resolvem um token nesta ordem:
GITHUB_TOKEN — permite direcionar uma conta específica sem alternar a sessão ativa do gh (e é a única credencial necessária em CI)gh auth token — o que o gh auth login configurouOs tokens são passados para processos filhos gh api como GH_TOKEN no ambiente do subprocesso e nunca são registrados em logs.
As operações Git (--local / --from push e clone) usam suas próprias credenciais Git — chave SSH ou helper de credenciais — por padrão, não o token da API. Em ambientes sem nenhum dos dois (ex.: CI com apenas GITHUB_TOKEN), a ferramenta recorre ao push via HTTPS com o token na URL; a configuração [git_transport] mode controla isso (veja a referência de configuração). URLs com token nunca são escritas no .git/config do seu repositório e são ocultadas de toda saída.
Todas as chamadas de API do GitHub passam por gh api via subprocess. Isso mantém a autenticação inteiramente na CLI gh — sem código de gerenciamento de token, sem fluxo OAuth, sem fixação de versão do PyGithub. Corpos de requisição JSON são passados via --input - (stdin), não por flags --field.
git clone https://github.com/your-username/gh-safe-repo cd gh-safe-repo uv sync # creates .venv, installs pytest
uv run pytest tests/ -v
./gh-safe-repo create <owner/repo> --dry-run
uv tool install .
Consulte [`tests/README.md`](https://github.com/ariesq/gh-safe-repo/blob/HEAD/tests/README.md) para descrições de arquivos de teste, convenções de mocking e como adicionar novos testes.
### Estrutura do projeto```
gh-safe-repo/
├── gh-safe-repo # Thin launcher (entry point for direct use)
├── gh_safe_repo/ # Package — see gh_safe_repo/README.md for internals
│ ├── cli.py # Subparser dispatch (create, fix, scan)
│ ├── commands/ # Subcommand implementations
│ │ ├── _common.py # Shared helpers, CLIContext, plan formatting
│ │ ├── create.py # create subcommand
│ │ ├── fix.py # fix subcommand
│ │ └── scan.py # scan subcommand
│ └── plugins/ # Settings plugins (one per category)
├── pyproject.toml # Build config, entry points
├── gh-safe-repo.ini.example # Fully annotated example config
└── tests/
Consulte gh_safe_repo/README.md para o mapa de módulos, arquitetura de plugins e um guia para adicionar novas configurações.
Não há dependências de tempo de execução. Tudo usa a biblioteca padrão do Python (argparse, configparser, subprocess, json, re). Não adicione pacotes de terceiros sem discussão.
pytest é a única dependência de desenvolvimento, declarada como uma entrada [dependency-groups] nativa do UV em pyproject.toml.
Estes projetos foram estudados durante o design e influenciaram a arquitetura do gh-safe-repo. São ferramentas distintas com escopos e modelos de usuário diferentes — consulte docs/LEARNINGS.md para notas técnicas detalhadas sobre como os padrões foram adaptados.
github/safe-settings — Aplicativo GitHub de nível organizacional (Node.js/Probot) que aplica configurações de repositório a partir de uma configuração central. Fonte do padrão de arquitetura de plugins (uma classe por categoria de configuração, fetch → diff → apply) e da abordagem de comparação mergeDeep.
repository-settings/app — Variante mais simples por repositório do safe-settings, também em Node.js/Probot. Forneceu uma referência mais limpa para o padrão de plugin base Diffable.
nicholasgasior/gh-repo-settings — Extensão CLI escrita em Go com um fluxo de trabalho plan/apply. Inspiração principal para o padrão de wrapper de subprocesso gh api e o design de saída do plano de simulação (dry-run).
| Opção | Descrição |
|---|
--public | Criar como repositório público (padrão: privado) |
--local PATH | Envia código de um repositório git local para o novo repositório. Executa verificação prévia primeiro. Mutuamente exclusivo com --from. |
--from OWNER/REPO | Espelha código de um repositório existente para o novo repositório. Executa verificação prévia. Mutuamente exclusivo com --local. |
--yes / -y | Pular prompt de confirmação e aplicar imediatamente (para uso em scripts/lotes) |
--dry-run | Exibe o plano sem fazer alterações |
--json | Emite o plano como JSON para stdout em vez da tabela ANSI |
--config [PATH] | Caminho para o arquivo de configuração; --config simples usa apenas os padrões incorporados |
--debug | Imprime cada chamada de API e resposta |
| Opção | Descrição |
|---|
--yes / -y | Pular prompt de confirmação e aplicar imediatamente (para uso em scripts/lotes) |
--dry-run | Mostra diff das configurações sem aplicar alterações |
--json | Emite o plano como JSON para stdout em vez da tabela ANSI |
--config [PATH] | Caminho para o arquivo de configuração; --config simples usa apenas os padrões incorporados |
--debug | Imprime cada chamada de API e resposta, além da identidade do repositório resolvida (id, nome completo, tipo de proprietário) |
| Ação | Significado |
|---|
ADD (verde) | Nova configuração sendo aplicada |
UPDATE (amarelo) | Configuração existente sendo alterada (modo de auditoria) |
DELETE (vermelho) | Configuração sendo removida |
SKIP (acinzentado) | Nenhuma ação necessária — já está no valor desejado, ou recurso indisponível na sua combinação de plano/visibilidade |
| Categoria | Severidade | Exemplos |
|---|
| Segredos codificados | Crítico | Chaves AWS (AKIA…), tokens GitHub (ghp_…, github_pat_…), chaves privadas, URLs de banco de dados |
| Strings banidas | Crítico | Quaisquer strings literais que você configurar (nomes de usuário, nomes de host internos, nomes de código) |
| Arquivos de contexto de IA | Crítico | CLAUDE.md, AGENTS.md, .cursorrules, copilot-instructions.md, .cursor/ — podem conter notas internas de desenvolvimento; o histórico do git pode ser mais sensível que a versão atual |
| Endereços de email | Aviso | Qualquer padrão [email protected] na árvore de trabalho e no histórico do git |
| Arquivos grandes | Aviso | Arquivos acima do limite de tamanho configurado (padrão: 100 MB) |
| Comentários TODO/FIXME | Informação | # TODO, # FIXME, # HACK, # XXX |