Configuración de Automatización de Repositorio
Prepara el stack completo de commits convencionales + release-please + automatización de CI en cualquier repositorio. Funciona en repositorios vacíos y con contenido existente.
Resumen
Desplazar a la izquierda (Shift Left): Detecta problemas lo antes posible. Un mensaje de commit incorrecto detectado en el PR cuesta segundos; el mismo mensaje contaminando las notas de release cuesta credibilidad. Mueve la validación hacia arriba — exige formato de commit en el título del PR, valida configuraciones en CI, detecta problemas antes de que lleguen a main.
Más rápido es más seguro: Lotes más pequeños y releases más frecuentes reducen el riesgo. Un release con 3 cambios es más fácil de depurar que uno con 30. Los releases automatizados generan confianza en el proceso mismo de release.
Esta habilidad configura el mecanismo de control: la validación del título del PR asegura que cada fusión a main produzca un commit significativo y analizable. Release-please convierte esos commits en releases automatizados con registros de cambios. CI detecta problemas de configuración y código antes de fusionar.
Cuándo Usarlo
- Al iniciar un nuevo repositorio y necesitar automatización de releases desde el primer día
- Repositorio existente sin versionado automatizado ni generación de changelog
- Migración desde procesos de release manuales u otras herramientas de release
- El repositorio necesita validación del título del PR y etiquetado automático
Cuándo NO Usarlo
- El repositorio ya tiene release-please o equivalente configurado (verificar primero)
- Monorepo con herramienta de release incompatible (ej. Lerna con versionado independiente ya funcionando)
- El repositorio es un fork donde el upstream controla el proceso de release
Proceso
Paso 1: Recopilar Configuración
Pregunta al usuario estos valores antes de generar cualquier archivo:
| Parámetro | Valor por defecto | Descripción |
|---|---|---|
PROJECT_NAME | (obligatorio) | Nombre legible del proyecto para la documentación |
INITIAL_VERSION | 0.1.0 | Versión semántica inicial |
RELEASE_TYPE | simple | simple (paquete único) o node / python / etc. |
REPO_LAYOUT | single | single (un paquete en la raíz) o monorepo (múltiples paquetes) |
PACKAGE_PATHS | . | Para monorepo: rutas separadas por comas como packages/api,packages/ui |
DEPENDABOT_ECOSYSTEMS | github-actions | Separados por comas: github-actions, npm, pip, cargo, etc. |
DEFAULT_BRANCH | main | Rama que desencadena los releases |
Detectar el RELEASE_TYPE correcto automáticamente:
package.jsonexiste → sugerirnodesetup.pyopyproject.tomlexiste → sugerirpythonCargo.tomlexiste → sugerirrust- Ninguno de los anteriores → sugerir
simple
Paso 2: Detectar Archivos Existentes
Antes de crear nada, verifica conflictos:
for f in .github/workflows/pull-request.yml .github/workflows/main-branch.yml \
.github/release-please-config.json .release-please-manifest.json \
.github/dependabot.yml .github/pull_request_template.md docs/DEVELOPMENT.md; do
[[ -f "$f" ]] && echo "EXISTE: $f"
done
Si existen archivos, muestra un diff de lo que cambiaría y pregunta antes de sobrescribir. No sobrescribas en silencio — los archivos existentes pueden contener personalizaciones específicas del proyecto.
Paso 3: Crear Archivos de Configuración
Crea los archivos en este orden (las dependencias fluyen de arriba a abajo):
3a. .github/release-please-config.json
Paquete único:
{
"release-type": "{{RELEASE_TYPE}}",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": false,
"initial-version": "{{INITIAL_VERSION}}",
"packages": {
".": {}
}
}
Monorepo: Reemplazar packages con una entrada por ruta:
{
"packages": {
"packages/api": { "release-type": "node" },
"packages/ui": { "release-type": "node" }
}
}
Cada paquete del monorepo tiene su propio release-type: pregunta al usuario por paquete si difieren.
3b. .release-please-manifest.json
Paquete único:
{
".": "{{INITIAL_VERSION}}"
}
Monorepo: Una entrada por ruta de paquete, cada una con su propia versión.
3c. .github/workflows/pull-request.yml
name: Validación de Pull Request
on:
pull_request:
types: [opened, edited, synchronize, reopened]
permissions:
pull-requests: write
contents: read
statuses: write
jobs:
validate-pr-title:
name: Validar Título del PR
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
types: |
feat
fix
docs
chore
refactor
test
ci
requireScope: false
subjectPattern: ^[a-z].+$
subjectPatternError: |
El asunto "{subject}" encontrado en el título del pull request "{title}"
no coincide con el patrón configurado. Asegúrate de que el asunto
comience con un carácter en minúscula (según commits convencionales).
auto-label:
name: Etiquetar PR Automáticamente
runs-on: ubuntu-latest
steps:
- name: Etiquetar según título del PR
uses: actions/github-script@v8
with:
script: |
const title = context.payload.pull_request.title;
const labelMap = {
'feat': 'enhancement',
'fix': 'bug',
'docs': 'documentation',
'chore': 'chore',
'refactor': 'refactor',
'test': 'test',
'ci': 'ci'
};
for (const [type, label] of Object.entries(labelMap)) {
if (title.startsWith(`${type}:`) || title.startsWith(`${type}(`)) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: [label]
});
break;
}
}
no-ai-attribution:
name: Sin Atribución de IA
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Escanear mensajes de commit y cuerpo del PR
env:
PR_BODY: ${{ github.event.pull_request.body }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
# Solo formas de trailer/pie de página: un commit que simplemente mencione Claude o
# Anthropic en prosa está bien. Los trailers Co-authored-by humanos están bien.
PAT='^[[:space:]]*Co-authored-by:.*(Claude|Anthropic|noreply@anthropic)|Generated with \[?(Claude|Anthropic)'
fail=0
if git log --format='%B' "$BASE_SHA..$HEAD_SHA" | grep -Eiq "$PAT"; then
echo "::error::Se encontró atribución de IA en un mensaje de commit en esta rama"
fail=1
fi
if printf '%s' "$PR_BODY" | grep -Eiq "$PAT"; then
echo "::error::Se encontró atribución de IA en la descripción del PR"
fail=1
fi
[ "$fail" -eq 0 ] && echo "No se encontró atribución de IA."
exit $fail
ci-checks:
name: Verificaciones de CI
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v6
- name: Validar sintaxis YAML
run: |
sudo apt-get update && sudo apt-get install -y yamllint
find . -name "*.yml" -o -name "*.yaml" | while read file; do
echo "Comprobando $file"
yamllint -d "{extends: default, rules: {line-length: disable}}" "$file" || true
done
Sin Atribución de IA viene bloqueante desde el día uno — no tiene superficie de falsos positivos que validar (coincide con formas de trailer y pie de página, no con prosa), por lo que no sigue el camino de graduación siguiente.
Camino de graduación de verificaciones de CI:
- Desplegar con
continue-on-error: true(informativo, no bloqueante) - Validar que las verificaciones pasen en 3-5 PRs reales
- Eliminar
continue-on-errory agregar a las verificaciones requeridas de protección de rama - Agregar verificaciones específicas del proyecto (lint, test, build) a medida que crece la base de código
Ninguna puerta puede saltarse. Si lint falla, corrige lint — no deshabilites la regla. Si una prueba falla, corrige el código — no te saltes la prueba. Cuando CI falle, pasa la salida del fallo a tu agente y corrígelo antes de volver a subir.
3d. .github/workflows/main-branch.yml
name: Automatización de Release
on:
push:
branches:
- {{DEFAULT_BRANCH}}
permissions:
contents: write
pull-requests: write
jobs:
release-please:
name: Release Please
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
config-file: .github/release-please-config.json
manifest-file: .release-please-manifest.json
update-major-tag:
name: Actualizar Etiqueta de Versión Mayor
needs: release-please
if: ${{ needs.release-please.outputs.release_created == 'true' }}
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
with:
ref: ${{ needs.release-please.outputs.tag_name }}
- name: Actualizar etiqueta de versión mayor
env:
TAG_NAME: ${{ needs.release-please.outputs.tag_name }}
run: |
MAJOR_TAG=$(echo "$TAG_NAME" | sed 's/^\(v[0-9]*\).*/\1/')
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -fa "$MAJOR_TAG" -m "Actualizar etiqueta $MAJOR_TAG a $TAG_NAME"
git push origin "$MAJOR_TAG" --force
3e. .github/dependabot.yml
Generar una entrada updates por ecosistema:
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
labels:
- "dependencies"
commit-message:
prefix: "chore"
include: "scope"
La configuración de commit-message asegura que los PRs de Dependabot cumplan con los commits convencionales automáticamente (ej. chore(deps): bump actions/checkout from v5 to v6).
3f. .github/pull_request_template.md
## Qué cambió
[Describe los cambios en detalle - esto irá al changelog]
## Por qué
[Contexto y motivación para el cambio]
## Lista de verificación
- [ ] El título del PR sigue el formato de commits convencionales (`tipo: descripción`)
- [ ] La descripción explica qué y por qué (no solo qué)
- [ ] No hay secretos en código, registros ni control de versiones
- [ ] Las pruebas pasan localmente
La sección "Por qué" es crítica — las descripciones de PR que solo dicen qué cambió son redundantes con el diff. El por qué proporciona contexto que hace útil al git-blame en el futuro.
3g. .gitignore (si falta)
Verifica si existe un .gitignore. Si no, crea uno apropiado para el proyecto:
# Dependencias
node_modules/
# Salida de compilación
dist/
build/
.next/
# Entorno
.env
.env.local
*.pem
# IDE
.idea/
# SO
.DS_Store
Thumbs.db
Adapta al stack detectado. Nunca subas node_modules/, .env ni artefactos de compilación.
3h. docs/DEVELOPMENT.md
Genera una guía de desarrollo que cubra:
- Flujo de trabajo de ramas — Desarrollo basado en tronco: ramas de características de corta duración (1-3 días), fusionar rápidamente. Nombres de rama:
feature/<desc>,fix/<desc>,chore/<desc>. - Disciplina de commits — Commits libres dentro de las ramas (cualquier formato). Solo importa el título del PR — se convierte en el mensaje de commit de squash-merge en main y aparece en el changelog.
- Formato del título del PR — Tabla de tipos:
feat,fix,docs,chore,refactor,test,ci. Cambios disruptivos:feat!:oBREAKING CHANGE:en el cuerpo. - Tamaño de los cambios — Apuntar a ~100 líneas por PR. ~300 aceptable para cambios lógicos únicos. ~1000+ deben dividirse. Separar refactorización de trabajo de características — son cambios diferentes.
- Descripciones de PR — Primera línea: corta, imperativa, independiente. Cuerpo: qué cambia y por qué. Anti-patrones: "Corregir error", "Actualizar archivo", "Fase 1".
- Verificaciones automáticas — Validación del título del PR (obligatorio, bloquea fusión), etiquetado automático (informativo), verificaciones de CI (inicialmente no bloqueantes, graduadas a obligatorias).
- Proceso de release — release-please crea PR de release → fusionar → release de GitHub + etiquetas + changelog. Progresión de versiones: pre-1.0 (
feat→ minor,fix→ patch) vs post-1.0 (feat→ minor,fix→ patch,BREAKING CHANGE→ major). - Solución de problemas — Problemas comunes: falla de validación del título del PR (editar título), fallos de verificaciones de CI, fusión bloqueada (verificar estados requeridos).
Usa {{PROJECT_NAME}} en los encabezados.
Paso 4: Recomendaciones de Protección de Rama
Después de la creación de archivos, presenta estos pasos como acciones manuales (no auto-aplicadas):
Configuración recomendada de protección de rama en GitHub para {{DEFAULT_BRANCH}}:
✓ Requerir pull request antes de fusionar
✓ Requerir verificaciones de estado: "Validar Título del PR"
✓ Requerir que las ramas estén actualizadas
✓ Permitir solo fusión por squash
✓ Requerir historial lineal
Por qué fusión por squash: El historial de la rama (commits WIP, correcciones, experimentos) se conserva en el PR. Main obtiene un registro limpio, significativo y lineal donde cada commit es un commit convencional. Esto es lo que hace útiles a release-please y a git log --oneline.
Paso 5: Habilitar Permisos del Workflow
Release-please necesita permiso para crear pull requests a través de GitHub Actions. Esto está deshabilitado por defecto en repositorios nuevos y hará que el workflow de Automatización de Release falle con:
GitHub Actions no tiene permitido crear o aprobar pull requests.
Indica al usuario que habilite esta configuración:
Repositorio de GitHub → Configuración → Actions → General → Permisos del workflow:
✓ Permisos de lectura y escritura (necesarios para que release-please pueda subir commits de changelog)
✓ Permitir que GitHub Actions cree y apruebe pull requests (necesario para los PRs de release)
Este paso debe completarse antes del primer push a {{DEFAULT_BRANCH}}, o release-please fallará en cada push hasta que se habilite.
Paso 6: Verificar
# Validar sintaxis YAML
yamllint .github/workflows/*.yml .github/dependabot.yml
# Validar JSON
python3 -c "import json; json.load(open('.github/release-please-config.json')); print('release-please-config.json: OK')" && python3 -c "import json; json.load(open('.release-please-manifest.json')); print('.release-please-manifest.json: OK')"
# Validar plantilla de PR tiene contenido
if [ -s .github/pull_request_template.md ]; then echo "Plantilla de PR: OK"; else echo "ERROR: Plantilla de PR está vacía"; fi
# Validar docs/DEVELOPMENT.md tiene contenido
if [ -s docs/DEVELOPMENT.md ]; then echo "Guía de desarrollo: OK"; else echo "ERROR: docs/DEVELOPMENT.md está vacío"; fi
# Validar .gitignore existe
[ -f .gitignore ] && echo ".gitignore: OK" || echo "NOTA: .gitignore no encontrado — se recomienda crearlo"
Paso 7: Primer Commit y Push
Después de la verificación, realiza el commit y push:
git add .github/ .release-please-manifest.json docs/ .gitignore
git commit -m "chore: configurar automatización de repositorio (commits convencionales + release-please + CI)"
git push origin {{DEFAULT_BRANCH}}
Luego indica al usuario:
"He creado y subido la configuración. Antes de abrir el primer PR, ve a la configuración de Actions como se describe en el Paso 5 para habilitar los permisos. Luego verifica que el workflow de CI se ejecute correctamente en la rama
{{DEFAULT_BRANCH}}. Una vez que los workflows estén funcionando, ya puedes crear tu primera rama de características y abrir un PR usando el flujo descrito en la guía de desarrollo."
Notas de Implementación
Seguridad y Lógica Correcta
- Nunca subir secretos: .gitignore incluye
.env,.env.local,*.pem. Nunca incluir rutas comoconfig/credentials.json. - Validar dependencias: Revisar cada dependencia (acciones, paquetes npm) por problemas de seguridad conocidos antes de agregarlas.
- Nunca ejecutar
git push --forceen ramas compartidas: Solo está permitido en la acciónupdate-major-tag, que sube una etiqueta independiente a un solo commit inmutable. - Revisión humana requerida: No fusionar PRs sin revisión, excepto para correcciones urgentes de seguridad o CI siguiendo un proceso documentado.
- Manejar errores correctamente: Cuando las verificaciones fallen, leer los registros de salida y corregir los problemas subyacentes — no deshabilitar verificaciones ni ignorar salidas.
Principios de Diseño de Skills
- Una sola habilidad, una sola responsabilidad: Esta habilidad se enfoca únicamente en la configuración de automatización de repositorio. No maneja configuración de tests, linters, frameworks de compilación ni otros aspectos de configuración del proyecto.
- Error en el lado de la precaución: Cuando no estés seguro sobre una configuración o archivo existente, pregunta al usuario en lugar de hacer suposiciones.
- Comportamiento determinista: Dados los mismos parámetros de entrada y estado del repositorio, la habilidad siempre debe producir la misma configuración.
Reversibilidad y Limpieza
Si el usuario decide deshacer la configuración:
rm -rf .github/workflows/pull-request.yml .github/workflows/main-branch.yml \
.github/release-please-config.json .release-please-manifest.json \
.github/dependabot.yml .github/pull_request_template.md docs/DEVELOPMENT.md
Esto elimina todos los archivos creados por la configuración. El historial de git permanece intacto — los PRs de release creados por release-please no se ven afectados.
Limitaciones y Planes Futuros
- Soporte de monorepo es básico — cada paquete recibe release-please independiente, pero no hay manejo de dependencias entre paquetes ni integración con Lerna/Nx/Turborepo.
- No hay configuración de despliegue — esta habilidad configura la automatización de versionado y CI, pero no el despliegue a entornos de producción.
- Solo GitHub Actions — no hay soporte para GitLab CI, CircleCI u otras plataformas CI.