Scripts de Composer, @import y hook commit-msg con core.hooksPath desde conventions:install
Hola, bienvenido. En esta lección vamos a terminar el comando conventions:install del paquete de convenciones. Vamos a añadirle 4 piezas, cada una en su clase: los scripts del contrato en composer.json, las líneas del .gitignore, la línea del import en CLAUDE.md y un hook de git que rechaza los mensajes de commit que no siguen Conventional Commits. Con ellas, un repo del equipo queda configurado con un solo comando.
En la lección anterior el comando aprendió a copiar las plantillas de PHPStan, Rector y Pint, el archivo de los arch tests y las rules. Las otras 4 piezas todavía dependen de que alguien se acuerde de ellas en cada repo. La regla de los commits es el caso más claro: la skill conventional-commits propone un buen mensaje cuando alguien la usa, pero nada impide hacer un commit con otro, como 2 commits de Aitor que llevan comillas invertidas en el mensaje.
Las 4 piezas siguen la forma que dejamos en la lección anterior: InstallCommand solo orquesta, y cada tarea es una clase pequeña en src/Console/Install/ que crea lo que falta, no sobrescribe lo que ya existe y dice qué ha hecho.
Los scripts del contrato en composer.json
La primera pieza añade los scripts del contrato a composer.json. Vamos a escribirla en conventions/src/Console/Install/ContractScripts.php:
Para cada script del contrato hay 3 casos, como con las plantillas. Si falta, lo añade. Si está y es igual, no hace nada. Y si está y es distinto, no lo toca y avisa, porque puede ser una decisión de ese repo que alguien tiene que mirar.
La excepción es test. El esqueleto de Laravel trae su propio script test, que ejecuta php artisan test sin cobertura, así que en un proyecto recién creado composer qa pasaría sin generar coverage.xml. Si test es exactamente el del esqueleto, el comando lo sustituye por el del contrato. Si es cualquier otro, lo respeta.
Hay 2 detalles en cómo trata el JSON. El primero es que json_decode se llama sin true, así que PHP devuelve objetos y no arrays asociativos. Con arrays, un objeto vacío {} de composer.json volvería al archivo como [], que para Composer no es lo mismo. El segundo es que json_encode escribe el archivo con su propio formato. En un composer.json recién creado, eso cambia 2 trozos que la clase no toca: keywords, que el esqueleto tiene en una línea, pasa a ocupar 4, y el bloque repositories se despliega en varias líneas. Composer lo lee igual, pero lo verás en el diff la primera vez. Por eso el comando solo escribe el archivo cuando añade o sustituye algún script.
- 02Las líneas del .gitignore
- 03La línea del import en CLAUDE.md
- 04El hook commit-msg
- 05core.hooksPath, la carpeta de hooks del repo
- 06El comando completo y la 1.3.0
- 07conventions:install en helpdesk
- 08El paso 3 del README