Este artículo es una adaptación al español del excelente artículo creado por JAMES SANSBURY de Lullabot en https://www.lullabot.com/articles/drupal-8-composer-best-practices
Esta traducción puede no ser literal pero contiene la esencia de lo expresado en el artículo original, se traduce con el objetivo de hacerlo disponible a quienes no dominan el idioma inglés.
----------
En Drupal 7 estábamos acostumbrados a instalar Drupal, sus módulos y sus temas simplemente descargando directamente desde el sitio Web o usando Drush, una tarea muy sencilla para quien ya venía acostumbrado a la tarea, sin embargo en otras trincheras de PHP la historia se estaba contando de otra forma y eso lo sabía el equipo de desarrollo del núcleo de Drupal 8, así que en todo ese proceso de arquitectura de la nueva versión se incluyó el gestor de paquetes por excelencia de PHP para hacernos la vida mas fácil, si, estoy hablando de Composer.
Estoy seguro que para muchos que andaban en la isla de Drupal 7, Composer sonaba a algo extraño al cual no valía la pena voltear a mirar ya que Drupal podía seguir instalándose de la manera tradicional, sin embargo, al momento de instalar un módulo contribuido es donde realmente se valora a Composer, algunos módulos tienen una gran cantidad de dependencias con librerías y módulos que si se instalan manualmente le pueden poner al borde de la locura.
La gestión de dependencias es complicada y no será fácil. Como Ryan Szrama dice:
"Si no está usando un gestor de dependencias [como composer], entonces usted es el gestor de dependencias y es poco confiable"
¿Por qué Composer?
- A menudo los módulos o temas tienen dependencias de librerías que son de terceros, es decir que no encuentras en Drupal.org y que se resuelven usando composer, si lo haces manualmente puede resultar muy tedioso.
- Composer le ayuda a validar si un paquete o módulo que está intentando instalar es compatible con su versión de PHP, no tendrá que intentar activarlo con Drupal para darse cuenta.
- También le ayuda a detectar conflictos entre paquetes y módulos
- Si llega a actualizar un paquete o incluso su versión de PHP, no tendrá que volver a hacer todo el proceso manual, composer lo hará para usted.
- drush dl será removido pronto en favor de composer, más info https://github.com/drush-ops/drush/pull/2654
Por donde empezar
La documentación disponible en drupal.org es bastante útil para empezar, si aún no tiene instalado Composer en su entorno, hágalo y recuerde asegurarse de estar usando la misma versión de PHP en todos sus entornos, desarrollo, pruebas y producción, así asegurará que todo lo validado por composer funcione también en sus otros entornos, usando herramientas como Vagrant o Docker podrá hacerlo fácilmente.
Vamos a instalar el núcleo de Drupal usando una plantilla que existe en Composer y que tiene por nombre drupal-project, esta plantilla se encargará de hacer la descarga del núcleo en nuestro directorio junto a un modelo de estructura recomendada para aprovechar al máximo docker.
Ejecute lo siguiente:
$ composer create-project drupal-composer/drupal-project:8.x-dev example --stability dev --no-interaction
El anterior comando copiará a drupal-project dentro de la carpeta example, descargará el núcleo de Drupal y algunos paquetes útiles, una vez termine es una buena práctica que ingrese a la carpeta e inicialice un repositorio en git, el proyecto ya contiene un archivo .gitignore con todos los directorios que no deberían ir en git y así hacer liviano el repositorio.
Fíjese en el archivo composer.json que está en el directorio, ese archivo funciona como una receta que indica todos paquetes necesarios para su instalación de Drupal así como dependencias entre otras cosas.
Cómo descargar módulos y temas.
Si le es familiar Drush, entonces no tendrá problemas con composer, básicamente solo es acostumbrarse a una nueva sintaxis que resulta muy similar, drupal-project ya include el repositorio de drupal para instalar paquetes, si por alguna razón, está usando composer bajo un modelo ajustado a sus necesidades, puede que requiera instalar el repositorio manualmente.
La siguiente es la sintaxis para instalar un paquete, ya sea un módulo o un tema
composer require [vendor]/[packagename]
Básicamente se trata de reemplazar lo que está entre [...], en donde el vendor es Drupal y el packagename es el nombre de máquina del módulo o tema, ejemplo:
composer require drupal/devel
¿Fácil verdad?, el lugar donde de instala el paquete descargado depende de donde se haya definido en el archivo composer.json, en el caso de drupal-project está definido en web/modules/contrib, a estos se les llaman installer-paths.
Dependencias para desarrollo
Suele suceder que algunos módulos solo los necesitamos para nuestro entorno de desarrollo y no para producción, un buen ejemplo es el módulo devel, para esos casos Composer nos permite especificarlo con la bandera --dev agregándolo al comando composer require.
$ composer require --dev drupal/devel
Esto asegurará que el módulo estará disponible para los desarrolladores cuando ejecuten
$ composer install
pero no se descargará si se ejecuta
$ composer install --no-dev
Hacer uso de la bandera --dev es una practica recomendada a la hora de usar composer
Como nota aparte, pero muy importante, si está haciendo commit de su configuración exportada de Drupal 8 es recomendable que haga uso del módulo Configuration Splint para asegurarse que el estado activado del módulo devel no se pase a producción y termine generando un error en el despliegue.
Dependencias anidadas
Los módulos o temas pueden tener su propio archivo composer.json que especifica unas dependencias adicionales, este archivo también es leído por composer al descargar el módulo y por lo tanto también resuelve esas dependencias, un ejemplo claro de eso es el módulo Address que tiene un archivo composer con distintas dependencias.
Descarga de versiones específicas
Puede suceder que en su instalación se requiera una versión específica de un módulo por compatibilidad, Composer es muy flexible en ese escanario y por eso tiene un extenso capítulo al respecto que es recomendable leer, a continuación las más usadas.
- Se usan los dos puntos después del vendor/package para especificar una versión o limitación.
composer require drupal/foo:1.2.3
En el ejemplo anterior puede notar que en el número de la versión no se ha incluido la versión de Drupal, es decir, de la forma 8.x-1.2.3, esto es porque en Composer esto no se requiere.
- La limitación usando el signo de intercalación (^) permite que sea instalada cualquier nueva versión excepto aquella que puede ser de un cambio de versión mayor y que puede dañar la estabilidad, en otras palabras, el primer número de la versión no puede cambiar pero los otros si.
composer require drupal/foo:^1.0
El anterior comando permitirá instalar un versión del módulo que sea mayor o igual a 1.0 pero inferior a 2.0, si necesita especificar una versión, este es el método recomendado.
- La limitación usando la virgulilla (~) es un poco mas restrictiva que el caso del signo de intercalación, esto significa que composer puede descargar una versión superior del último dígito especificado, por ejemplo
composer requirer drupal/foo:~1.2
Permitirá cualquier valor mas grande o igual a 1.2 como 1.2.0, 1.2.1, 1.9 etc... pero no permitirá incrementar a la versión 2.x
- Se pueden establecer otras limitaciones que se explican mejor por si solas como las banderas -dev -stable o incluso especificar comodines con *.
A qué hacerle commit
Usando como referencia la estructura de directorio definida por drupal.project, explore el archivo .gitignore, seguramente lo mencionado a continuación ya está siendo tenido en cuenta.
No haga commit a
- ./vendor
- ./web/core
- ./web/modules/contrib
- ./web/themes/contrib
Haga commit a
- composer.json y composer.lock (Este guardará las versiones exactas que fueron descargadas y así se asegurará que no se instalarán versiones distintas en otros ambientes)
- Cualquier otro código como es normal.
Cómo realizar actualizaciones
Actualizar un módulo, tema o librería es muy similar a como se hace la instalación, solo tenga en cuenta hacerlo en un momento no crítico del proyecto para poder evaluar las posibles incompatibilidades de la nueva versión.
Este es un ejemplo de actualización el módulo devel, note que se hace uso de la bandera --with-dependencies para que también se actualicen las dependencias requeridas por el módulo, de lo contrario solo se actualiza el módulo.
$ composer update --with-dependencies drupal/devel
Si quisiera actualizar todos los paquetes de Drupal, haga uso de del comodín *, ejemplo:
composer update --with-dependencies drupal/*
Sobre los parches
Es inevitable que un módulo, tema o librería pueda requerir un parche en el proceso de desarrollo y ya que no estamos cargando el código parchado en el repositorio, se necesita un método para que el flujo entre ambientes tenga en cuenta los parches que se están aplicando. Para ese caso recomendamos el uso de composer-patches si está trabajando con la plantilla drupal-project este ya se encuentra instalado, sino, instalarlo es mu y fácil con el siguiente comando.
$ composer require cweagans/composer-patches
Para parchar un paquete se edita el archivo composer.json para agregar la definición en la sección extra, veamos un ejemplo con un parche para el módulo devel.
"extra": {
"patches": {
"drupal/devel": {
"2860796: Create a branch of devel compatible with Media in core": "https://www.drupal.org/files/issues/2860796-2.patch"
}
}
}
Puede notar que la sintaxis define un comentario a la izquierda y la URL del parche a la derecha, si lo prefiere también puede apuntar a un directorio en el repositorio donde esté almacenando el parche.
Una vez haya hecho la modificación ejecute composer update drupal/devel para que quede aplicado el parche
$ composer update drupal/devel
Gathering patches for root package.
Removing package drupal/devel so that it can be re-installed and re-patched.
Deleting web/modules/contrib/devel - deleted
> DrupalProject\composer\ScriptHandler::checkComposerVersion
Loading composer repositories with package information
Updating dependencies (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
Gathering patches for root package.
Gathering patches for dependencies. This might take a minute.
- Installing drupal/devel (1.2.0): Loading from cache
- Applying patches for drupal/devel
https://www.drupal.org/files/issues/2860796-2.patch (2860796: Create a branch of devel compatible with Media in core)
Si se presenta el caso que alguna dependecia requiere un parche, por ejemplo un módulo conribuido requiere un parche en el core, tendrá que definirlo explícitamente en su archivo composer.json o ejecutar lo siguiente
$ composer config extra.enable-patching true
Cómo eliminar un módulo o tema
Tal como agregar o actualizar, eliminar es muy similar y fácil de hacer
$ composer remove drupal/devel
Si agregó el módulo con la bandera --dev, puede que le pregunte si quiere borrarlo de require-dev, si ese es el caso, podría usar el comando de la siguiente forma para que no le haga la pregunta
$ composer remove --dev drupal/devel
Eliminar parches
Si desea eliminar un módulo o tema que tenga parches, debe tener en cuenta que los parches no se eliminan por si solos sino que tendrá que hacerlo manualmente desde el archivo composer.json o con un comando como el siguiente que es para el módulo devel.
$ composer config --unset extra.patches.drupal/devel
Alertas por cambio en el hash del lock
Cuando se hacen cambios al composer.json el hash del archivo .lock deja de coincidir por o tanto arroja una alerta, para volver a ajustar el hash sin necesidad de ejecutar un composer update completo, puede ejecutarlo de la siguiente manera para que solo recree el hash
composer update --lock
Acelerar la cosas
Algunas operaciones de composer pueden resultar muy lentas, para mejorar la velocidad puede instalar prestissimo que le permite a composer ejecutar operaciones en paralelo, lo mejor es instalarlo de forma global
$ composer global require hirak/prestissimo
No es perfecto
Es inevitable que en ocasiones algo no salga bien y se generen momentos de frustración en donde quieras mandar a Composer a la mismísima $%&/()=)(/&, en esos casos le invitamos a recordar que no está solo y que lo mas seguro es que se pueda resolver, solo en muy pocos casos puede resultar mas fácil que usted tenga que gestionar las dependencias manualmente, piense que es una inversión a largo plazo para la estabilidad de su proyecto.