
CLI de Python que crea repositorios de GitHub con valores predeterminados seguros: protección de ramas, Dependabot, escaneo de secretos y escaneo de seguridad previo al vuelo, aplicados automáticamente.
Crea repositorios de GitHub con configuraciones seguras aplicadas automáticamente. Reemplaza la lista de verificación de configuración posterior a la creación de cinco minutos con un solo comando.``` gh-safe-repo create <owner/repo>
Protección de ramas, etiquetas inmutables, Dependabot, permisos restringidos de Actions, escaneo de secretos con protección contra push, y wiki y proyectos deshabilitados — todo configurado antes de escribir tu primera línea de código.
gh-safe-repo está en pleno desarrollo. Funciona bien para el caso de uso de crear un nuevo repositorio con valores seguros predeterminados. Estoy trabajando en pulir las opciones de CLI para alinearlas mejor con las expectativas de los usuarios. Espera cambios importantes hasta que lleguemos a un punto en el que publique versiones y tenga CI/CD bien definido. ✌️
---
## Tabla de Contenidos
- [Por qué](#por-qué)
- [Qué cambia](#qué-cambia)
- [Requisitos](#requisitos)
- [Instalación](#instalación)
- [Inicio rápido](#inicio-rápido)
- [Referencia de CLI](#referencia-de-cli)
- [Ejecución en seco / Salida del plan](#ejecución-en-seco--salida-del-plan)
- [Modo de corrección (auditar repositorios existentes)](#modo-de-corrección-auditar-repositorios-existentes)
- [Clonar repositorios (`--from`)](#clonar-repositorios---from)
- [Crear un repositorio desde un directorio local (`--local`)](#crear-un-repositorio-desde-un-directorio-local---local)
- [Escáner de seguridad prevuelo](#escáner-de-seguridad-prevuelo)
- [Escaneo independiente](#escaneo-independiente)
- [Suprimir falsos positivos](#suprimir-falsos-positivos)
- [Configuración](#configuración)
- [Limitaciones del plan de GitHub](#limitaciones-del-plan-de-github)
- [Cómo funciona](#cómo-funciona)
- [Desarrollo](#desarrollo)
---
## Por qué
La configuración predeterminada de los repositorios de GitHub está optimizada para la detectabilidad y la flexibilidad, no para la seguridad. Cada repositorio nuevo viene con:
- Wiki y Proyectos habilitados (superficie de ataque, incluso si no se usan)
- Confirmaciones de fusión permitidas (historial desordenado, pero no es la preocupación principal)
- Sin protección de ramas (cualquiera con acceso de escritura puede hacer push directamente a `main`)
- Sin alertas de Dependabot
- GitHub Actions con permisos de escritura en el repositorio
- Actions que pueden aprobar solicitudes de extracción
Arreglar todo esto manualmente toma minutos por repositorio y es fácil de olvidar. `gh-safe-repo` aplica un conjunto de valores predeterminados prácticos y opinados de una sola vez, con una vista previa del plan para que sepas exactamente qué cambiará antes de que ocurra.
---
## Qué cambia
### Configuración del repositorio
| Configuración | Predeterminado de GitHub | Predeterminado seguro | Notas |
|---|---|---|---|
| Visibilidad | Público | **Privado** | Usa `--public` para anular |
| Wiki | Habilitado | **Deshabilitado** | |
| Proyectos | Habilitado | **Deshabilitado** | |
| Issues | Habilitado | Habilitado | |
| Eliminar rama al fusionar | Desactivado | Desactivado | Configurar a `true` en la configuración para limpieza automática |
| Permitir confirmaciones de fusión | Activado | Activado | Configurar a `false` en la configuración para solo squash |
| Permitir squash merge | Activado | Activado | |
| Permitir rebase merge | Activado | Activado | |
### GitHub Actions
| Configuración | Predeterminado de GitHub | Predeterminado seguro |
|---|---|---|
| Actions permitidas | Todas | **Seleccionadas** (propiedad de GitHub + creadores verificados; personalizable) |
| Permisos de flujo de trabajo predeterminados | Lectura/escritura | **Solo lectura** |
| Actions pueden aprobar PRs | Sí | **No** |
| Exigir anclaje SHA | No | **Sí** (los flujos de trabajo deben anclar acciones a un SHA de confirmación, no a una etiqueta mutable) |
| Política de aprobación de PRs de bifurcación | Nuevos contribuyentes, nuevos en GitHub | **Todos los contribuyentes externos** — requiere aprobación antes de que los flujos de trabajo de PRs bifurcadas ejecuten CI. Opciones: solo cuentas nuevas de GitHub (predeterminado de GitHub), primeros contribuyentes del repositorio, o todos los PRs bifurcados (más seguro) |
### Protección de ramas (repositorios públicos, o cualquier repositorio en un plan de pago)
| Regla | Valor |
|---|---|
| Requerir solicitud de extracción antes de fusionar | Sí |
| Revisiones de aprobación requeridas | 1 |
| Descartar revisiones obsoletas al hacer push | Sí |
| Requerir resolución de conversación | Sí |
| Permitir forzar push | No |
| Permitir eliminación de rama | No |
| Aplicar a administradores | No (permite que las herramientas del propietario hagan push) |
La protección de ramas se aplica mediante la **API de Rulesets** por defecto (`use_rulesets = true`): un único conjunto de reglas `gh-safe-repo defaults` cubre cada rama configurada y expresa "los administradores pueden omitir" a través de un actor de omisión en lugar de la bandera clásica `enforce_admins`. Configura `use_rulesets = false` para la ruta clásica por rama (mantenida por un ciclo de lanzamiento).
**Migrar un repositorio existente desde protección clásica:** si `fix` encuentra protección de rama clásica en un repositorio, se niega a convertirla en un conjunto de reglas a menos que pases `--migrate-branch-protection`. Las reglas solo clásicas no tienen equivalente en el conjunto de reglas que esta herramienta construye y se eliminarían silenciosamente de lo contrario — brechas conocidas:
- `required_status_checks` — las comprobaciones de CI requeridas no se modelan en el cuerpo del ruleset.
- `restrictions` (restricciones de push por usuario/equipo) — Rulesets modela esto de manera diferente mediante actores de omisión; no es un mapeo 1:1.
- Divergencia por rama — un ruleset de condición compartida única no puede expresar reglas diferentes para `master` vs `main`.
Con la bandera, `fix` crea/actualiza el ruleset y luego elimina la protección clásica en cada rama para que las dos capas no se acumulen.
### Protección de etiquetas (repositorios públicos, o cualquier repositorio en un plan de pago)
La protección de etiquetas crea un Ruleset de GitHub dirigido a todas las etiquetas (`*` por defecto, configurable mediante `protected_tags`). Se aplican las siguientes reglas:
| Regla del ruleset | ¿Aplicada? | Notas |
|---|---|---|
| Restringir creaciones | No | |
| **Restringir actualizaciones** | **Sí** | Evita sobrescribir / forzar push de etiquetas |
| **Restringir eliminaciones** | **Sí** | Evita `git push --delete` de etiquetas |
| Requerir historial lineal | No | |
| Requerir que los despliegues tengan éxito | No | |
| Requerir confirmaciones firmadas | No | |
| Requerir que las comprobaciones de estado pasen | No | |
| Bloquear forzar push | No | |
Los administradores del repositorio están en la lista de omisión (consistente con el valor predeterminado `enforce_admins = false` de la protección de rama). Solo funciona en repositorios públicos o planes de pago de GitHub (misma restricción que la protección de rama). Los repositorios privados de plan gratuito verán esto omitido en la salida del plan.
### Seguridad
| Característica | Comportamiento |
|---|---|
| Alertas de Dependabot | Habilitado (repositorios públicos / planes de pago) |
| Actualizaciones de seguridad de Dependabot | Habilitado (abre automáticamente PRs para dependencias vulnerables) |
| Escaneo de secretos | Automático en repositorios públicos; habilitado en planes privados de pago |
| Protección contra push | Habilitado (bloquea confirmaciones que contengan secretos compatibles) |
| Informe de vulnerabilidad privado | Habilitado (permite que investigadores de seguridad informen de forma privada) |
| Gráfico de dependencias | Automático en repositorios públicos; sin API REST para privados (solo interfaz) |
---
## Requisitos
- Python 3.8+
- [`gh` CLI](https://cli.github.com/) instalada y autenticada (`gh auth login`), **o** `GITHUB_TOKEN` configurada en tu entorno
- Para `--local` / `--from` (que hacen push o clonan código): tus credenciales git habituales deben estar configuradas — ya sea una clave SSH cargada en `ssh-agent` (cuando `gh config get git_protocol` es `ssh`) o un helper de credenciales HTTPS (`gh auth setup-git` configura uno automáticamente). El token OAuth **no** se usa para git push, por lo que los archivos de flujo de trabajo (`.github/workflows/*`) se envían sin necesidad del alcance `workflow` de OAuth.
- [`uv`](https://docs.astral.sh/uv/) para instalación desde fuente (recomendado)
- `truffleHog` v3 (opcional — usado por el escáner prevuelo; se detecta automáticamente desde PATH, o se ejecuta mediante podman/docker; recurre a regex si ninguno está disponible)
---
## Instalación
### Desde fuente con uv (recomendado)```bash
git clone https://github.com/your-username/gh-safe-repo
cd gh-safe-repo
uv tool install .
Esto instala gh-safe-repo en el entorno de herramientas de uv y lo agrega a tu 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
## Referencia de CLI```
gh-safe-repo create <owner/repo> [OPTIONS]
gh-safe-repo fix <owner/repo> [OPTIONS]
gh-safe-repo scan <path> [OPTIONS]
Todos los comandos que interactúan con GitHub requieren el formato owner/repo (por ejemplo, myuser/my-repo). Para create, el propietario se valida contra tu cuenta autenticada de GitHub para evitar errores en sistemas con múltiples cuentas. Para fix, se requieren permisos de administrador en el repositorio objetivo, lo que te permite arreglar repositorios propiedad de organizaciones u otras cuentas donde tengas acceso de administrador.
create — Crear un nuevo repositorioUn create simple (sin --local/--from) inicializa el repositorio para que exista una rama predeterminada para la protección de ramas, luego elimina el README.md generado automáticamente para que el nuevo repositorio comience limpio. Establece auto_init = true en la configuración para conservar el README en su lugar. --local/--from envían tu propio historial y nunca crean un README.
fix — Auditar y corregir un repositorio existentescan — Escaneo local de secretos| Option | Description |
|---|---|
--config [PATH] | Ruta al archivo de configuración; --config sin argumentos usa solo los valores predeterminados integrados |
--debug | Muestra los detalles del escáner |
El código de salida es 0 si no se encuentran hallazgos críticos, 1 si se encuentran críticos.
--dry-run muestra exactamente lo que gh-safe-repo haría, sin realizar ningún cambio ni llamadas a la API. Úsalo antes de ejecutar realmente. Combínalo con --json para obtener una salida del plan legible por máquina:```bash
gh-safe-repo create <owner/repo> --dry-run --json
gh-safe-repo fix <owner/repo> --dry-run --json
Cuando `--json` está activo, el plan se escribe en stdout como un objeto JSON y todos los demás mensajes (progreso, advertencias, el pie de página "Dry run") van a stderr, para que la salida esté limpia para tuberías o scripts.```
$ 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.
Colores de las acciones:
Salida 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` solo incluye los tipos que están presentes en el plan. Los consumidores deben usar `.get("delete", 0)` etc. en lugar de asumir que las cuatro claves están presentes.
---
## Modo de Corrección (Auditar Repositorios Existentes)
`fix` compara los ajustes actuales de un repositorio existente con los valores predeterminados seguros y aplica las correcciones necesarias. Sin escaneo de secretos — `fix` se ocupa únicamente de los ajustes del repositorio.```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 corrección:
UPDATE para ajustes modificados y SKIP para ajustes que ya tienen el valor deseado (detección sin operación — nunca realiza llamadas a la API que no cambiarían nada)--yes)Solo se aplican cambios reales — los ajustes que ya tienen el valor deseado se muestran como SKIP y no generan llamadas a la API.
--from)--from crea un espejo de un repositorio existente en uno nuevo con valores seguros predeterminados. Funciona tanto para destinos privados como públicos.```bash
gh-safe-repo create <owner/repo> --from <owner/source>
gh-safe-repo create <owner/pub> --from <owner/priv> --public
**Lo que sucede, en orden:**
1. Tus credenciales de git para `github.com` se verifican de antemano (sonda SSH cuando `gh config get git_protocol` es `ssh`; HTTPS es confiable), por lo que una clave faltante falla rápidamente antes de que se cree cualquier repositorio
2. El repositorio fuente se clona localmente (clon completo, sin `--depth`, para que truffleHog pueda recorrer todo el historial de confirmaciones)
3. El [escáner de seguridad previo al vuelo](#pre-flight-security-scanner) se ejecuta en el clon local
4. Revisas los hallazgos y confirmas (o cancelas)
5. Se crea un nuevo repositorio (privado por defecto, o público con `--public`)
6. Se aplican permisos de Actions y configuraciones de seguridad (Dependabot, escaneo de secretos, protección de push)
7. El historial completo se refleja: `git clone --mirror` + `git push --mirror`
8. Se aplica protección de ramas y etiquetas (después del push de código, para que la rama de destino exista)
Si el escáner revela un problema y cancelas, nunca se copia código a GitHub.
> **Nota:** `--from` usa el formato `owner/repo` tanto para el origen como para el destino.
---
## Crear un Repositorio desde un Directorio Local (`--local`)
`--local PATH` es la contraparte local a GitHub de `--from`. Crea un nuevo repositorio de GitHub y envía código desde un repositorio git local. `PATH` debe ser un repositorio git inicializado (`git init` o un clon).```bash
gh-safe-repo create <owner/repo> --local ~/projects/myapp
gh-safe-repo create <owner/repo> --local ~/projects/myapp --public
Qué sucede, en orden:
github.com se verifican de antemano (sondeo SSH cuando gh config get git_protocol es ssh; HTTPS es confiable), por lo que una clave faltante falla rápidamente antes de que se cree cualquier repositorio.push --all --tags (todas las ramas y etiquetas)origin al repositorio local original apuntando a la nueva URL de GitHub, y se configura el seguimiento ascendente de la rama actual — así que git push y git pull funcionan inmediatamente sin configuración adicional.Tanto --local como --from funcionan para repositorios privados y públicos. Son mutuamente excluyentes.
La rama predeterminada local (a través de git -C PATH symbolic-ref HEAD) se usa para dirigir las reglas de protección de ramas, de modo que la protección se aplique en la rama correcta incluso si no es main.
Consejo: Ejecuta
gh-safe-repo scan PATHprimero si quieres inspeccionar los hallazgos sin crear nada.
El escáner se ejecuta localmente y nunca envía código a GitHub. Úsalo de forma independiente antes de cualquier push, o se ejecuta automáticamente como parte de los flujos de trabajo --from y --local.
gh-safe-repo scan .
gh-safe-repo scan ~/projects/myapp
El código de salida es `0` si no se encuentran hallazgos críticos, `1` si se encuentran críticos — por lo que se compone limpiamente con otros comandos:```bash
gh-safe-repo scan . && git push
La configuración completa de [pre_flight_scan] aplica: banned_strings, max_file_size_mb, trufflehog_mode, etc.
gh-safe-repo elige automáticamente el mejor escáner disponible mediante una cadena de descubrimiento de tres pasos:
trufflehog --version, verifica que sea v3 y lo utiliza. Una instalación v2 o una versión no reconocida imprime una advertencia y pasa al paso 2.ghcr.io/trufflesecurity/trufflehog:latest) usando podman run o docker run, montando la ruta de escaneo en modo solo lectura en la misma ruta absoluta para que las rutas de salida JSON sean idénticas a una ejecución nativa.El escáner seleccionado se muestra en el encabezado "Running pre-flight security scan..." y en la entrada SCAN de la tabla del plan, por ejemplo:``` 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)
Variables de entorno que respeta la ruta del contenedor: `CONTAINER_RUNTIME` para anular la selección del runtime (por ejemplo, `CONTAINER_RUNTIME=docker`) y `TRUFFLEHOG_IMAGE` para fijar una etiqueta de imagen específica.
### Ejecutar truffleHog mediante podman o Docker (sin instalación local)
No se requiere configuración manual. `gh-safe-repo` detecta automáticamente podman o docker (paso 2 anterior) y ejecuta truffleHog en un contenedor con los montajes de volumen correctos. Se respetan las variables de entorno `CONTAINER_RUNTIME` y `TRUFFLEHOG_IMAGE`.
Se proporcionan un envoltorio de shell (`tools/trufflehog`) y un `Containerfile` para construir una imagen local fija en [`tools/`](https://github.com/ariesq/gh-safe-repo/blob/HEAD/tools/README.md) para usuarios que deseen tener truffleHog basado en contenedor disponible en todo el sistema, o que necesiten una imagen aislada.
### Revisión interactiva```
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). Debes escribir explícitamente y para continuar.Y). Presiona Enter para proceder o escribe n para abortar.Los secretos se redactan en la salida. Las direcciones de correo electrónico y los TODOs muestran la línea correspondiente.
Los directorios de artefactos de compilación (node_modules, __pycache__, .venv, venv, dist, build) se omiten de forma predeterminada para mantener los escaneos rápidos. En repositorios git, esta omisión es condicional: antes de omitir un directorio, el escáner ejecuta git ls-files -- <dir> para verificar si hay archivos dentro que estén siendo rastreados. Si los hay, el directorio se escanea normalmente.
Esto significa que los árboles node_modules o dist confirmados (inusuales, pero ocurren) no se pasan por alto silenciosamente. Los directorios no confirmados (el caso normal) continúan omitiéndose como antes.
Todavía se imprime una advertencia cuando se encuentran subdirectorios SKIP_DIRS en un repositorio fuente clonado, ya que su presencia puede indicar que se ha confirmado más contenido del esperado.
Dos claves de configuración te permiten suprimir hallazgos conocidos como seguros sin deshabilitar categorías de verificación completas.
scan_exclude_paths — omitir archivos o directorios por completo. Los valores son patrones regex separados por nueva línea/comas que se comparan con la ruta de archivo relativa. Un archivo que coincide es excluido de todas las verificaciones: secretos, correos electrónicos, TODOs, archivos grandes y detección de archivos de contexto de IA. Los mismos patrones también se pasan a truffleHog a través de --exclude-paths, por lo que la cobertura es consistente independientemente del motor de escaneo activo.```ini
[pre_flight_scan]
scan_exclude_paths = docs/api.github.com.json tests/fixtures/
**`exclude_emails`** — suprime los hallazgos de correos electrónicos para direcciones específicas o dominios completos. Los valores están separados por nueva línea o coma, sin distinción entre mayúsculas y minúsculas. Las entradas que comienzan con `@` coinciden con todos los correos electrónicos de ese dominio; de lo contrario, la entrada debe coincidir exactamente con la dirección completa. Se aplica tanto a los hallazgos del árbol de trabajo como del historial de 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
Cuando se encuentran cadenas prohibidas o archivos de contexto de IA, el escáner imprime un comando `git filter-repo` listo para ejecutar, para eliminarlos del historial del repositorio de origen antes de volver a ejecutarlo.
---
## Configuración
`gh-safe-repo` busca la configuración en este orden (el primero que coincide gana):
1. **`--config RUTA`** — anulación explícita
2. **`./gh-safe-repo.ini`** — directorio de trabajo actual
3. **`$XDG_CONFIG_HOME/gh-safe-repo/gh-safe-repo.ini`** — predeterminado a `~/.config` cuando `$XDG_CONFIG_HOME` no está establecido
`--config` sin argumento (sin ruta) omite la búsqueda de archivo por completo y usa solo los valores predeterminados internos.
Todos los valores tienen valores predeterminados seguros: no se requiere un archivo de configuración para empezar.
En el repositorio se incluye un ejemplo de configuración completamente anotado como `gh-safe-repo.ini.example`. Cópialo para empezar:```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.---
## Limitaciones del Plan de GitHub
Algunas características solo están disponibles según la visibilidad del repositorio y tu plan de GitHub.
| Característica | Gratuito + Público | Gratuito + Privado | Pro/Equipo + Privado |
|---|:---:|:---:|:---:|
| Protección de ramas / Conjuntos de reglas | Sí | No | Sí |
| Protección de etiquetas (Conjuntos de reglas) | Sí | No | Sí |
| Alertas de Dependabot | Sí | No | Sí |
| Actualizaciones de seguridad de Dependabot | Sí | No | Sí |
| Escaneo de secretos | Auto | No | Sí |
| Protección contra envíos | Sí | No | Sí |
| Reporte privado de vulnerabilidades | Sí | Sí | Sí |
| Gráfico de dependencias | Auto | No | Sí |
`gh-safe-repo` detecta el nivel de tu plan y la visibilidad del repositorio en tiempo de ejecución. Las características no disponibles aparecen como `SKIP` en la salida del plan con una razón clara: la herramienta nunca falla en silencio.
---
## Cómo 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 categoría de configuración es una clase de plugin autocontenida (gh_safe_repo/plugins/). Cada plugin:
Plan (lista de objetos Change: ADD / UPDATE / DELETE / SKIP)Esto significa que el modo de auditoría y el modo de creación usan la misma ruta de plan/aplicación. La única diferencia es si el estado actual se obtiene de un repositorio existente o se asumen los valores predeterminados de GitHub.
Las llamadas a la API resuelven un token en este orden:
GITHUB_TOKEN variable de entorno — te permite apuntar a una cuenta específica sin cambiar la sesión activa de gh (y es la única credencial necesaria en CI)gh auth token — lo que haya configurado gh auth loginLos tokens se pasan a los procesos hijos de gh api como GH_TOKEN en el entorno del subproceso y nunca se registran.
Las operaciones de Git (--local / --from push y clone) usan tus propias credenciales de git — clave SSH o helper de credenciales — por defecto, no el token de API. En entornos sin ninguno (por ejemplo, CI con solo GITHUB_TOKEN), la herramienta recurre a hacer push sobre HTTPS con el token en la URL; la opción de configuración [git_transport] mode controla esto (consulte la referencia de configuración). Las URL con token nunca se escriben en .git/config de tu repositorio y se eliminan de toda la salida.
Todas las llamadas a la API de GitHub pasan por gh api a través de subprocess. Esto mantiene la autenticación completamente en la CLI de gh — sin código de gestión de tokens, sin flujo OAuth, sin fijación de versión de PyGithub. Los cuerpos de las solicitudes JSON se pasan mediante --input - (stdin), no con banderas --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 .
Vea [`tests/README.md`](https://github.com/ariesq/gh-safe-repo/blob/HEAD/tests/README.md) para descripciones de archivos de prueba, convenciones de simulación y cómo añadir nuevas pruebas.
### Estructura del proyecto```
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 el mapa de módulos, la arquitectura de complementos y una guía para agregar nuevas configuraciones.
No hay dependencias en tiempo de ejecución. Todo usa la biblioteca estándar de Python (argparse, configparser, subprocess, json, re). No agregue paquetes de terceros sin discusión.
pytest es la única dependencia de desarrollo, declarada como una entrada nativa de UV [dependency-groups] en pyproject.toml.
Estos proyectos se estudiaron durante el diseño e influyeron en la arquitectura de gh-safe-repo. Son herramientas distintas con diferentes alcances y modelos de usuario; consulte docs/LEARNINGS.md para obtener notas técnicas detalladas sobre cómo se adaptaron los patrones.
github/safe-settings — Aplicación de GitHub a nivel de organización (Node.js/Probot) que aplica configuraciones de repositorio desde una configuración central. Fuente del patrón de arquitectura de complementos (una clase por categoría de configuración, obtener → diferenciar → aplicar) y el enfoque de comparación mergeDeep.
repository-settings/app — Variante más simple por repositorio de safe-settings, también Node.js/Probot. Proporcionó una referencia más limpia para el patrón de complemento base Diffable.
nicholasgasior/gh-repo-settings — Extensión CLI escrita en Go con un flujo de trabajo plan/apply. Inspiración principal para el patrón de envoltorio de subprocesos gh api y el diseño de salida del plan de ejecución en seco.
| Option | Description |
|---|
--public | Crear como repositorio público (predeterminado: privado) |
--local PATH | Envía código desde un repositorio git local al nuevo repositorio. Primero ejecuta un escaneo previo. Mutuamente excluyente con --from. |
--from OWNER/REPO | Refleja código desde un repositorio existente al nuevo repositorio. Ejecuta un escaneo previo. Mutuamente excluyente con --local. |
--yes / -y | Omite el mensaje de confirmación y aplica inmediatamente (para uso en scripting/lotes) |
--dry-run | Imprime el plan sin realizar ningún cambio |
--json | Emite el plan como JSON a stdout en lugar de la tabla ANSI |
--config [PATH] | Ruta al archivo de configuración; --config sin argumentos usa solo los valores predeterminados integrados |
--debug | Imprime cada llamada y respuesta de API |
| Option | Description |
|---|
--yes / -y | Omite el mensaje de confirmación y aplica inmediatamente (para uso en scripting/lotes) |
--dry-run | Muestra el diff de configuración sin aplicar cambios |
--json | Emite el plan como JSON a stdout en lugar de la tabla ANSI |
--config [PATH] | Ruta al archivo de configuración; --config sin argumentos usa solo los valores predeterminados integrados |
--debug | Imprime cada llamada y respuesta de API, más la identidad del repositorio resuelta (id, nombre completo, tipo de propietario) |
| Acción |
|---|
| Significado |
|---|
ADD (verde) | Nuevo ajuste aplicándose |
UPDATE (amarillo) | Ajuste existente cambiándose (modo auditoría) |
DELETE (rojo) | Ajuste eliminándose |
SKIP (atenuado) | No se necesita acción — ya en el valor deseado, o función no disponible en su combinación de plan/visibilidad |
| Categoría | Gravedad | Ejemplos |
|---|
| Secretos hardcodeados | Crítica | Claves AWS (AKIA…), tokens de GitHub (ghp_…, github_pat_…), claves privadas, URLs de bases de datos |
| Strings prohibidos | Crítica | Cualquier cadena literal que configures (nombres de usuario, nombres de host internos, nombres en clave) |
| Archivos de contexto de IA | Crítica | CLAUDE.md, AGENTS.md, .cursorrules, copilot-instructions.md, .cursor/ — pueden contener notas internas de desarrollo; el historial de git puede ser más sensible que la versión actual |
| Direcciones de correo electrónico | Advertencia | Cualquier patrón [email protected] en el árbol de trabajo y el historial de git |
| Archivos grandes | Advertencia | Archivos por encima del umbral de tamaño configurado (por defecto: 100 MB) |
| Comentarios TODO/FIXME | Informativo | # TODO, # FIXME, # HACK, # XXX |