pre-commit gestiona hooks que revisan archivos antes de un commit. Detecta espacios finales, YAML inválido, formato y lint, pero no sustituye pruebas ni CI.

python -m pip install pre-commit
pre-commit install

Configura pocos hooks confiables en .pre-commit-config.yaml y fija cada rev. Comprueba versiones compatibles actuales en los repositorios oficiales.

repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: check-yaml
      - id: end-of-file-fixer
      - id: trailing-whitespace
pre-commit run --all-files

Algunos hooks modifican archivos; revisa el diff y repite la ejecución. La documentación oficial explica entornos aislados.

Añade las verificaciones de Ruff y ejecuta la misma política en CI. Trata pre-commit autoupdate como actualización de dependencias: revisa changelogs y resultados. Usa repositorios confiables, revisiones fijas y evita hooks lentos o dependientes de red.

Integrar Ruff sin duplicar funciones

Cada hook necesita una responsabilidad clara. Los hooks básicos pueden cuidar la higiene de archivos y Ruff puede encargarse del lint y del formato de Python. Ejecutar dos formateadores sobre el mismo archivo genera diffs ruidosos y conflictos.

  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.12.4
    hooks:
      - id: ruff-check
        args: [--fix]
      - id: ruff-format

La revisión es un ejemplo fijado, no una afirmación sobre la última versión. Comprueba el release actual y su compatibilidad. Centraliza las reglas en pyproject.toml para que editor, terminal y CI compartan la política:

[tool.ruff]
target-version = "py311"
line-length = 88

[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B"]

Las correcciones de lint deben ejecutarse antes del formato. Si un hook modifica un archivo, el commit se detiene para que revises y añadas el diff. Una segunda ejecución limpia confirma que las transformaciones son estables.

Limitar el alcance

pre-commit selecciona normalmente los archivos preparados. Usa files, exclude y types para omitir código generado, dependencias copiadas o fixtures que deban conservar espacios inválidos.

      - id: check-yaml
        exclude: ^tests/fixtures/invalid/
      - id: trailing-whitespace
        types: [text]

Investiga un fallo con pre-commit run check-yaml --all-files --verbose o archivos concretos mediante pre-commit run --files ruta/a.py ruta/b.py. No excluyas un directorio completo sin decidir antes si el fallo revela un defecto.

Reserva pre-commit para verificaciones rápidas. Una convención de mensajes puede usar commit-msg; las pruebas de integración lentas pertenecen a CI.

Adoptarlo en un repositorio existente

Instalar el hook no revisa el historial. La primera ejecución global puede modificar numerosos archivos. Haz esa limpieza en un commit aislado para no mezclarla con cambios funcionales.

python -m pip install -r requirements-dev.txt
pre-commit install --install-hooks
pre-commit run --all-files

Fija también la versión del paquete pre-commit en las dependencias de desarrollo. Como git commit --no-verify puede omitir el hook local, CI debe seguir siendo la autoridad. El bypass es una salida excepcional, no el flujo normal.

En un monorepo, guarda la configuración en la raíz de Git y filtra cada paquete. Si un comando requiere otro directorio de trabajo, crea un script explícito.

Hooks locales y seguridad

Un hook del propio repositorio puede declararse así:

  - repo: local
    hooks:
      - id: unit-tests-fast
        name: pruebas unitarias rápidas
        entry: python -m pytest -q tests/unit
        language: system
        pass_filenames: false

language: system usa el entorno activo y ofrece menos aislamiento. Úsalo cuando el proyecto ya controle esas dependencias. Si el comando acepta rutas, permite que pre-commit le pase nombres para conservar la ejecución incremental.

Los hooks ejecutan código con acceso al repositorio. Elige repositorios oficiales, fija revisiones y revisa cambios inesperados de origen o mantenimiento. No expongas secretos de despliegue a un hook general.

Aplicar la misma política en CI

La verificación obligatoria debe ejecutarse desde un checkout limpio:

python -m pre_commit run --all-files --show-diff-on-failure

Si un formateador cambia archivos, el job falla y muestra el diff sin crear commits. Si guardas entornos en caché, incluye la versión de Python y el hash de .pre-commit-config.yaml en la clave.

Conserva las pruebas en un paso distinto. Así se diferencia una infracción de formato o configuración de un fallo de comportamiento. La guía de GitHub Actions para Python explica la estructura de CI.

Actualizar y diagnosticar

Trata pre-commit autoupdate como una actualización de dependencias. Ejecútalo en una rama, lee las notas de versión, revisa el YAML y pasa todos los hooks y pruebas. Un commit separado facilita revertir el cambio.

Si algo falla solo en CI, compara Python, sistema operativo, locale, finales de línea y revisiones. pre-commit clean reconstruye entornos almacenados y pre-commit gc elimina los que ya no se usan.

Una configuración saludable es rápida, determinista y comprensible. Empieza por defectos con corrección objetiva, mide el coste local y deja las verificaciones caras para CI. Así el equipo obtiene feedback temprano sin convertir cada commit en una espera.

Lista de revisión práctica

Antes del merge, clona el repositorio en una carpeta limpia y sigue la instalación documentada. La ejecución completa debe pasar dos veces: la primera puede corregir archivos y la segunda debe quedar limpia. Crea temporalmente un YAML inválido y una infracción de Ruff para comprobar la selección.

Revisa cada repo, revisión fija y finalidad. Confirma que las exclusiones correspondan a fixtures o archivos generados reales, que ningún secreto aparezca en args y que los hooks locales funcionen desde la raíz. Compara el resultado con CI en el mismo commit.

Mide una ejecución cotidiana sobre archivos preparados. Si es lenta, identifica el hook costoso con salida detallada, limita su alcance o muévelo a CI. No ocultes el problema desactivando reglas importantes.

También conviene documentar cómo resolver los fallos habituales: añadir el diff corregido, ejecutar un hook aislado, recrear su entorno y revisar una actualización. Una guía breve evita que cada integrante invente un procedimiento distinto.

Designa responsable y frecuencia de mantenimiento. Las revisiones fijas aportan reproducibilidad, pero necesitan actualizaciones deliberadas para recibir correcciones.

Problemas frecuentes

Si aparece “command not found” al hacer commit, confirma que la instalación se realizó en el mismo repositorio y que el entorno de Python sigue disponible. pre-commit install escribe el script en .git/hooks; copiar solamente el YAML no instala nada para otros colaboradores.

Cuando un hook recibe rutas que no reconoce, revisa sus tipos y filtros con pre-commit run nombre --all-files --verbose. El problema puede ser una expresión regular anclada de forma incorrecta. Las rutas que evalúa pre-commit usan barras normales y son relativas a la raíz.

Si dos hooks alternan cambios en cada ejecución, existe un conflicto de formato o de orden. Elimina la responsabilidad duplicada y conserva una única fuente de configuración. Nunca añadas automáticamente el resultado sin inspeccionar el diff, especialmente después de una actualización.

En Windows y Linux pueden variar finales de línea o ejecutabilidad. Define la política también en .gitattributes y evita que un hook compense una configuración ambigua de Git. Prueba el repositorio en los sistemas realmente soportados.

Con estos controles, el equipo recibe resultados coherentes antes de enviar cambios y conserva una verificación independiente en CI.